blob: 0fdb4e17219e26e15cc4d76322bedbcc24e1791d [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;
Joe Onorato8595a3d2010-11-19 18:12:07 -080051import android.view.View;
Jeff Sharkey1c400132011-08-05 14:50:13 -070052import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.widget.RemoteViews;
54
Griff Hazen959591e2014-05-15 22:26:18 -070055import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070056import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070057
Tor Norbyed9273d62013-05-30 15:59:53 -070058import java.lang.annotation.Retention;
59import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020060import java.lang.reflect.Constructor;
Andy Stadler110988c2010-12-03 14:29:16 -080061import java.text.NumberFormat;
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);
2972 contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
2973 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2974
2975 if (profileBadge != null) {
2976 contentView.setImageViewBitmap(resId, profileBadge);
2977 contentView.setViewVisibility(resId, View.VISIBLE);
2978
2979 // Make sure Line 3 is visible. As badge will be here if there
2980 // is no text to display.
2981 if (resId == R.id.profile_badge_line3) {
2982 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2983 }
2984 return true;
2985 }
2986 return false;
2987 }
2988
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002989 private void shrinkLine3Text(RemoteViews contentView) {
2990 float subTextSize = mContext.getResources().getDimensionPixelSize(
2991 R.dimen.notification_subtext_size);
2992 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
2993 }
2994
Christoph Studerfe718432014-09-01 18:21:18 +02002995 private void unshrinkLine3Text(RemoteViews contentView) {
2996 float regularTextSize = mContext.getResources().getDimensionPixelSize(
2997 com.android.internal.R.dimen.notification_text_size);
2998 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
2999 }
3000
3001 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003002 resetHeader(contentView);
3003 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003004 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003005 contentView.setTextViewText(R.id.title, null);
3006 contentView.setTextViewText(R.id.text, null);
3007 unshrinkLine3Text(contentView);
3008 contentView.setTextViewText(R.id.text2, null);
3009 contentView.setViewVisibility(R.id.text2, View.GONE);
3010 contentView.setViewVisibility(R.id.info, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003011 contentView.setViewVisibility(R.id.line3, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003012 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003013 }
3014
3015 private void resetHeader(RemoteViews contentView) {
3016 contentView.setImageViewResource(R.id.icon, 0);
3017 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003018 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003019 contentView.setViewVisibility(R.id.expand_button, View.GONE);
3020 contentView.setViewVisibility(R.id.sub_text_time_divider, View.GONE);
3021 contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
3022 contentView.setViewVisibility(R.id.app_title_sub_text_divider, View.GONE);
3023 contentView.setViewVisibility(R.id.number_of_children, View.GONE);
3024 }
3025
3026 private void resetContentMargins(RemoteViews contentView) {
3027 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
3028 contentView.setViewLayoutMarginEnd(R.id.line2, 0);
3029 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003030 }
3031
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003032 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003033 return applyStandardTemplate(resId, true /* hasProgress */);
3034 }
3035
3036 /**
3037 * @param hasProgress whether the progress bar should be shown and set
3038 */
3039 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003040 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003041
Christoph Studerfe718432014-09-01 18:21:18 +02003042 resetStandardTemplate(contentView);
3043
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003044 boolean showLine3 = false;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003045 // TODO: look into line3 shrinking
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003046 boolean showLine2 = false;
Kenny Guy98193ea2014-07-24 19:54:37 +01003047 boolean contentTextInLine2 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003048 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003049
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003050 bindNotificationHeader(contentView);
3051 bindLargeIcon(contentView);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003052 if (ex.getCharSequence(EXTRA_TITLE) != null) {
3053 contentView.setTextViewText(R.id.title,
3054 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003055 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003056 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3057 contentView.setTextViewText(R.id.text,
3058 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003059 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003060 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003061 if (ex.getCharSequence(EXTRA_INFO_TEXT) != null) {
3062 contentView.setTextViewText(R.id.info,
3063 processLegacyText(ex.getCharSequence(EXTRA_INFO_TEXT)));
Jeff Sharkey1c400132011-08-05 14:50:13 -07003064 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003065 showLine3 = true;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003066 } else if (mN.number > 0) {
Daniel Sandlerebce0112011-06-16 16:44:51 -04003067 final int tooBig = mContext.getResources().getInteger(
3068 R.integer.status_bar_notification_info_maxnum);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003069 if (mN.number > tooBig) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003070 contentView.setTextViewText(R.id.info, processLegacyText(
3071 mContext.getResources().getString(
3072 R.string.status_bar_notification_info_overflow)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003073 } else {
3074 NumberFormat f = NumberFormat.getIntegerInstance();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003075 contentView.setTextViewText(R.id.info, processLegacyText(f.format(mN.number)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003076 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003077 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003078 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003079 } else {
3080 contentView.setViewVisibility(R.id.info, View.GONE);
3081 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003082
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003083 contentView.setViewVisibility(R.id.text2, View.GONE);
3084 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3085 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3086 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3087 if (hasProgress && (max != 0 || ind)) {
3088 contentView.setViewVisibility(R.id.progress, View.VISIBLE);
3089 contentView.setProgressBar(
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003090 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003091 contentView.setProgressBackgroundTintList(
3092 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3093 R.color.notification_progress_background_color)));
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003094 if (mN.color != COLOR_DEFAULT) {
3095 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003096 contentView.setProgressTintList(R.id.progress, colorStateList);
3097 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003098 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003099 showLine2 = true;
3100 } else {
3101 contentView.setViewVisibility(R.id.progress, View.GONE);
Jeff Sharkey1c400132011-08-05 14:50:13 -07003102 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003103 if (showLine2) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003104
3105 // need to shrink all the type to make sure everything fits
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003106 shrinkLine3Text(contentView);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003107 }
3108
Kenny Guy98193ea2014-07-24 19:54:37 +01003109 // We want to add badge to first line of text.
3110 boolean addedBadge = addProfileBadge(contentView,
3111 contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
3112 // If we added the badge to line 3 then we should show line 3.
3113 if (addedBadge && !contentTextInLine2) {
3114 showLine3 = true;
3115 }
3116
3117 // Note getStandardView may hide line 3 again.
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003118 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Joe Onorato561d3852010-11-20 18:09:34 -08003119 return contentView;
3120 }
3121
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003122 private void bindLargeIcon(RemoteViews contentView) {
3123 if (mN.mLargeIcon != null) {
3124 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3125 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3126 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3127 int endMargin = mContext.getResources().getDimensionPixelSize(
3128 R.dimen.notification_content_picture_margin);
3129 contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
3130 contentView.setViewLayoutMarginEnd(R.id.line2, endMargin);
3131 contentView.setViewLayoutMarginEnd(R.id.line3, endMargin);
3132 }
3133 }
3134
3135 private void bindNotificationHeader(RemoteViews contentView) {
3136 bindSmallIcon(contentView);
3137 bindHeaderAppName(contentView);
Selim Cinek03d0d652015-11-13 13:18:09 -05003138 bindHeaderSubText(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003139 bindHeaderChronometerAndTime(contentView);
3140 bindExpandButton(contentView);
3141 }
3142
3143 private void bindExpandButton(RemoteViews contentView) {
3144 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
3145 PorterDuff.Mode.SRC_ATOP, -1);
3146 }
3147
3148 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3149 if (showsTimeOrChronometer()) {
3150 contentView.setViewVisibility(R.id.sub_text_time_divider, View.VISIBLE);
3151 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3152 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3153 contentView.setLong(R.id.chronometer, "setBase",
3154 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3155 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3156 } else {
3157 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3158 contentView.setLong(R.id.time, "setTime", mN.when);
3159 }
3160 }
3161 }
3162
Selim Cinek03d0d652015-11-13 13:18:09 -05003163 private void bindHeaderSubText(RemoteViews contentView) {
3164 CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3165 if (subText == null && mStyle != null && mStyle.mSummaryTextSet
3166 && mStyle.hasSummaryInHeader()) {
3167 subText = mStyle.mSummaryText;
3168 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003169 if (subText != null) {
3170 // TODO: Remove the span entirely to only have the string with propper formating.
3171 contentView.setTextViewText(R.id.header_sub_text, processLegacyText(subText));
3172 contentView.setViewVisibility(R.id.header_sub_text, View.VISIBLE);
3173 contentView.setViewVisibility(R.id.app_title_sub_text_divider, View.VISIBLE);
3174 }
3175 }
3176
3177 private void bindHeaderAppName(RemoteViews contentView) {
3178 PackageManager packageManager = mContext.getPackageManager();
3179 ApplicationInfo info = null;
3180 try {
3181 info = packageManager.getApplicationInfo(mContext.getApplicationInfo().packageName,
3182 0);
3183 } catch (final NameNotFoundException e) {
3184 return;
3185 }
3186 CharSequence appName = info != null ? packageManager.getApplicationLabel(info)
3187 : null;
3188 if (TextUtils.isEmpty(appName)) {
3189 return;
3190 }
3191 contentView.setTextViewText(R.id.app_name_text, appName);
3192 }
3193
3194 private void bindSmallIcon(RemoteViews contentView) {
3195 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3196 processSmallIconColor(mN.mSmallIcon, contentView);
3197 }
3198
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003199 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003200 * @return true if the built notification will show the time or the chronometer; false
3201 * otherwise
3202 */
3203 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003204 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003205 }
3206
3207 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003208 * Logic to find out whether the notification is going to have three lines in the contracted
3209 * layout. This is used to adjust the top padding.
3210 *
3211 * @return true if the notification is going to have three lines; false if the notification
3212 * is going to have one or two lines
3213 */
3214 private boolean hasThreeLines() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003215 final CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3216 final CharSequence text = mN.extras.getCharSequence(EXTRA_TEXT);
3217 boolean contentTextInLine2 = subText != null && text != null;
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003218 boolean hasProgress = mStyle == null || mStyle.hasProgress();
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003219 // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
3220 boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003221 boolean hasLine3 = text != null || mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null
3222 || mN.number > 0 || badgeInLine3;
3223 final Bundle ex = mN.extras;
3224 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3225 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3226 boolean hasLine2 = (subText != null && text != null) ||
3227 (hasProgress && subText == null && (max != 0 || ind));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003228 return hasLine2 && hasLine3;
3229 }
3230
Christoph Studerfe718432014-09-01 18:21:18 +02003231 private void resetStandardTemplateWithActions(RemoteViews big) {
3232 big.setViewVisibility(R.id.actions, View.GONE);
3233 big.setViewVisibility(R.id.action_divider, View.GONE);
3234 big.removeAllViews(R.id.actions);
3235 }
3236
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003237 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003238 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003239
Christoph Studerfe718432014-09-01 18:21:18 +02003240 resetStandardTemplateWithActions(big);
3241
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003242 int N = mActions.size();
3243 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003244 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003245 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003246 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003247 for (int i=0; i<N; i++) {
3248 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003249 big.addView(R.id.actions, button);
3250 }
3251 }
3252 return big;
3253 }
3254
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003255 /**
3256 * Construct a RemoteViews for the final 1U notification layout. In order:
3257 * 1. Custom contentView from the caller
3258 * 2. Style's proposed content view
3259 * 3. Standard template view
3260 */
3261 public RemoteViews makeContentView() {
3262 if (mN.contentView != null) {
3263 return mN.contentView;
3264 } else if (mStyle != null) {
3265 final RemoteViews styleView = mStyle.makeContentView();
3266 if (styleView != null) {
3267 return styleView;
3268 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003269 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003270 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003271 }
3272
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003273 /**
3274 * Construct a RemoteViews for the final big notification layout.
3275 */
3276 public RemoteViews makeBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003277 RemoteViews result = null;
Julia Reynolds089e3e42015-11-18 09:59:57 -05003278 if (mN.bigContentView != null) {
3279 return mN.bigContentView;
3280 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003281 result = mStyle.makeBigContentView();
Julia Reynolds089e3e42015-11-18 09:59:57 -05003282 } else if (mActions.size() == 0) {
3283 return null;
3284 }
Selim Cinek850a8542015-11-11 11:48:36 -05003285 if (result == null) {
3286 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
3287 }
3288 adaptNotificationHeaderForBigContentView(result);
3289 return result;
3290 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003291
Selim Cinek850a8542015-11-11 11:48:36 -05003292 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
3293 // We have to set the collapse button instead
3294 result.setImageViewResource(R.id.expand_button, R.drawable.ic_arrow_up_14dp);
3295 // Apply the color again
3296 result.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
3297 PorterDuff.Mode.SRC_ATOP, -1);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003298 }
3299
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003300 /**
3301 * Construct a RemoteViews for the final heads-up notification layout.
3302 */
3303 public RemoteViews makeHeadsUpContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003304 if (mN.headsUpContentView != null) {
3305 return mN.headsUpContentView;
3306 } else if (mStyle != null) {
3307 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3308 if (styleView != null) {
3309 return styleView;
3310 }
3311 } else if (mActions.size() == 0) {
3312 return null;
3313 }
3314
Chris Wren8fd39ec2014-02-27 17:43:26 -05003315
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003316 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003317 }
3318
3319
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003320 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003321 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003322 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003323 tombstone ? getActionTombstoneLayoutResource()
3324 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003325 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003326 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003327 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003328 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003329 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003330 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003331 if (action.mRemoteInputs != null) {
3332 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3333 }
3334 if (mN.color != COLOR_DEFAULT) {
3335 button.setTextColor(R.id.action0, mN.color);
3336 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003337 processLegacyAction(action, button);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003338 return button;
3339 }
3340
Joe Onoratocb109a02011-01-18 17:57:41 -08003341 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003342 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003343 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003344 */
3345 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003346 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003347 }
3348
3349 private void processLegacyAction(Action action, RemoteViews button) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003350 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
Christoph Studer239f8352014-08-25 15:13:18 +02003351 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
Alan Viverette4a357cd2015-03-18 18:37:18 -07003352 mContext.getColor(R.color.notification_action_color_filter),
Christoph Studer239f8352014-08-25 15:13:18 +02003353 PorterDuff.Mode.MULTIPLY);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003354 }
3355 }
3356
3357 private CharSequence processLegacyText(CharSequence charSequence) {
3358 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003359 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003360 } else {
3361 return charSequence;
3362 }
3363 }
3364
Dan Sandler26e81cf2014-05-06 10:01:27 -04003365 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003366 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003367 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003368 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
3369 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon)) {
3370 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003371 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003372 }
3373 }
3374
Dan Sandler26e81cf2014-05-06 10:01:27 -04003375 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003376 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003377 * if it's grayscale).
3378 */
3379 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003380 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3381 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003382 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003383 // resolve color will fall back to the default when legacy
3384 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003385 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003386 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003387 }
3388
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003389 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003390 if (mN.color != COLOR_DEFAULT) {
3391 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003392 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003393 }
3394
Selim Cinek5bf069a2015-11-10 19:14:27 -05003395 int resolveColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003396 if (mN.color == COLOR_DEFAULT) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003397 return mContext.getColor(R.color.notification_icon_default_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003398 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003399 return mN.color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003400 }
3401
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003402 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003403 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003404 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003405 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003406 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003407 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003408 mN.actions = new Action[mActions.size()];
3409 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003410 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003411 if (!mPersonList.isEmpty()) {
3412 mN.extras.putStringArray(EXTRA_PEOPLE,
3413 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003414 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003415 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003416 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003417
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003418 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003419 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003420 ApplicationInfo applicationInfo = n.extras.getParcelable(
3421 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003422 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003423 if (applicationInfo != null) {
3424 try {
3425 builderContext = context.createApplicationContext(applicationInfo,
3426 Context.CONTEXT_RESTRICTED);
3427 } catch (NameNotFoundException e) {
3428 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3429 builderContext = context; // try with our context
3430 }
3431 } else {
3432 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003433 }
3434
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003435 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003436 }
3437
3438 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3439 Class<? extends Style>[] classes = new Class[]{
3440 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3441 for (Class<? extends Style> innerClass : classes) {
3442 if (templateClass.equals(innerClass.getName())) {
3443 return innerClass;
3444 }
3445 }
3446 return null;
3447 }
3448
3449 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3450 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003451 }
3452
3453 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3454 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003455 }
3456
3457 private void setBuilderHeadsUpContentView(Notification n,
3458 RemoteViews headsUpContentView) {
3459 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003460 }
3461
3462 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003463 * @deprecated Use {@link #build()} instead.
3464 */
3465 @Deprecated
3466 public Notification getNotification() {
3467 return build();
3468 }
3469
3470 /**
3471 * Combine all of the options that have been set and return a new {@link Notification}
3472 * object.
3473 */
3474 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003475 // first, add any extras from the calling code
3476 if (mUserExtras != null) {
3477 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003478 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003479
3480 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003481 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003482
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003483 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003484
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003485 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003486 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003487 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003488
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003489 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003490 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003491
3492 /**
3493 * Apply this Builder to an existing {@link Notification} object.
3494 *
3495 * @hide
3496 */
3497 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003498 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003499 return n;
3500 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003501
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003502 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003503 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003504 }
3505
3506 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003507 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003508 }
3509
3510 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003511 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003512 }
3513
3514 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003515 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003516 }
3517
3518 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003519 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003520 }
3521
3522 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003523 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003524 }
3525
3526 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003527 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003528 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003529 }
3530
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003531 /**
3532 * An object that can apply a rich notification style to a {@link Notification.Builder}
3533 * object.
3534 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003535 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003536 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003537
3538 /**
3539 * @hide
3540 */
3541 protected CharSequence mSummaryText = null;
3542
3543 /**
3544 * @hide
3545 */
3546 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003547
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003548 protected Builder mBuilder;
3549
Chris Wrend6297db2012-05-03 16:20:13 -04003550 /**
3551 * Overrides ContentTitle in the big form of the template.
3552 * This defaults to the value passed to setContentTitle().
3553 */
3554 protected void internalSetBigContentTitle(CharSequence title) {
3555 mBigContentTitle = title;
3556 }
3557
3558 /**
3559 * Set the first line of text after the detail section in the big form of the template.
3560 */
3561 protected void internalSetSummaryText(CharSequence cs) {
3562 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003563 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003564 }
3565
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003566 public void setBuilder(Builder builder) {
3567 if (mBuilder != builder) {
3568 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003569 if (mBuilder != null) {
3570 mBuilder.setStyle(this);
3571 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003572 }
3573 }
3574
Chris Wrend6297db2012-05-03 16:20:13 -04003575 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003576 if (mBuilder == null) {
3577 throw new IllegalArgumentException("Style requires a valid Builder object");
3578 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003579 }
Chris Wrend6297db2012-05-03 16:20:13 -04003580
3581 protected RemoteViews getStandardView(int layoutId) {
3582 checkBuilder();
3583
Christoph Studer4600f9b2014-07-22 22:44:43 +02003584 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003585 CharSequence oldBuilderContentTitle =
3586 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003587 if (mBigContentTitle != null) {
3588 mBuilder.setContentTitle(mBigContentTitle);
3589 }
3590
Chris Wrend6297db2012-05-03 16:20:13 -04003591 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3592
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003593 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003594
Chris Wrend6297db2012-05-03 16:20:13 -04003595 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3596 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003597 } else {
3598 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003599 }
3600
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05003601 // Clear text in case we use the line to show the profile badge.
3602 contentView.setTextViewText(com.android.internal.R.id.text, "");
3603 contentView.setViewVisibility(com.android.internal.R.id.line3, View.GONE);
3604
Chris Wrend6297db2012-05-03 16:20:13 -04003605 return contentView;
3606 }
3607
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003608 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003609 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3610 * The default implementation has nothing additional to add.
3611 * @hide
3612 */
3613 public RemoteViews makeContentView() {
3614 return null;
3615 }
3616
3617 /**
3618 * Construct a Style-specific RemoteViews for the final big notification layout.
3619 * @hide
3620 */
3621 public RemoteViews makeBigContentView() {
3622 return null;
3623 }
3624
3625 /**
3626 * Construct a Style-specific RemoteViews for the final HUN layout.
3627 * @hide
3628 */
3629 public RemoteViews makeHeadsUpContentView() {
3630 return null;
3631 }
3632
3633 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003634 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003635 * @hide
3636 */
3637 public void addExtras(Bundle extras) {
3638 if (mSummaryTextSet) {
3639 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3640 }
3641 if (mBigContentTitle != null) {
3642 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3643 }
Chris Wren91ad5632013-06-05 15:05:57 -04003644 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003645 }
3646
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003647 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003648 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003649 * @hide
3650 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003651 protected void restoreFromExtras(Bundle extras) {
3652 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3653 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3654 mSummaryTextSet = true;
3655 }
3656 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3657 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3658 }
3659 }
3660
3661
3662 /**
3663 * @hide
3664 */
3665 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003666 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003667 return wip;
3668 }
3669
Daniel Sandler0ec46202015-06-24 01:27:05 -04003670 /**
3671 * @hide
3672 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003673 public void purgeResources() {}
3674
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003675 /**
3676 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3677 * attached to.
3678 *
3679 * @return the fully constructed Notification.
3680 */
3681 public Notification build() {
3682 checkBuilder();
3683 return mBuilder.build();
3684 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003685
3686 /**
3687 * @hide
3688 * @return true if the style positions the progress bar on the second line; false if the
3689 * style hides the progress bar
3690 */
3691 protected boolean hasProgress() {
3692 return true;
3693 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003694
3695 /**
3696 * @hide
3697 * @return Whether we should put the summary be put into the notification header
3698 */
3699 public boolean hasSummaryInHeader() {
3700 return true;
3701 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003702 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003703
3704 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003705 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003706 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003707 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003708 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003709 * Notification notif = new Notification.Builder(mContext)
3710 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3711 * .setContentText(subject)
3712 * .setSmallIcon(R.drawable.new_post)
3713 * .setLargeIcon(aBitmap)
3714 * .setStyle(new Notification.BigPictureStyle()
3715 * .bigPicture(aBigBitmap))
3716 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003717 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003718 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003719 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003720 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003721 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003722 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003723 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003724 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003725
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003726 public BigPictureStyle() {
3727 }
3728
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003729 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003730 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003731 }
3732
Chris Wrend6297db2012-05-03 16:20:13 -04003733 /**
3734 * Overrides ContentTitle in the big form of the template.
3735 * This defaults to the value passed to setContentTitle().
3736 */
3737 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003738 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003739 return this;
3740 }
3741
3742 /**
3743 * Set the first line of text after the detail section in the big form of the template.
3744 */
3745 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003746 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003747 return this;
3748 }
3749
Chris Wren0bd664d2012-08-01 13:56:56 -04003750 /**
3751 * Provide the bitmap to be used as the payload for the BigPicture notification.
3752 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003753 public BigPictureStyle bigPicture(Bitmap b) {
3754 mPicture = b;
3755 return this;
3756 }
3757
Chris Wren3745a3d2012-05-22 15:11:52 -04003758 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003759 * Override the large icon when the big notification is shown.
3760 */
3761 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003762 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3763 }
3764
3765 /**
3766 * Override the large icon when the big notification is shown.
3767 */
3768 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003769 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003770 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003771 return this;
3772 }
3773
Riley Andrews0394a0c2015-11-03 23:36:52 -08003774 /** @hide */
3775 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
3776
Daniel Sandler0ec46202015-06-24 01:27:05 -04003777 /**
3778 * @hide
3779 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003780 @Override
3781 public void purgeResources() {
3782 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08003783 if (mPicture != null &&
3784 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08003785 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003786 mPicture = mPicture.createAshmemBitmap();
3787 }
3788 if (mBigLargeIcon != null) {
3789 mBigLargeIcon.convertToAshmem();
3790 }
3791 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003792
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003793 /**
3794 * @hide
3795 */
3796 public RemoteViews makeBigContentView() {
3797 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003798 // This covers the following cases:
3799 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003800 // mN.mLargeIcon
3801 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003802 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003803 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003804 oldLargeIcon = mBuilder.mN.mLargeIcon;
3805 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003806 }
3807
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003808 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05003809 if (mSummaryTextSet) {
3810 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
3811 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3812 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003813 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003814 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003815 }
3816
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003817 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3818
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003819 boolean twoTextLines = mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT) != null
3820 && mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT) != null;
Kenny Guy98193ea2014-07-24 19:54:37 +01003821 mBuilder.addProfileBadge(contentView,
3822 twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003823 return contentView;
3824 }
3825
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003826 /**
3827 * @hide
3828 */
3829 public void addExtras(Bundle extras) {
3830 super.addExtras(extras);
3831
3832 if (mBigLargeIconSet) {
3833 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3834 }
3835 extras.putParcelable(EXTRA_PICTURE, mPicture);
3836 }
3837
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003838 /**
3839 * @hide
3840 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003841 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003842 protected void restoreFromExtras(Bundle extras) {
3843 super.restoreFromExtras(extras);
3844
3845 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003846 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003847 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003848 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003849 mPicture = extras.getParcelable(EXTRA_PICTURE);
3850 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003851
3852 /**
3853 * @hide
3854 */
3855 @Override
3856 public boolean hasSummaryInHeader() {
3857 return false;
3858 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003859 }
3860
3861 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003862 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003863 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003864 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003865 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003866 * Notification notif = new Notification.Builder(mContext)
3867 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3868 * .setContentText(subject)
3869 * .setSmallIcon(R.drawable.new_mail)
3870 * .setLargeIcon(aBitmap)
3871 * .setStyle(new Notification.BigTextStyle()
3872 * .bigText(aVeryLongString))
3873 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003874 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003875 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003876 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003877 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003878 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003879
3880 private static final int MAX_LINES = 13;
3881 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3882 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3883
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003884 private CharSequence mBigText;
3885
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003886 public BigTextStyle() {
3887 }
3888
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003889 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003890 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003891 }
3892
Chris Wrend6297db2012-05-03 16:20:13 -04003893 /**
3894 * Overrides ContentTitle in the big form of the template.
3895 * This defaults to the value passed to setContentTitle().
3896 */
3897 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003898 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003899 return this;
3900 }
3901
3902 /**
3903 * Set the first line of text after the detail section in the big form of the template.
3904 */
3905 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003906 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003907 return this;
3908 }
3909
Chris Wren0bd664d2012-08-01 13:56:56 -04003910 /**
3911 * Provide the longer text to be displayed in the big form of the
3912 * template in place of the content text.
3913 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003914 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003915 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003916 return this;
3917 }
3918
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003919 /**
3920 * @hide
3921 */
3922 public void addExtras(Bundle extras) {
3923 super.addExtras(extras);
3924
Christoph Studer4600f9b2014-07-22 22:44:43 +02003925 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3926 }
3927
3928 /**
3929 * @hide
3930 */
3931 @Override
3932 protected void restoreFromExtras(Bundle extras) {
3933 super.restoreFromExtras(extras);
3934
3935 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003936 }
3937
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003938 /**
3939 * @hide
3940 */
3941 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003942
3943 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003944 CharSequence oldBuilderContentText =
3945 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3946 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003947
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003948 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003949
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003950 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003951
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003952 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003953 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003954 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Chris Wren3c5f92432012-05-04 16:31:17 -04003955 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003956
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003957 mBuilder.shrinkLine3Text(contentView);
3958
Kenny Guy98193ea2014-07-24 19:54:37 +01003959 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3960
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003961 return contentView;
3962 }
3963
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003964 private int calculateMaxLines() {
3965 int lineCount = MAX_LINES;
3966 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003967 boolean hasSummary = (mSummaryTextSet ? mSummaryText
3968 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003969 if (hasActions) {
3970 lineCount -= LINES_CONSUMED_BY_ACTIONS;
3971 }
3972 if (hasSummary) {
3973 lineCount -= LINES_CONSUMED_BY_SUMMARY;
3974 }
3975
3976 // If we have less top padding at the top, we can fit less lines.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003977 if (!mBuilder.hasThreeLines()) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003978 lineCount--;
3979 }
3980 return lineCount;
3981 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003982 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04003983
3984 /**
3985 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08003986 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003987 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04003988 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003989 * Notification notif = new Notification.Builder(mContext)
3990 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
3991 * .setContentText(subject)
3992 * .setSmallIcon(R.drawable.new_mail)
3993 * .setLargeIcon(aBitmap)
3994 * .setStyle(new Notification.InboxStyle()
3995 * .addLine(str1)
3996 * .addLine(str2)
3997 * .setContentTitle(&quot;&quot;)
3998 * .setSummaryText(&quot;+3 more&quot;))
3999 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004000 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004001 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004002 * @see Notification#bigContentView
4003 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004004 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004005 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4006
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004007 public InboxStyle() {
4008 }
4009
Daniel Sandler879c5e02012-04-17 16:46:51 -04004010 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004011 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004012 }
4013
Chris Wrend6297db2012-05-03 16:20:13 -04004014 /**
4015 * Overrides ContentTitle in the big form of the template.
4016 * This defaults to the value passed to setContentTitle().
4017 */
4018 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004019 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004020 return this;
4021 }
4022
4023 /**
4024 * Set the first line of text after the detail section in the big form of the template.
4025 */
4026 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004027 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004028 return this;
4029 }
4030
Chris Wren0bd664d2012-08-01 13:56:56 -04004031 /**
4032 * Append a line to the digest section of the Inbox notification.
4033 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004034 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004035 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004036 return this;
4037 }
4038
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004039 /**
4040 * @hide
4041 */
4042 public void addExtras(Bundle extras) {
4043 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004044
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004045 CharSequence[] a = new CharSequence[mTexts.size()];
4046 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4047 }
4048
Christoph Studer4600f9b2014-07-22 22:44:43 +02004049 /**
4050 * @hide
4051 */
4052 @Override
4053 protected void restoreFromExtras(Bundle extras) {
4054 super.restoreFromExtras(extras);
4055
4056 mTexts.clear();
4057 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4058 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4059 }
4060 }
4061
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004062 /**
4063 * @hide
4064 */
4065 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004066 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004067 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004068 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4069 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004070
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004071 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004072
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004073 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004074
Chris Wrend6297db2012-05-03 16:20:13 -04004075 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004076
Chris Wrend6297db2012-05-03 16:20:13 -04004077 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 -04004078 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004079
Chris Wren4ed80d52012-05-17 09:30:03 -04004080 // Make sure all rows are gone in case we reuse a view.
4081 for (int rowId : rowIds) {
4082 contentView.setViewVisibility(rowId, View.GONE);
4083 }
4084
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004085 final boolean largeText =
4086 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4087 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4088 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004089 int i=0;
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004090 final float density = mBuilder.mContext.getResources().getDisplayMetrics().density;
4091 int topPadding = (int) (5 * density);
4092 int bottomPadding = (int) (13 * density);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004093 while (i < mTexts.size() && i < rowIds.length) {
4094 CharSequence str = mTexts.get(i);
4095 if (str != null && !str.equals("")) {
4096 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004097 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004098 if (largeText) {
4099 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4100 subTextSize);
4101 }
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004102 contentView.setViewPadding(rowIds[i], 0, topPadding, 0,
4103 i == rowIds.length - 1 || i == mTexts.size() - 1 ? bottomPadding : 0);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004104 }
4105 i++;
4106 }
4107
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004108 mBuilder.shrinkLine3Text(contentView);
4109
Kenny Guy98193ea2014-07-24 19:54:37 +01004110 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4111
Daniel Sandler879c5e02012-04-17 16:46:51 -04004112 return contentView;
4113 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004114 }
Dan Sandler842dd772014-05-15 09:36:47 -04004115
4116 /**
4117 * Notification style for media playback notifications.
4118 *
4119 * In the expanded form, {@link Notification#bigContentView}, up to 5
4120 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004121 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004122 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4123 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4124 * treated as album artwork.
4125 *
4126 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4127 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004128 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004129 * in the standard view alongside the usual content.
4130 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004131 * Notifications created with MediaStyle will have their category set to
4132 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4133 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4134 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004135 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4136 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004137 * the System UI can identify this as a notification representing an active media session
4138 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4139 *
4140 * To use this style with your Notification, feed it to
4141 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4142 * <pre class="prettyprint">
4143 * Notification noti = new Notification.Builder()
4144 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004145 * .setContentTitle(&quot;Track title&quot;)
4146 * .setContentText(&quot;Artist - Album&quot;)
4147 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004148 * .setStyle(<b>new Notification.MediaStyle()</b>
4149 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004150 * .build();
4151 * </pre>
4152 *
4153 * @see Notification#bigContentView
4154 */
4155 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004156 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004157 static final int MAX_MEDIA_BUTTONS = 5;
4158
4159 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004160 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004161
4162 public MediaStyle() {
4163 }
4164
4165 public MediaStyle(Builder builder) {
4166 setBuilder(builder);
4167 }
4168
4169 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004170 * 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 -04004171 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004172 *
4173 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004174 */
4175 public MediaStyle setShowActionsInCompactView(int...actions) {
4176 mActionsToShowInCompact = actions;
4177 return this;
4178 }
4179
4180 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004181 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4182 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004183 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004184 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004185 mToken = token;
4186 return this;
4187 }
4188
Christoph Studer4600f9b2014-07-22 22:44:43 +02004189 /**
4190 * @hide
4191 */
Dan Sandler842dd772014-05-15 09:36:47 -04004192 @Override
4193 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004194 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004195 if (wip.category == null) {
4196 wip.category = Notification.CATEGORY_TRANSPORT;
4197 }
Dan Sandler842dd772014-05-15 09:36:47 -04004198 return wip;
4199 }
4200
Christoph Studer4600f9b2014-07-22 22:44:43 +02004201 /**
4202 * @hide
4203 */
4204 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004205 public RemoteViews makeContentView() {
4206 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004207 }
4208
4209 /**
4210 * @hide
4211 */
4212 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004213 public RemoteViews makeBigContentView() {
4214 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004215 }
4216
Dan Sandler842dd772014-05-15 09:36:47 -04004217 /** @hide */
4218 @Override
4219 public void addExtras(Bundle extras) {
4220 super.addExtras(extras);
4221
4222 if (mToken != null) {
4223 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4224 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004225 if (mActionsToShowInCompact != null) {
4226 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4227 }
Dan Sandler842dd772014-05-15 09:36:47 -04004228 }
4229
Christoph Studer4600f9b2014-07-22 22:44:43 +02004230 /**
4231 * @hide
4232 */
4233 @Override
4234 protected void restoreFromExtras(Bundle extras) {
4235 super.restoreFromExtras(extras);
4236
4237 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4238 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4239 }
4240 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4241 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4242 }
4243 }
4244
Selim Cinek5bf069a2015-11-10 19:14:27 -05004245 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04004246 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004247 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004248 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004249 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05004250 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
4251 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004252 if (!tombstone) {
4253 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4254 }
4255 button.setContentDescription(R.id.action0, action.title);
4256 return button;
4257 }
4258
4259 private RemoteViews makeMediaContentView() {
4260 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004261 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004262
4263 final int numActions = mBuilder.mActions.size();
4264 final int N = mActionsToShowInCompact == null
4265 ? 0
4266 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4267 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004268 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004269 for (int i = 0; i < N; i++) {
4270 if (i >= numActions) {
4271 throw new IllegalArgumentException(String.format(
4272 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4273 i, numActions - 1));
4274 }
4275
4276 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004277 final RemoteViews button = generateMediaActionButton(action,
4278 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004279 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004280 }
4281 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004282 handleImage(view /* addPaddingToMainColumn */);
Dan Sandler842dd772014-05-15 09:36:47 -04004283 return view;
4284 }
4285
4286 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004287 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004288 RemoteViews big = mBuilder.applyStandardTemplate(
4289 R.layout.notification_template_material_big_media,
4290 false);
Dan Sandler842dd772014-05-15 09:36:47 -04004291
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004292 if (actionCount > 0) {
4293 big.removeAllViews(com.android.internal.R.id.media_actions);
4294 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05004295 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
4296 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004297 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004298 }
4299 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004300 handleImage(big);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004301 big.setViewVisibility(android.R.id.progress, View.GONE);
Dan Sandler842dd772014-05-15 09:36:47 -04004302 return big;
4303 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004304
Selim Cinek5bf069a2015-11-10 19:14:27 -05004305 private void handleImage(RemoteViews contentView) {
4306 if (mBuilder.mN.mLargeIcon != null) {
4307 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
4308 contentView.setViewLayoutMarginEnd(R.id.line2, 0);
4309 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004310 }
4311 }
4312
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004313 /**
4314 * @hide
4315 */
4316 @Override
4317 protected boolean hasProgress() {
4318 return false;
4319 }
Dan Sandler842dd772014-05-15 09:36:47 -04004320 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004321
Christoph Studer4600f9b2014-07-22 22:44:43 +02004322 // When adding a new Style subclass here, don't forget to update
4323 // Builder.getNotificationStyleClass.
4324
Griff Hazen61a9e862014-05-22 16:05:19 -07004325 /**
4326 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4327 * metadata or change options on a notification builder.
4328 */
4329 public interface Extender {
4330 /**
4331 * Apply this extender to a notification builder.
4332 * @param builder the builder to be modified.
4333 * @return the build object for chaining.
4334 */
4335 public Builder extend(Builder builder);
4336 }
4337
4338 /**
4339 * Helper class to add wearable extensions to notifications.
4340 * <p class="note"> See
4341 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4342 * for Android Wear</a> for more information on how to use this class.
4343 * <p>
4344 * To create a notification with wearable extensions:
4345 * <ol>
4346 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4347 * properties.
4348 * <li>Create a {@link android.app.Notification.WearableExtender}.
4349 * <li>Set wearable-specific properties using the
4350 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4351 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4352 * notification.
4353 * <li>Post the notification to the notification system with the
4354 * {@code NotificationManager.notify(...)} methods.
4355 * </ol>
4356 *
4357 * <pre class="prettyprint">
4358 * Notification notif = new Notification.Builder(mContext)
4359 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4360 * .setContentText(subject)
4361 * .setSmallIcon(R.drawable.new_mail)
4362 * .extend(new Notification.WearableExtender()
4363 * .setContentIcon(R.drawable.new_mail))
4364 * .build();
4365 * NotificationManager notificationManger =
4366 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4367 * notificationManger.notify(0, notif);</pre>
4368 *
4369 * <p>Wearable extensions can be accessed on an existing notification by using the
4370 * {@code WearableExtender(Notification)} constructor,
4371 * and then using the {@code get} methods to access values.
4372 *
4373 * <pre class="prettyprint">
4374 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4375 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004376 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004377 */
4378 public static final class WearableExtender implements Extender {
4379 /**
4380 * Sentinel value for an action index that is unset.
4381 */
4382 public static final int UNSET_ACTION_INDEX = -1;
4383
4384 /**
4385 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4386 * default sizing.
4387 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004388 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004389 * on their content.
4390 */
4391 public static final int SIZE_DEFAULT = 0;
4392
4393 /**
4394 * Size value for use with {@link #setCustomSizePreset} to show this notification
4395 * with an extra small size.
4396 * <p>This value is only applicable for custom display notifications created using
4397 * {@link #setDisplayIntent}.
4398 */
4399 public static final int SIZE_XSMALL = 1;
4400
4401 /**
4402 * Size value for use with {@link #setCustomSizePreset} to show this notification
4403 * with a small size.
4404 * <p>This value is only applicable for custom display notifications created using
4405 * {@link #setDisplayIntent}.
4406 */
4407 public static final int SIZE_SMALL = 2;
4408
4409 /**
4410 * Size value for use with {@link #setCustomSizePreset} to show this notification
4411 * with a medium size.
4412 * <p>This value is only applicable for custom display notifications created using
4413 * {@link #setDisplayIntent}.
4414 */
4415 public static final int SIZE_MEDIUM = 3;
4416
4417 /**
4418 * Size value for use with {@link #setCustomSizePreset} to show this notification
4419 * with a large size.
4420 * <p>This value is only applicable for custom display notifications created using
4421 * {@link #setDisplayIntent}.
4422 */
4423 public static final int SIZE_LARGE = 4;
4424
Griff Hazend5f11f92014-05-27 15:40:09 -07004425 /**
4426 * Size value for use with {@link #setCustomSizePreset} to show this notification
4427 * full screen.
4428 * <p>This value is only applicable for custom display notifications created using
4429 * {@link #setDisplayIntent}.
4430 */
4431 public static final int SIZE_FULL_SCREEN = 5;
4432
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004433 /**
4434 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4435 * short amount of time when this notification is displayed on the screen. This
4436 * is the default value.
4437 */
4438 public static final int SCREEN_TIMEOUT_SHORT = 0;
4439
4440 /**
4441 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4442 * for a longer amount of time when this notification is displayed on the screen.
4443 */
4444 public static final int SCREEN_TIMEOUT_LONG = -1;
4445
Griff Hazen61a9e862014-05-22 16:05:19 -07004446 /** Notification extra which contains wearable extensions */
4447 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4448
Pete Gastaf6781d2014-10-07 15:17:05 -04004449 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004450 private static final String KEY_ACTIONS = "actions";
4451 private static final String KEY_FLAGS = "flags";
4452 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4453 private static final String KEY_PAGES = "pages";
4454 private static final String KEY_BACKGROUND = "background";
4455 private static final String KEY_CONTENT_ICON = "contentIcon";
4456 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4457 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4458 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4459 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4460 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004461 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004462
4463 // Flags bitwise-ored to mFlags
4464 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4465 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4466 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4467 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004468 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004469
4470 // Default value for flags integer
4471 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4472
4473 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4474 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4475
4476 private ArrayList<Action> mActions = new ArrayList<Action>();
4477 private int mFlags = DEFAULT_FLAGS;
4478 private PendingIntent mDisplayIntent;
4479 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4480 private Bitmap mBackground;
4481 private int mContentIcon;
4482 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4483 private int mContentActionIndex = UNSET_ACTION_INDEX;
4484 private int mCustomSizePreset = SIZE_DEFAULT;
4485 private int mCustomContentHeight;
4486 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004487 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004488
4489 /**
4490 * Create a {@link android.app.Notification.WearableExtender} with default
4491 * options.
4492 */
4493 public WearableExtender() {
4494 }
4495
4496 public WearableExtender(Notification notif) {
4497 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4498 if (wearableBundle != null) {
4499 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4500 if (actions != null) {
4501 mActions.addAll(actions);
4502 }
4503
4504 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4505 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4506
4507 Notification[] pages = getNotificationArrayFromBundle(
4508 wearableBundle, KEY_PAGES);
4509 if (pages != null) {
4510 Collections.addAll(mPages, pages);
4511 }
4512
4513 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4514 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4515 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4516 DEFAULT_CONTENT_ICON_GRAVITY);
4517 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4518 UNSET_ACTION_INDEX);
4519 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4520 SIZE_DEFAULT);
4521 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4522 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004523 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004524 }
4525 }
4526
4527 /**
4528 * Apply wearable extensions to a notification that is being built. This is typically
4529 * called by the {@link android.app.Notification.Builder#extend} method of
4530 * {@link android.app.Notification.Builder}.
4531 */
4532 @Override
4533 public Notification.Builder extend(Notification.Builder builder) {
4534 Bundle wearableBundle = new Bundle();
4535
4536 if (!mActions.isEmpty()) {
4537 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4538 }
4539 if (mFlags != DEFAULT_FLAGS) {
4540 wearableBundle.putInt(KEY_FLAGS, mFlags);
4541 }
4542 if (mDisplayIntent != null) {
4543 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4544 }
4545 if (!mPages.isEmpty()) {
4546 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4547 new Notification[mPages.size()]));
4548 }
4549 if (mBackground != null) {
4550 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4551 }
4552 if (mContentIcon != 0) {
4553 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4554 }
4555 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4556 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4557 }
4558 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4559 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4560 mContentActionIndex);
4561 }
4562 if (mCustomSizePreset != SIZE_DEFAULT) {
4563 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4564 }
4565 if (mCustomContentHeight != 0) {
4566 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4567 }
4568 if (mGravity != DEFAULT_GRAVITY) {
4569 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4570 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004571 if (mHintScreenTimeout != 0) {
4572 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4573 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004574
4575 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4576 return builder;
4577 }
4578
4579 @Override
4580 public WearableExtender clone() {
4581 WearableExtender that = new WearableExtender();
4582 that.mActions = new ArrayList<Action>(this.mActions);
4583 that.mFlags = this.mFlags;
4584 that.mDisplayIntent = this.mDisplayIntent;
4585 that.mPages = new ArrayList<Notification>(this.mPages);
4586 that.mBackground = this.mBackground;
4587 that.mContentIcon = this.mContentIcon;
4588 that.mContentIconGravity = this.mContentIconGravity;
4589 that.mContentActionIndex = this.mContentActionIndex;
4590 that.mCustomSizePreset = this.mCustomSizePreset;
4591 that.mCustomContentHeight = this.mCustomContentHeight;
4592 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004593 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004594 return that;
4595 }
4596
4597 /**
4598 * Add a wearable action to this notification.
4599 *
4600 * <p>When wearable actions are added using this method, the set of actions that
4601 * show on a wearable device splits from devices that only show actions added
4602 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4603 * of which actions display on different devices.
4604 *
4605 * @param action the action to add to this notification
4606 * @return this object for method chaining
4607 * @see android.app.Notification.Action
4608 */
4609 public WearableExtender addAction(Action action) {
4610 mActions.add(action);
4611 return this;
4612 }
4613
4614 /**
4615 * Adds wearable actions to this notification.
4616 *
4617 * <p>When wearable actions are added using this method, the set of actions that
4618 * show on a wearable device splits from devices that only show actions added
4619 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4620 * of which actions display on different devices.
4621 *
4622 * @param actions the actions to add to this notification
4623 * @return this object for method chaining
4624 * @see android.app.Notification.Action
4625 */
4626 public WearableExtender addActions(List<Action> actions) {
4627 mActions.addAll(actions);
4628 return this;
4629 }
4630
4631 /**
4632 * Clear all wearable actions present on this builder.
4633 * @return this object for method chaining.
4634 * @see #addAction
4635 */
4636 public WearableExtender clearActions() {
4637 mActions.clear();
4638 return this;
4639 }
4640
4641 /**
4642 * Get the wearable actions present on this notification.
4643 */
4644 public List<Action> getActions() {
4645 return mActions;
4646 }
4647
4648 /**
4649 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004650 * this notification. The {@link PendingIntent} provided should be for an activity.
4651 *
4652 * <pre class="prettyprint">
4653 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4654 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4655 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4656 * Notification notif = new Notification.Builder(context)
4657 * .extend(new Notification.WearableExtender()
4658 * .setDisplayIntent(displayPendingIntent)
4659 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4660 * .build();</pre>
4661 *
4662 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004663 * should have an empty task affinity. It is also recommended to use the device
4664 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004665 *
4666 * <p>Example AndroidManifest.xml entry:
4667 * <pre class="prettyprint">
4668 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4669 * android:exported=&quot;true&quot;
4670 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004671 * android:taskAffinity=&quot;&quot;
4672 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004673 *
4674 * @param intent the {@link PendingIntent} for an activity
4675 * @return this object for method chaining
4676 * @see android.app.Notification.WearableExtender#getDisplayIntent
4677 */
4678 public WearableExtender setDisplayIntent(PendingIntent intent) {
4679 mDisplayIntent = intent;
4680 return this;
4681 }
4682
4683 /**
4684 * Get the intent to launch inside of an activity view when displaying this
4685 * notification. This {@code PendingIntent} should be for an activity.
4686 */
4687 public PendingIntent getDisplayIntent() {
4688 return mDisplayIntent;
4689 }
4690
4691 /**
4692 * Add an additional page of content to display with this notification. The current
4693 * notification forms the first page, and pages added using this function form
4694 * subsequent pages. This field can be used to separate a notification into multiple
4695 * sections.
4696 *
4697 * @param page the notification to add as another page
4698 * @return this object for method chaining
4699 * @see android.app.Notification.WearableExtender#getPages
4700 */
4701 public WearableExtender addPage(Notification page) {
4702 mPages.add(page);
4703 return this;
4704 }
4705
4706 /**
4707 * Add additional pages of content to display with this notification. The current
4708 * notification forms the first page, and pages added using this function form
4709 * subsequent pages. This field can be used to separate a notification into multiple
4710 * sections.
4711 *
4712 * @param pages a list of notifications
4713 * @return this object for method chaining
4714 * @see android.app.Notification.WearableExtender#getPages
4715 */
4716 public WearableExtender addPages(List<Notification> pages) {
4717 mPages.addAll(pages);
4718 return this;
4719 }
4720
4721 /**
4722 * Clear all additional pages present on this builder.
4723 * @return this object for method chaining.
4724 * @see #addPage
4725 */
4726 public WearableExtender clearPages() {
4727 mPages.clear();
4728 return this;
4729 }
4730
4731 /**
4732 * Get the array of additional pages of content for displaying this notification. The
4733 * current notification forms the first page, and elements within this array form
4734 * subsequent pages. This field can be used to separate a notification into multiple
4735 * sections.
4736 * @return the pages for this notification
4737 */
4738 public List<Notification> getPages() {
4739 return mPages;
4740 }
4741
4742 /**
4743 * Set a background image to be displayed behind the notification content.
4744 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4745 * will work with any notification style.
4746 *
4747 * @param background the background bitmap
4748 * @return this object for method chaining
4749 * @see android.app.Notification.WearableExtender#getBackground
4750 */
4751 public WearableExtender setBackground(Bitmap background) {
4752 mBackground = background;
4753 return this;
4754 }
4755
4756 /**
4757 * Get a background image to be displayed behind the notification content.
4758 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4759 * will work with any notification style.
4760 *
4761 * @return the background image
4762 * @see android.app.Notification.WearableExtender#setBackground
4763 */
4764 public Bitmap getBackground() {
4765 return mBackground;
4766 }
4767
4768 /**
4769 * Set an icon that goes with the content of this notification.
4770 */
4771 public WearableExtender setContentIcon(int icon) {
4772 mContentIcon = icon;
4773 return this;
4774 }
4775
4776 /**
4777 * Get an icon that goes with the content of this notification.
4778 */
4779 public int getContentIcon() {
4780 return mContentIcon;
4781 }
4782
4783 /**
4784 * Set the gravity that the content icon should have within the notification display.
4785 * Supported values include {@link android.view.Gravity#START} and
4786 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4787 * @see #setContentIcon
4788 */
4789 public WearableExtender setContentIconGravity(int contentIconGravity) {
4790 mContentIconGravity = contentIconGravity;
4791 return this;
4792 }
4793
4794 /**
4795 * Get the gravity that the content icon should have within the notification display.
4796 * Supported values include {@link android.view.Gravity#START} and
4797 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4798 * @see #getContentIcon
4799 */
4800 public int getContentIconGravity() {
4801 return mContentIconGravity;
4802 }
4803
4804 /**
4805 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004806 * this notification. This action will no longer display separately from the
4807 * notification's content.
4808 *
Griff Hazenca48d352014-05-28 22:37:13 -07004809 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004810 * set, although the list of available actions comes from the main notification and not
4811 * from the child page's notification.
4812 *
4813 * @param actionIndex The index of the action to hoist onto the current notification page.
4814 * If wearable actions were added to the main notification, this index
4815 * will apply to that list, otherwise it will apply to the regular
4816 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004817 */
4818 public WearableExtender setContentAction(int actionIndex) {
4819 mContentActionIndex = actionIndex;
4820 return this;
4821 }
4822
4823 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004824 * Get the index of the notification action, if any, that was specified as being clickable
4825 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004826 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004827 *
Griff Hazenca48d352014-05-28 22:37:13 -07004828 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004829 * set, although the list of available actions comes from the main notification and not
4830 * from the child page's notification.
4831 *
4832 * <p>If wearable specific actions were added to the main notification, this index will
4833 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004834 *
4835 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004836 */
4837 public int getContentAction() {
4838 return mContentActionIndex;
4839 }
4840
4841 /**
4842 * Set the gravity that this notification should have within the available viewport space.
4843 * Supported values include {@link android.view.Gravity#TOP},
4844 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4845 * The default value is {@link android.view.Gravity#BOTTOM}.
4846 */
4847 public WearableExtender setGravity(int gravity) {
4848 mGravity = gravity;
4849 return this;
4850 }
4851
4852 /**
4853 * Get the gravity that this notification should have within the available viewport space.
4854 * Supported values include {@link android.view.Gravity#TOP},
4855 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4856 * The default value is {@link android.view.Gravity#BOTTOM}.
4857 */
4858 public int getGravity() {
4859 return mGravity;
4860 }
4861
4862 /**
4863 * Set the custom size preset for the display of this notification out of the available
4864 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4865 * {@link #SIZE_LARGE}.
4866 * <p>Some custom size presets are only applicable for custom display notifications created
4867 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4868 * documentation for the preset in question. See also
4869 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4870 */
4871 public WearableExtender setCustomSizePreset(int sizePreset) {
4872 mCustomSizePreset = sizePreset;
4873 return this;
4874 }
4875
4876 /**
4877 * Get the custom size preset for the display of this notification out of the available
4878 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4879 * {@link #SIZE_LARGE}.
4880 * <p>Some custom size presets are only applicable for custom display notifications created
4881 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4882 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4883 */
4884 public int getCustomSizePreset() {
4885 return mCustomSizePreset;
4886 }
4887
4888 /**
4889 * Set the custom height in pixels for the display of this notification's content.
4890 * <p>This option is only available for custom display notifications created
4891 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4892 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4893 * {@link #getCustomContentHeight}.
4894 */
4895 public WearableExtender setCustomContentHeight(int height) {
4896 mCustomContentHeight = height;
4897 return this;
4898 }
4899
4900 /**
4901 * Get the custom height in pixels for the display of this notification's content.
4902 * <p>This option is only available for custom display notifications created
4903 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4904 * {@link #setCustomContentHeight}.
4905 */
4906 public int getCustomContentHeight() {
4907 return mCustomContentHeight;
4908 }
4909
4910 /**
4911 * Set whether the scrolling position for the contents of this notification should start
4912 * at the bottom of the contents instead of the top when the contents are too long to
4913 * display within the screen. Default is false (start scroll at the top).
4914 */
4915 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4916 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4917 return this;
4918 }
4919
4920 /**
4921 * Get whether the scrolling position for the contents of this notification should start
4922 * at the bottom of the contents instead of the top when the contents are too long to
4923 * display within the screen. Default is false (start scroll at the top).
4924 */
4925 public boolean getStartScrollBottom() {
4926 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4927 }
4928
4929 /**
4930 * Set whether the content intent is available when the wearable device is not connected
4931 * to a companion device. The user can still trigger this intent when the wearable device
4932 * is offline, but a visual hint will indicate that the content intent may not be available.
4933 * Defaults to true.
4934 */
4935 public WearableExtender setContentIntentAvailableOffline(
4936 boolean contentIntentAvailableOffline) {
4937 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4938 return this;
4939 }
4940
4941 /**
4942 * Get whether the content intent is available when the wearable device is not connected
4943 * to a companion device. The user can still trigger this intent when the wearable device
4944 * is offline, but a visual hint will indicate that the content intent may not be available.
4945 * Defaults to true.
4946 */
4947 public boolean getContentIntentAvailableOffline() {
4948 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
4949 }
4950
4951 /**
4952 * Set a hint that this notification's icon should not be displayed.
4953 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
4954 * @return this object for method chaining
4955 */
4956 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
4957 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
4958 return this;
4959 }
4960
4961 /**
4962 * Get a hint that this notification's icon should not be displayed.
4963 * @return {@code true} if this icon should not be displayed, false otherwise.
4964 * The default value is {@code false} if this was never set.
4965 */
4966 public boolean getHintHideIcon() {
4967 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
4968 }
4969
4970 /**
4971 * Set a visual hint that only the background image of this notification should be
4972 * displayed, and other semantic content should be hidden. This hint is only applicable
4973 * to sub-pages added using {@link #addPage}.
4974 */
4975 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
4976 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
4977 return this;
4978 }
4979
4980 /**
4981 * Get a visual hint that only the background image of this notification should be
4982 * displayed, and other semantic content should be hidden. This hint is only applicable
4983 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
4984 */
4985 public boolean getHintShowBackgroundOnly() {
4986 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
4987 }
4988
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004989 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08004990 * Set a hint that this notification's background should not be clipped if possible,
4991 * and should instead be resized to fully display on the screen, retaining the aspect
4992 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004993 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
4994 * @return this object for method chaining
4995 */
4996 public WearableExtender setHintAvoidBackgroundClipping(
4997 boolean hintAvoidBackgroundClipping) {
4998 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
4999 return this;
5000 }
5001
5002 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005003 * Get a hint that this notification's background should not be clipped if possible,
5004 * and should instead be resized to fully display on the screen, retaining the aspect
5005 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005006 * @return {@code true} if it's ok if the background is clipped on the screen, false
5007 * otherwise. The default value is {@code false} if this was never set.
5008 */
5009 public boolean getHintAvoidBackgroundClipping() {
5010 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5011 }
5012
5013 /**
5014 * Set a hint that the screen should remain on for at least this duration when
5015 * this notification is displayed on the screen.
5016 * @param timeout The requested screen timeout in milliseconds. Can also be either
5017 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5018 * @return this object for method chaining
5019 */
5020 public WearableExtender setHintScreenTimeout(int timeout) {
5021 mHintScreenTimeout = timeout;
5022 return this;
5023 }
5024
5025 /**
5026 * Get the duration, in milliseconds, that the screen should remain on for
5027 * when this notification is displayed.
5028 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5029 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5030 */
5031 public int getHintScreenTimeout() {
5032 return mHintScreenTimeout;
5033 }
5034
Griff Hazen61a9e862014-05-22 16:05:19 -07005035 private void setFlag(int mask, boolean value) {
5036 if (value) {
5037 mFlags |= mask;
5038 } else {
5039 mFlags &= ~mask;
5040 }
5041 }
5042 }
5043
5044 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005045 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5046 * with car extensions:
5047 *
5048 * <ol>
5049 * <li>Create an {@link Notification.Builder}, setting any desired
5050 * properties.
5051 * <li>Create a {@link CarExtender}.
5052 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5053 * {@link CarExtender}.
5054 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5055 * to apply the extensions to a notification.
5056 * </ol>
5057 *
5058 * <pre class="prettyprint">
5059 * Notification notification = new Notification.Builder(context)
5060 * ...
5061 * .extend(new CarExtender()
5062 * .set*(...))
5063 * .build();
5064 * </pre>
5065 *
5066 * <p>Car extensions can be accessed on an existing notification by using the
5067 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5068 * to access values.
5069 */
5070 public static final class CarExtender implements Extender {
5071 private static final String TAG = "CarExtender";
5072
5073 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5074 private static final String EXTRA_LARGE_ICON = "large_icon";
5075 private static final String EXTRA_CONVERSATION = "car_conversation";
5076 private static final String EXTRA_COLOR = "app_color";
5077
5078 private Bitmap mLargeIcon;
5079 private UnreadConversation mUnreadConversation;
5080 private int mColor = Notification.COLOR_DEFAULT;
5081
5082 /**
5083 * Create a {@link CarExtender} with default options.
5084 */
5085 public CarExtender() {
5086 }
5087
5088 /**
5089 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5090 *
5091 * @param notif The notification from which to copy options.
5092 */
5093 public CarExtender(Notification notif) {
5094 Bundle carBundle = notif.extras == null ?
5095 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5096 if (carBundle != null) {
5097 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5098 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5099
5100 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5101 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5102 }
5103 }
5104
5105 /**
5106 * Apply car extensions to a notification that is being built. This is typically called by
5107 * the {@link Notification.Builder#extend(Notification.Extender)}
5108 * method of {@link Notification.Builder}.
5109 */
5110 @Override
5111 public Notification.Builder extend(Notification.Builder builder) {
5112 Bundle carExtensions = new Bundle();
5113
5114 if (mLargeIcon != null) {
5115 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5116 }
5117 if (mColor != Notification.COLOR_DEFAULT) {
5118 carExtensions.putInt(EXTRA_COLOR, mColor);
5119 }
5120
5121 if (mUnreadConversation != null) {
5122 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5123 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5124 }
5125
5126 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5127 return builder;
5128 }
5129
5130 /**
5131 * Sets the accent color to use when Android Auto presents the notification.
5132 *
5133 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5134 * to accent the displayed notification. However, not all colors are acceptable in an
5135 * automotive setting. This method can be used to override the color provided in the
5136 * notification in such a situation.
5137 */
Tor Norbye80756e32015-03-02 09:39:27 -08005138 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005139 mColor = color;
5140 return this;
5141 }
5142
5143 /**
5144 * Gets the accent color.
5145 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005146 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005147 */
Tor Norbye80756e32015-03-02 09:39:27 -08005148 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005149 public int getColor() {
5150 return mColor;
5151 }
5152
5153 /**
5154 * Sets the large icon of the car notification.
5155 *
5156 * If no large icon is set in the extender, Android Auto will display the icon
5157 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5158 *
5159 * @param largeIcon The large icon to use in the car notification.
5160 * @return This object for method chaining.
5161 */
5162 public CarExtender setLargeIcon(Bitmap largeIcon) {
5163 mLargeIcon = largeIcon;
5164 return this;
5165 }
5166
5167 /**
5168 * Gets the large icon used in this car notification, or null if no icon has been set.
5169 *
5170 * @return The large icon for the car notification.
5171 * @see CarExtender#setLargeIcon
5172 */
5173 public Bitmap getLargeIcon() {
5174 return mLargeIcon;
5175 }
5176
5177 /**
5178 * Sets the unread conversation in a message notification.
5179 *
5180 * @param unreadConversation The unread part of the conversation this notification conveys.
5181 * @return This object for method chaining.
5182 */
5183 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5184 mUnreadConversation = unreadConversation;
5185 return this;
5186 }
5187
5188 /**
5189 * Returns the unread conversation conveyed by this notification.
5190 * @see #setUnreadConversation(UnreadConversation)
5191 */
5192 public UnreadConversation getUnreadConversation() {
5193 return mUnreadConversation;
5194 }
5195
5196 /**
5197 * A class which holds the unread messages from a conversation.
5198 */
5199 public static class UnreadConversation {
5200 private static final String KEY_AUTHOR = "author";
5201 private static final String KEY_TEXT = "text";
5202 private static final String KEY_MESSAGES = "messages";
5203 private static final String KEY_REMOTE_INPUT = "remote_input";
5204 private static final String KEY_ON_REPLY = "on_reply";
5205 private static final String KEY_ON_READ = "on_read";
5206 private static final String KEY_PARTICIPANTS = "participants";
5207 private static final String KEY_TIMESTAMP = "timestamp";
5208
5209 private final String[] mMessages;
5210 private final RemoteInput mRemoteInput;
5211 private final PendingIntent mReplyPendingIntent;
5212 private final PendingIntent mReadPendingIntent;
5213 private final String[] mParticipants;
5214 private final long mLatestTimestamp;
5215
5216 UnreadConversation(String[] messages, RemoteInput remoteInput,
5217 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5218 String[] participants, long latestTimestamp) {
5219 mMessages = messages;
5220 mRemoteInput = remoteInput;
5221 mReadPendingIntent = readPendingIntent;
5222 mReplyPendingIntent = replyPendingIntent;
5223 mParticipants = participants;
5224 mLatestTimestamp = latestTimestamp;
5225 }
5226
5227 /**
5228 * Gets the list of messages conveyed by this notification.
5229 */
5230 public String[] getMessages() {
5231 return mMessages;
5232 }
5233
5234 /**
5235 * Gets the remote input that will be used to convey the response to a message list, or
5236 * null if no such remote input exists.
5237 */
5238 public RemoteInput getRemoteInput() {
5239 return mRemoteInput;
5240 }
5241
5242 /**
5243 * Gets the pending intent that will be triggered when the user replies to this
5244 * notification.
5245 */
5246 public PendingIntent getReplyPendingIntent() {
5247 return mReplyPendingIntent;
5248 }
5249
5250 /**
5251 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5252 * in this object's message list.
5253 */
5254 public PendingIntent getReadPendingIntent() {
5255 return mReadPendingIntent;
5256 }
5257
5258 /**
5259 * Gets the participants in the conversation.
5260 */
5261 public String[] getParticipants() {
5262 return mParticipants;
5263 }
5264
5265 /**
5266 * Gets the firs participant in the conversation.
5267 */
5268 public String getParticipant() {
5269 return mParticipants.length > 0 ? mParticipants[0] : null;
5270 }
5271
5272 /**
5273 * Gets the timestamp of the conversation.
5274 */
5275 public long getLatestTimestamp() {
5276 return mLatestTimestamp;
5277 }
5278
5279 Bundle getBundleForUnreadConversation() {
5280 Bundle b = new Bundle();
5281 String author = null;
5282 if (mParticipants != null && mParticipants.length > 1) {
5283 author = mParticipants[0];
5284 }
5285 Parcelable[] messages = new Parcelable[mMessages.length];
5286 for (int i = 0; i < messages.length; i++) {
5287 Bundle m = new Bundle();
5288 m.putString(KEY_TEXT, mMessages[i]);
5289 m.putString(KEY_AUTHOR, author);
5290 messages[i] = m;
5291 }
5292 b.putParcelableArray(KEY_MESSAGES, messages);
5293 if (mRemoteInput != null) {
5294 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5295 }
5296 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5297 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5298 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5299 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5300 return b;
5301 }
5302
5303 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5304 if (b == null) {
5305 return null;
5306 }
5307 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5308 String[] messages = null;
5309 if (parcelableMessages != null) {
5310 String[] tmp = new String[parcelableMessages.length];
5311 boolean success = true;
5312 for (int i = 0; i < tmp.length; i++) {
5313 if (!(parcelableMessages[i] instanceof Bundle)) {
5314 success = false;
5315 break;
5316 }
5317 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5318 if (tmp[i] == null) {
5319 success = false;
5320 break;
5321 }
5322 }
5323 if (success) {
5324 messages = tmp;
5325 } else {
5326 return null;
5327 }
5328 }
5329
5330 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5331 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5332
5333 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5334
5335 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5336 if (participants == null || participants.length != 1) {
5337 return null;
5338 }
5339
5340 return new UnreadConversation(messages,
5341 remoteInput,
5342 onReply,
5343 onRead,
5344 participants, b.getLong(KEY_TIMESTAMP));
5345 }
5346 };
5347
5348 /**
5349 * Builder class for {@link CarExtender.UnreadConversation} objects.
5350 */
5351 public static class Builder {
5352 private final List<String> mMessages = new ArrayList<String>();
5353 private final String mParticipant;
5354 private RemoteInput mRemoteInput;
5355 private PendingIntent mReadPendingIntent;
5356 private PendingIntent mReplyPendingIntent;
5357 private long mLatestTimestamp;
5358
5359 /**
5360 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5361 *
5362 * @param name The name of the other participant in the conversation.
5363 */
5364 public Builder(String name) {
5365 mParticipant = name;
5366 }
5367
5368 /**
5369 * Appends a new unread message to the list of messages for this conversation.
5370 *
5371 * The messages should be added from oldest to newest.
5372 *
5373 * @param message The text of the new unread message.
5374 * @return This object for method chaining.
5375 */
5376 public Builder addMessage(String message) {
5377 mMessages.add(message);
5378 return this;
5379 }
5380
5381 /**
5382 * Sets the pending intent and remote input which will convey the reply to this
5383 * notification.
5384 *
5385 * @param pendingIntent The pending intent which will be triggered on a reply.
5386 * @param remoteInput The remote input parcelable which will carry the reply.
5387 * @return This object for method chaining.
5388 *
5389 * @see CarExtender.UnreadConversation#getRemoteInput
5390 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5391 */
5392 public Builder setReplyAction(
5393 PendingIntent pendingIntent, RemoteInput remoteInput) {
5394 mRemoteInput = remoteInput;
5395 mReplyPendingIntent = pendingIntent;
5396
5397 return this;
5398 }
5399
5400 /**
5401 * Sets the pending intent that will be sent once the messages in this notification
5402 * are read.
5403 *
5404 * @param pendingIntent The pending intent to use.
5405 * @return This object for method chaining.
5406 */
5407 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5408 mReadPendingIntent = pendingIntent;
5409 return this;
5410 }
5411
5412 /**
5413 * Sets the timestamp of the most recent message in an unread conversation.
5414 *
5415 * If a messaging notification has been posted by your application and has not
5416 * yet been cancelled, posting a later notification with the same id and tag
5417 * but without a newer timestamp may result in Android Auto not displaying a
5418 * heads up notification for the later notification.
5419 *
5420 * @param timestamp The timestamp of the most recent message in the conversation.
5421 * @return This object for method chaining.
5422 */
5423 public Builder setLatestTimestamp(long timestamp) {
5424 mLatestTimestamp = timestamp;
5425 return this;
5426 }
5427
5428 /**
5429 * Builds a new unread conversation object.
5430 *
5431 * @return The new unread conversation object.
5432 */
5433 public UnreadConversation build() {
5434 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5435 String[] participants = { mParticipant };
5436 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5437 mReadPendingIntent, participants, mLatestTimestamp);
5438 }
5439 }
5440 }
5441
5442 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005443 * Get an array of Notification objects from a parcelable array bundle field.
5444 * Update the bundle to have a typed array so fetches in the future don't need
5445 * to do an array copy.
5446 */
5447 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5448 Parcelable[] array = bundle.getParcelableArray(key);
5449 if (array instanceof Notification[] || array == null) {
5450 return (Notification[]) array;
5451 }
5452 Notification[] typedArray = Arrays.copyOf(array, array.length,
5453 Notification[].class);
5454 bundle.putParcelableArray(key, typedArray);
5455 return typedArray;
5456 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005457
5458 private static class BuilderRemoteViews extends RemoteViews {
5459 public BuilderRemoteViews(Parcel parcel) {
5460 super(parcel);
5461 }
5462
Kenny Guy77320062014-08-27 21:37:15 +01005463 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5464 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005465 }
5466
5467 @Override
5468 public BuilderRemoteViews clone() {
5469 Parcel p = Parcel.obtain();
5470 writeToParcel(p, 0);
5471 p.setDataPosition(0);
5472 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5473 p.recycle();
5474 return brv;
5475 }
5476 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005477}