blob: 6c0c3e864877c99a4ce73fdb7bb8ff518c2a2b13 [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;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050061import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070062import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070063import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070064import java.util.List;
Julia Reynolds74303cf2015-10-16 11:37:55 -040065import java.util.Objects;
Joe Onorato561d3852010-11-20 18:09:34 -080066
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067/**
68 * A class that represents how a persistent notification is to be presented to
69 * the user using the {@link android.app.NotificationManager}.
70 *
Joe Onoratocb109a02011-01-18 17:57:41 -080071 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
72 * easier to construct Notifications.</p>
73 *
Joe Fernandez558459f2011-10-13 16:47:36 -070074 * <div class="special reference">
75 * <h3>Developer Guides</h3>
76 * <p>For a guide to creating notifications, read the
77 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
78 * developer guide.</p>
79 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 */
81public class Notification implements Parcelable
82{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040083 private static final String TAG = "Notification";
84
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -040086 * An activity that provides a user interface for adjusting notification preferences for its
87 * containing application. Optional but recommended for apps that post
88 * {@link android.app.Notification Notifications}.
89 */
90 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
91 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
92 = "android.intent.category.NOTIFICATION_PREFERENCES";
93
94 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 * Use all default values (where applicable).
96 */
97 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050098
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 /**
100 * Use the default notification sound. This will ignore any given
101 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500102 *
Chris Wren47c20a12014-06-18 17:27:29 -0400103 * <p>
104 * A notification that is noisy is more likely to be presented as a heads-up notification.
105 * </p>
106 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500108 */
109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 public static final int DEFAULT_SOUND = 1;
111
112 /**
113 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500114 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700115 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500116 *
Chris Wren47c20a12014-06-18 17:27:29 -0400117 * <p>
118 * A notification that vibrates is more likely to be presented as a heads-up notification.
119 * </p>
120 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500122 */
123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 /**
127 * Use the default notification lights. This will ignore the
128 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
129 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500130 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500132 */
133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200137 * Maximum length of CharSequences accepted by Builder and friends.
138 *
139 * <p>
140 * Avoids spamming the system with overly large strings such as full e-mails.
141 */
142 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
143
144 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500145 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800146 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500147 * Default value: {@link System#currentTimeMillis() Now}.
148 *
149 * Choose a timestamp that will be most relevant to the user. For most finite events, this
150 * corresponds to the time the event happened (or will happen, in the case of events that have
151 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800152 * timestamped according to when the activity began.
153 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500154 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800155 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500156 * <ul>
157 * <li>Notification of a new chat message should be stamped when the message was received.</li>
158 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
159 * <li>Notification of a completed file download should be stamped when the download finished.</li>
160 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
161 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
162 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800163 * </ul>
164 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 */
166 public long when;
167
168 /**
169 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400170 *
171 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 */
Dan Sandler86647982015-05-13 23:41:13 -0400173 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700174 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 public int icon;
176
177 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800178 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
179 * leave it at its default value of 0.
180 *
181 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700182 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800183 */
184 public int iconLevel;
185
186 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500187 * The number of events that this notification represents. For example, in a new mail
188 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800189 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500190 * The system may or may not use this field to modify the appearance of the notification. For
191 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
192 * superimposed over the icon in the status bar. Starting with
193 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
194 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800195 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500196 * If the number is 0 or negative, it is never shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 */
198 public int number;
199
200 /**
201 * The intent to execute when the expanded status entry is clicked. If
202 * this is an activity, it must include the
203 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800204 * that you take care of task management as described in the
205 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800206 * Stack</a> document. In particular, make sure to read the notification section
207 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
208 * Notifications</a> for the correct ways to launch an application from a
209 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 */
211 public PendingIntent contentIntent;
212
213 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500214 * The intent to execute when the notification is explicitly dismissed by the user, either with
215 * the "Clear All" button or by swiping it away individually.
216 *
217 * This probably shouldn't be launching an activity since several of those will be sent
218 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 */
220 public PendingIntent deleteIntent;
221
222 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700223 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800224 *
Chris Wren47c20a12014-06-18 17:27:29 -0400225 * <p>
226 * The system UI may choose to display a heads-up notification, instead of
227 * launching this intent, while the user is using the device.
228 * </p>
229 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800230 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400231 */
232 public PendingIntent fullScreenIntent;
233
234 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400235 * Text that summarizes this notification for accessibility services.
236 *
237 * As of the L release, this text is no longer shown on screen, but it is still useful to
238 * accessibility services (where it serves as an audible announcement of the notification's
239 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400240 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800241 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 */
243 public CharSequence tickerText;
244
245 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400246 * Formerly, a view showing the {@link #tickerText}.
247 *
248 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400249 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400250 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800251 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400252
253 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400254 * The view that will represent this notification in the notification list (which is pulled
255 * down from the status bar).
256 *
257 * As of N, this field is not used. The notification view is determined by the inputs to
258 * {@link Notification.Builder}; a custom RemoteViews can optionally be
259 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400261 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 public RemoteViews contentView;
263
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400264 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400265 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400266 * opportunity to show more detail. The system UI may choose to show this
267 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400268 *
269 * As of N, this field is not used. The expanded notification view is determined by the
270 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
271 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400272 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400273 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400274 public RemoteViews bigContentView;
275
Chris Wren8fd39ec2014-02-27 17:43:26 -0500276
277 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400278 * A medium-format version of {@link #contentView}, providing the Notification an
279 * opportunity to add action buttons to contentView. At its discretion, the system UI may
280 * choose to show this as a heads-up notification, which will pop up so the user can see
281 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400282 *
283 * As of N, this field is not used. The heads-up notification view is determined by the
284 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
285 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500286 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400287 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500288 public RemoteViews headsUpContentView;
289
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400290 /**
Dan Sandler86647982015-05-13 23:41:13 -0400291 * A large bitmap to be shown in the notification content area.
292 *
293 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 */
Dan Sandler86647982015-05-13 23:41:13 -0400295 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800296 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297
298 /**
299 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500300 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400302 * A notification that is noisy is more likely to be presented as a heads-up notification.
303 * </p>
304 *
305 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500306 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 * </p>
308 */
309 public Uri sound;
310
311 /**
312 * Use this constant as the value for audioStreamType to request that
313 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700314 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400315 *
316 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700318 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 public static final int STREAM_DEFAULT = -1;
320
321 /**
322 * The audio stream type to use when playing the sound.
323 * Should be one of the STREAM_ constants from
324 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400325 *
326 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700328 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 public int audioStreamType = STREAM_DEFAULT;
330
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400332 * The default value of {@link #audioAttributes}.
333 */
334 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
335 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
336 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
337 .build();
338
339 /**
340 * The {@link AudioAttributes audio attributes} to use when playing the sound.
341 */
342 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
343
344 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500345 * The pattern with which to vibrate.
346 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 * <p>
348 * To vibrate the default pattern, see {@link #defaults}.
349 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500350 *
Chris Wren47c20a12014-06-18 17:27:29 -0400351 * <p>
352 * A notification that vibrates is more likely to be presented as a heads-up notification.
353 * </p>
354 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 * @see android.os.Vibrator#vibrate(long[],int)
356 */
357 public long[] vibrate;
358
359 /**
360 * The color of the led. The hardware will do its best approximation.
361 *
362 * @see #FLAG_SHOW_LIGHTS
363 * @see #flags
364 */
Tor Norbye80756e32015-03-02 09:39:27 -0800365 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 public int ledARGB;
367
368 /**
369 * The number of milliseconds for the LED to be on while it's flashing.
370 * The hardware will do its best approximation.
371 *
372 * @see #FLAG_SHOW_LIGHTS
373 * @see #flags
374 */
375 public int ledOnMS;
376
377 /**
378 * The number of milliseconds for the LED to be off while it's flashing.
379 * The hardware will do its best approximation.
380 *
381 * @see #FLAG_SHOW_LIGHTS
382 * @see #flags
383 */
384 public int ledOffMS;
385
386 /**
387 * Specifies which values should be taken from the defaults.
388 * <p>
389 * To set, OR the desired from {@link #DEFAULT_SOUND},
390 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
391 * values, use {@link #DEFAULT_ALL}.
392 * </p>
393 */
394 public int defaults;
395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 /**
397 * Bit to be bitwise-ored into the {@link #flags} field that should be
398 * set if you want the LED on for this notification.
399 * <ul>
400 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
401 * or 0 for both ledOnMS and ledOffMS.</li>
402 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
403 * <li>To flash the LED, pass the number of milliseconds that it should
404 * be on and off to ledOnMS and ledOffMS.</li>
405 * </ul>
406 * <p>
407 * Since hardware varies, you are not guaranteed that any of the values
408 * you pass are honored exactly. Use the system defaults (TODO) if possible
409 * because they will be set to values that work on any given hardware.
410 * <p>
411 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500412 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 */
414 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
415
416 /**
417 * Bit to be bitwise-ored into the {@link #flags} field that should be
418 * set if this notification is in reference to something that is ongoing,
419 * like a phone call. It should not be set if this notification is in
420 * reference to something that happened at a particular point in time,
421 * like a missed phone call.
422 */
423 public static final int FLAG_ONGOING_EVENT = 0x00000002;
424
425 /**
426 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700427 * the audio will be repeated until the notification is
428 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 */
430 public static final int FLAG_INSISTENT = 0x00000004;
431
432 /**
433 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700434 * set if you would only like the sound, vibrate and ticker to be played
435 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 */
437 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
438
439 /**
440 * Bit to be bitwise-ored into the {@link #flags} field that should be
441 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500442 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 */
444 public static final int FLAG_AUTO_CANCEL = 0x00000010;
445
446 /**
447 * Bit to be bitwise-ored into the {@link #flags} field that should be
448 * set if the notification should not be canceled when the user clicks
449 * the Clear all button.
450 */
451 public static final int FLAG_NO_CLEAR = 0x00000020;
452
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700453 /**
454 * Bit to be bitwise-ored into the {@link #flags} field that should be
455 * set if this notification represents a currently running service. This
456 * will normally be set for you by {@link Service#startForeground}.
457 */
458 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
459
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400460 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500461 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800462 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500463 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400464 */
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500465 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400466
Griff Hazendfcb0802014-02-11 12:00:00 -0800467 /**
468 * Bit to be bitswise-ored into the {@link #flags} field that should be
469 * set if this notification is relevant to the current device only
470 * and it is not recommended that it bridge to other devices.
471 */
472 public static final int FLAG_LOCAL_ONLY = 0x00000100;
473
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700474 /**
475 * Bit to be bitswise-ored into the {@link #flags} field that should be
476 * set if this notification is the group summary for a group of notifications.
477 * Grouped notifications may display in a cluster or stack on devices which
478 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
479 */
480 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
481
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 public int flags;
483
Tor Norbyed9273d62013-05-30 15:59:53 -0700484 /** @hide */
485 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
486 @Retention(RetentionPolicy.SOURCE)
487 public @interface Priority {}
488
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500490 * Default notification {@link #priority}. If your application does not prioritize its own
491 * notifications, use this value for all notifications.
492 */
493 public static final int PRIORITY_DEFAULT = 0;
494
495 /**
496 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
497 * items smaller, or at a different position in the list, compared with your app's
498 * {@link #PRIORITY_DEFAULT} items.
499 */
500 public static final int PRIORITY_LOW = -1;
501
502 /**
503 * Lowest {@link #priority}; these items might not be shown to the user except under special
504 * circumstances, such as detailed notification logs.
505 */
506 public static final int PRIORITY_MIN = -2;
507
508 /**
509 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
510 * show these items larger, or at a different position in notification lists, compared with
511 * your app's {@link #PRIORITY_DEFAULT} items.
512 */
513 public static final int PRIORITY_HIGH = 1;
514
515 /**
516 * Highest {@link #priority}, for your application's most important items that require the
517 * user's prompt attention or input.
518 */
519 public static final int PRIORITY_MAX = 2;
520
521 /**
522 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800523 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500524 * Priority is an indication of how much of the user's valuable attention should be consumed by
525 * this notification. Low-priority notifications may be hidden from the user in certain
526 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500527 * system will make a determination about how to interpret this priority when presenting
528 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400529 *
530 * <p>
531 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
532 * as a heads-up notification.
533 * </p>
534 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500535 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700536 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500537 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800538
Dan Sandler26e81cf2014-05-06 10:01:27 -0400539 /**
540 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
541 * to be applied by the standard Style templates when presenting this notification.
542 *
543 * The current template design constructs a colorful header image by overlaying the
544 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
545 * ignored.
546 */
Tor Norbye80756e32015-03-02 09:39:27 -0800547 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400548 public int color = COLOR_DEFAULT;
549
550 /**
551 * Special value of {@link #color} telling the system not to decorate this notification with
552 * any special color but instead use default colors when presenting this notification.
553 */
Tor Norbye80756e32015-03-02 09:39:27 -0800554 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400555 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600556
557 /**
558 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
559 * the notification's presence and contents in untrusted situations (namely, on the secure
560 * lockscreen).
561 *
562 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
563 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
564 * shown in all situations, but the contents are only available if the device is unlocked for
565 * the appropriate user.
566 *
567 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
568 * can be read even in an "insecure" context (that is, above a secure lockscreen).
569 * To modify the public version of this notification—for example, to redact some portions—see
570 * {@link Builder#setPublicVersion(Notification)}.
571 *
572 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
573 * and ticker until the user has bypassed the lockscreen.
574 */
575 public int visibility;
576
Griff Hazenfc3922d2014-08-20 11:56:44 -0700577 /**
578 * Notification visibility: Show this notification in its entirety on all lockscreens.
579 *
580 * {@see #visibility}
581 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600582 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700583
584 /**
585 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
586 * private information on secure lockscreens.
587 *
588 * {@see #visibility}
589 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600590 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700591
592 /**
593 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
594 *
595 * {@see #visibility}
596 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600597 public static final int VISIBILITY_SECRET = -1;
598
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500599 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400600 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500601 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400602 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500603
604 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400605 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500606 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400607 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500608
609 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400610 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500611 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400612 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500613
614 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400615 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500616 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400617 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500618
619 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400620 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500621 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400622 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500623
624 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400625 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500626 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400627 public static final String CATEGORY_ALARM = "alarm";
628
629 /**
630 * Notification category: progress of a long-running background operation.
631 */
632 public static final String CATEGORY_PROGRESS = "progress";
633
634 /**
635 * Notification category: social network or sharing update.
636 */
637 public static final String CATEGORY_SOCIAL = "social";
638
639 /**
640 * Notification category: error in background operation or authentication status.
641 */
642 public static final String CATEGORY_ERROR = "err";
643
644 /**
645 * Notification category: media transport control for playback.
646 */
647 public static final String CATEGORY_TRANSPORT = "transport";
648
649 /**
650 * Notification category: system or device status update. Reserved for system use.
651 */
652 public static final String CATEGORY_SYSTEM = "sys";
653
654 /**
655 * Notification category: indication of running background service.
656 */
657 public static final String CATEGORY_SERVICE = "service";
658
659 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400660 * Notification category: a specific, timely recommendation for a single thing.
661 * For example, a news app might want to recommend a news story it believes the user will
662 * want to read next.
663 */
664 public static final String CATEGORY_RECOMMENDATION = "recommendation";
665
666 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400667 * Notification category: ongoing information about device or contextual status.
668 */
669 public static final String CATEGORY_STATUS = "status";
670
671 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400672 * Notification category: user-scheduled reminder.
673 */
674 public static final String CATEGORY_REMINDER = "reminder";
675
676 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400677 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
678 * that best describes this Notification. May be used by the system for ranking and filtering.
679 */
680 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500681
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700682 private String mGroupKey;
683
684 /**
685 * Get the key used to group this notification into a cluster or stack
686 * with other notifications on devices which support such rendering.
687 */
688 public String getGroup() {
689 return mGroupKey;
690 }
691
692 private String mSortKey;
693
694 /**
695 * Get a sort key that orders this notification among other notifications from the
696 * same package. This can be useful if an external sort was already applied and an app
697 * would like to preserve this. Notifications will be sorted lexicographically using this
698 * value, although providing different priorities in addition to providing sort key may
699 * cause this value to be ignored.
700 *
701 * <p>This sort key can also be used to order members of a notification group. See
702 * {@link Builder#setGroup}.
703 *
704 * @see String#compareTo(String)
705 */
706 public String getSortKey() {
707 return mSortKey;
708 }
709
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500710 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400711 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400712 * <p>
713 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
714 * APIs, and are intended to be used by
715 * {@link android.service.notification.NotificationListenerService} implementations to extract
716 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500717 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400718 public Bundle extras = new Bundle();
719
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400720 /**
721 * {@link #extras} key: this is the title of the notification,
722 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
723 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500724 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400725
726 /**
727 * {@link #extras} key: this is the title of the notification when shown in expanded form,
728 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
729 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400730 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400731
732 /**
733 * {@link #extras} key: this is the main text payload, as supplied to
734 * {@link Builder#setContentText(CharSequence)}.
735 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500736 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400737
738 /**
739 * {@link #extras} key: this is a third line of text, as supplied to
740 * {@link Builder#setSubText(CharSequence)}.
741 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400742 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400743
744 /**
745 * {@link #extras} key: this is a small piece of additional text as supplied to
746 * {@link Builder#setContentInfo(CharSequence)}.
747 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400748 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400749
750 /**
751 * {@link #extras} key: this is a line of summary information intended to be shown
752 * alongside expanded notifications, as supplied to (e.g.)
753 * {@link BigTextStyle#setSummaryText(CharSequence)}.
754 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400755 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400756
757 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200758 * {@link #extras} key: this is the longer text shown in the big form of a
759 * {@link BigTextStyle} notification, as supplied to
760 * {@link BigTextStyle#bigText(CharSequence)}.
761 */
762 public static final String EXTRA_BIG_TEXT = "android.bigText";
763
764 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400765 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
766 * supplied to {@link Builder#setSmallIcon(int)}.
767 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500768 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400769
770 /**
771 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
772 * notification payload, as
773 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
774 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400775 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400776
777 /**
778 * {@link #extras} key: this is a bitmap to be used instead of the one from
779 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
780 * shown in its expanded form, as supplied to
781 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
782 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400783 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400784
785 /**
786 * {@link #extras} key: this is the progress value supplied to
787 * {@link Builder#setProgress(int, int, boolean)}.
788 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400789 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400790
791 /**
792 * {@link #extras} key: this is the maximum value supplied to
793 * {@link Builder#setProgress(int, int, boolean)}.
794 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400795 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400796
797 /**
798 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
799 * {@link Builder#setProgress(int, int, boolean)}.
800 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400801 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400802
803 /**
804 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
805 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
806 * {@link Builder#setUsesChronometer(boolean)}.
807 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400808 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400809
810 /**
811 * {@link #extras} key: whether {@link #when} should be shown,
812 * as supplied to {@link Builder#setShowWhen(boolean)}.
813 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400814 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400815
816 /**
817 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
818 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
819 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400820 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400821
822 /**
823 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
824 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
825 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400826 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400827
828 /**
829 * {@link #extras} key: A string representing the name of the specific
830 * {@link android.app.Notification.Style} used to create this notification.
831 */
Chris Wren91ad5632013-06-05 15:05:57 -0400832 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400833
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400834 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400835 * {@link #extras} key: A String array containing the people that this notification relates to,
836 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400837 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400838 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500839
840 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400841 * Allow certain system-generated notifications to appear before the device is provisioned.
842 * Only available to notifications coming from the android package.
843 * @hide
844 */
845 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
846
847 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700848 * {@link #extras} key: A
849 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
850 * in the background when the notification is selected. The URI must point to an image stream
851 * suitable for passing into
852 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
853 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
854 * URI used for this purpose must require no permissions to read the image data.
855 */
856 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
857
858 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400859 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700860 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400861 * {@link android.app.Notification.MediaStyle} notification.
862 */
863 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
864
865 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100866 * {@link #extras} key: the indices of actions to be shown in the compact view,
867 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
868 */
869 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
870
Christoph Studer943aa672014-08-03 20:31:16 +0200871 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100872 * {@link #extras} key: the user that built the notification.
873 *
874 * @hide
875 */
876 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
877
878 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400879 * @hide
880 */
881 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
882
Dan Sandlerd63f9322015-05-06 15:18:49 -0400883 private Icon mSmallIcon;
884 private Icon mLargeIcon;
885
Chris Wren51c75102013-07-16 20:49:17 -0400886 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400887 * Structure to encapsulate a named action that can be shown as part of this notification.
888 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
889 * selected by the user.
890 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700891 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
892 * or {@link Notification.Builder#addAction(Notification.Action)}
893 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400894 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500895 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700896 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -0400897 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -0700898 private final RemoteInput[] mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700899
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400900 /**
901 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -0400902 *
903 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400904 */
Dan Sandler86647982015-05-13 23:41:13 -0400905 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400906 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700907
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400908 /**
909 * Title of the action.
910 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400911 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700912
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400913 /**
914 * Intent to send when the user invokes this action. May be null, in which case the action
915 * may be rendered in a disabled presentation by the system UI.
916 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400917 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -0700918
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400919 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -0400920 if (in.readInt() != 0) {
921 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -0400922 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
923 icon = mIcon.getResId();
924 }
Dan Sandler86647982015-05-13 23:41:13 -0400925 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400926 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
927 if (in.readInt() == 1) {
928 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
929 }
Griff Hazen959591e2014-05-15 22:26:18 -0700930 mExtras = in.readBundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700931 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400932 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700933
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400934 /**
Dan Sandler86647982015-05-13 23:41:13 -0400935 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400936 */
Dan Sandler86647982015-05-13 23:41:13 -0400937 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400938 public Action(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -0400939 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -0700940 }
941
Dan Sandler86647982015-05-13 23:41:13 -0400942 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700943 RemoteInput[] remoteInputs) {
Dan Sandler86647982015-05-13 23:41:13 -0400944 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -0400945 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
946 this.icon = icon.getResId();
947 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400948 this.title = title;
949 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -0700950 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700951 this.mRemoteInputs = remoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700952 }
953
954 /**
Dan Sandler86647982015-05-13 23:41:13 -0400955 * Return an icon representing the action.
956 */
957 public Icon getIcon() {
958 if (mIcon == null && icon != 0) {
959 // you snuck an icon in here without using the builder; let's try to keep it
960 mIcon = Icon.createWithResource("", icon);
961 }
962 return mIcon;
963 }
964
965 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700966 * Get additional metadata carried around with this Action.
967 */
968 public Bundle getExtras() {
969 return mExtras;
970 }
971
972 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700973 * Get the list of inputs to be collected from the user when this action is sent.
974 * May return null if no remote inputs were added.
975 */
976 public RemoteInput[] getRemoteInputs() {
977 return mRemoteInputs;
978 }
979
980 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700981 * Builder class for {@link Action} objects.
982 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700983 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -0400984 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -0700985 private final CharSequence mTitle;
986 private final PendingIntent mIntent;
987 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700988 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700989
990 /**
991 * Construct a new builder for {@link Action} object.
992 * @param icon icon to show for this action
993 * @param title the title of the action
994 * @param intent the {@link PendingIntent} to fire when users trigger this action
995 */
Dan Sandler86647982015-05-13 23:41:13 -0400996 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -0700997 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -0400998 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
999 }
1000
1001 /**
1002 * Construct a new builder for {@link Action} object.
1003 * @param icon icon to show for this action
1004 * @param title the title of the action
1005 * @param intent the {@link PendingIntent} to fire when users trigger this action
1006 */
1007 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001008 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001009 }
1010
1011 /**
1012 * Construct a new builder for {@link Action} object using the fields from an
1013 * {@link Action}.
1014 * @param action the action to read fields from.
1015 */
1016 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001017 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001018 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001019 }
1020
Dan Sandler86647982015-05-13 23:41:13 -04001021 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001022 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001023 mIcon = icon;
1024 mTitle = title;
1025 mIntent = intent;
1026 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001027 if (remoteInputs != null) {
1028 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1029 Collections.addAll(mRemoteInputs, remoteInputs);
1030 }
Griff Hazen959591e2014-05-15 22:26:18 -07001031 }
1032
1033 /**
1034 * Merge additional metadata into this builder.
1035 *
1036 * <p>Values within the Bundle will replace existing extras values in this Builder.
1037 *
1038 * @see Notification.Action#extras
1039 */
1040 public Builder addExtras(Bundle extras) {
1041 if (extras != null) {
1042 mExtras.putAll(extras);
1043 }
1044 return this;
1045 }
1046
1047 /**
1048 * Get the metadata Bundle used by this Builder.
1049 *
1050 * <p>The returned Bundle is shared with this Builder.
1051 */
1052 public Bundle getExtras() {
1053 return mExtras;
1054 }
1055
1056 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001057 * Add an input to be collected from the user when this action is sent.
1058 * Response values can be retrieved from the fired intent by using the
1059 * {@link RemoteInput#getResultsFromIntent} function.
1060 * @param remoteInput a {@link RemoteInput} to add to the action
1061 * @return this object for method chaining
1062 */
1063 public Builder addRemoteInput(RemoteInput remoteInput) {
1064 if (mRemoteInputs == null) {
1065 mRemoteInputs = new ArrayList<RemoteInput>();
1066 }
1067 mRemoteInputs.add(remoteInput);
1068 return this;
1069 }
1070
1071 /**
1072 * Apply an extender to this action builder. Extenders may be used to add
1073 * metadata or change options on this builder.
1074 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001075 public Builder extend(Extender extender) {
1076 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001077 return this;
1078 }
1079
1080 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001081 * Combine all of the options that have been set and return a new {@link Action}
1082 * object.
1083 * @return the built action
1084 */
1085 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001086 RemoteInput[] remoteInputs = mRemoteInputs != null
1087 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1088 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
Griff Hazen959591e2014-05-15 22:26:18 -07001089 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001090 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001091
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001092 @Override
1093 public Action clone() {
1094 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001095 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001096 title,
1097 actionIntent, // safe to alias
1098 new Bundle(mExtras),
1099 getRemoteInputs());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001100 }
1101 @Override
1102 public int describeContents() {
1103 return 0;
1104 }
1105 @Override
1106 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001107 final Icon ic = getIcon();
1108 if (ic != null) {
1109 out.writeInt(1);
1110 ic.writeToParcel(out, 0);
1111 } else {
1112 out.writeInt(0);
1113 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001114 TextUtils.writeToParcel(title, out, flags);
1115 if (actionIntent != null) {
1116 out.writeInt(1);
1117 actionIntent.writeToParcel(out, flags);
1118 } else {
1119 out.writeInt(0);
1120 }
Griff Hazen959591e2014-05-15 22:26:18 -07001121 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001122 out.writeTypedArray(mRemoteInputs, flags);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001123 }
Griff Hazen959591e2014-05-15 22:26:18 -07001124 public static final Parcelable.Creator<Action> CREATOR =
1125 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001126 public Action createFromParcel(Parcel in) {
1127 return new Action(in);
1128 }
1129 public Action[] newArray(int size) {
1130 return new Action[size];
1131 }
1132 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001133
1134 /**
1135 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1136 * metadata or change options on an action builder.
1137 */
1138 public interface Extender {
1139 /**
1140 * Apply this extender to a notification action builder.
1141 * @param builder the builder to be modified.
1142 * @return the build object for chaining.
1143 */
1144 public Builder extend(Builder builder);
1145 }
1146
1147 /**
1148 * Wearable extender for notification actions. To add extensions to an action,
1149 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1150 * the {@code WearableExtender()} constructor and apply it to a
1151 * {@link android.app.Notification.Action.Builder} using
1152 * {@link android.app.Notification.Action.Builder#extend}.
1153 *
1154 * <pre class="prettyprint">
1155 * Notification.Action action = new Notification.Action.Builder(
1156 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001157 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001158 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001159 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001160 */
1161 public static final class WearableExtender implements Extender {
1162 /** Notification action extra which contains wearable extensions */
1163 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1164
Pete Gastaf6781d2014-10-07 15:17:05 -04001165 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001166 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001167 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1168 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1169 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001170
1171 // Flags bitwise-ored to mFlags
1172 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1173
1174 // Default value for flags integer
1175 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1176
1177 private int mFlags = DEFAULT_FLAGS;
1178
Pete Gastaf6781d2014-10-07 15:17:05 -04001179 private CharSequence mInProgressLabel;
1180 private CharSequence mConfirmLabel;
1181 private CharSequence mCancelLabel;
1182
Griff Hazen61a9e862014-05-22 16:05:19 -07001183 /**
1184 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1185 * options.
1186 */
1187 public WearableExtender() {
1188 }
1189
1190 /**
1191 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1192 * wearable options present in an existing notification action.
1193 * @param action the notification action to inspect.
1194 */
1195 public WearableExtender(Action action) {
1196 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1197 if (wearableBundle != null) {
1198 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001199 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1200 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1201 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001202 }
1203 }
1204
1205 /**
1206 * Apply wearable extensions to a notification action that is being built. This is
1207 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1208 * method of {@link android.app.Notification.Action.Builder}.
1209 */
1210 @Override
1211 public Action.Builder extend(Action.Builder builder) {
1212 Bundle wearableBundle = new Bundle();
1213
1214 if (mFlags != DEFAULT_FLAGS) {
1215 wearableBundle.putInt(KEY_FLAGS, mFlags);
1216 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001217 if (mInProgressLabel != null) {
1218 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1219 }
1220 if (mConfirmLabel != null) {
1221 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1222 }
1223 if (mCancelLabel != null) {
1224 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1225 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001226
1227 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1228 return builder;
1229 }
1230
1231 @Override
1232 public WearableExtender clone() {
1233 WearableExtender that = new WearableExtender();
1234 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001235 that.mInProgressLabel = this.mInProgressLabel;
1236 that.mConfirmLabel = this.mConfirmLabel;
1237 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001238 return that;
1239 }
1240
1241 /**
1242 * Set whether this action is available when the wearable device is not connected to
1243 * a companion device. The user can still trigger this action when the wearable device is
1244 * offline, but a visual hint will indicate that the action may not be available.
1245 * Defaults to true.
1246 */
1247 public WearableExtender setAvailableOffline(boolean availableOffline) {
1248 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1249 return this;
1250 }
1251
1252 /**
1253 * Get whether this action is available when the wearable device is not connected to
1254 * a companion device. The user can still trigger this action when the wearable device is
1255 * offline, but a visual hint will indicate that the action may not be available.
1256 * Defaults to true.
1257 */
1258 public boolean isAvailableOffline() {
1259 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1260 }
1261
1262 private void setFlag(int mask, boolean value) {
1263 if (value) {
1264 mFlags |= mask;
1265 } else {
1266 mFlags &= ~mask;
1267 }
1268 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001269
1270 /**
1271 * Set a label to display while the wearable is preparing to automatically execute the
1272 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1273 *
1274 * @param label the label to display while the action is being prepared to execute
1275 * @return this object for method chaining
1276 */
1277 public WearableExtender setInProgressLabel(CharSequence label) {
1278 mInProgressLabel = label;
1279 return this;
1280 }
1281
1282 /**
1283 * Get the label to display while the wearable is preparing to automatically execute
1284 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1285 *
1286 * @return the label to display while the action is being prepared to execute
1287 */
1288 public CharSequence getInProgressLabel() {
1289 return mInProgressLabel;
1290 }
1291
1292 /**
1293 * Set a label to display to confirm that the action should be executed.
1294 * This is usually an imperative verb like "Send".
1295 *
1296 * @param label the label to confirm the action should be executed
1297 * @return this object for method chaining
1298 */
1299 public WearableExtender setConfirmLabel(CharSequence label) {
1300 mConfirmLabel = label;
1301 return this;
1302 }
1303
1304 /**
1305 * Get the label to display to confirm that the action should be executed.
1306 * This is usually an imperative verb like "Send".
1307 *
1308 * @return the label to confirm the action should be executed
1309 */
1310 public CharSequence getConfirmLabel() {
1311 return mConfirmLabel;
1312 }
1313
1314 /**
1315 * Set a label to display to cancel the action.
1316 * This is usually an imperative verb, like "Cancel".
1317 *
1318 * @param label the label to display to cancel the action
1319 * @return this object for method chaining
1320 */
1321 public WearableExtender setCancelLabel(CharSequence label) {
1322 mCancelLabel = label;
1323 return this;
1324 }
1325
1326 /**
1327 * Get the label to display to cancel the action.
1328 * This is usually an imperative verb like "Cancel".
1329 *
1330 * @return the label to display to cancel the action
1331 */
1332 public CharSequence getCancelLabel() {
1333 return mCancelLabel;
1334 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001335 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001336 }
1337
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001338 /**
1339 * Array of all {@link Action} structures attached to this notification by
1340 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1341 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1342 * interface for invoking actions.
1343 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001344 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001345
1346 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001347 * Replacement version of this notification whose content will be shown
1348 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1349 * and {@link #VISIBILITY_PUBLIC}.
1350 */
1351 public Notification publicVersion;
1352
1353 /**
Julia Reynolds74303cf2015-10-16 11:37:55 -04001354 * Structure to encapsulate a topic that is shown in Notification settings.
1355 * It must include an id and label.
1356 */
1357 public static class Topic implements Parcelable {
1358 private final String id;
1359 private final CharSequence label;
1360
1361 public Topic(String id, CharSequence label) {
1362 this.id = id;
1363 this.label = safeCharSequence(label);
1364 }
1365
1366 private Topic(Parcel in) {
1367 if (in.readInt() != 0) {
1368 id = in.readString();
1369 } else {
1370 id = null;
1371 }
1372 label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1373 }
1374
1375 public String getId() {
1376 return id;
1377 }
1378
1379 public CharSequence getLabel() {
1380 return label;
1381 }
1382
1383 @Override
1384 public String toString() {
1385 return new StringBuilder(Topic.class.getSimpleName()).append('[')
1386 .append("id=").append(id)
1387 .append(",label=").append(label)
1388 .append(']').toString();
1389 }
1390
1391 @Override
1392 public boolean equals(Object o) {
1393 if (!(o instanceof Topic)) return false;
1394 if (o == this) return true;
1395 final Topic other = (Topic) o;
1396 return Objects.equals(other.id, id)
1397 && Objects.equals(other.label, label);
1398 }
1399
1400 @Override
1401 public int hashCode() {
1402 return Objects.hash(id, label);
1403 }
1404
1405 @Override
1406 public Topic clone() {
1407 return new Topic(id, label);
1408 }
1409
1410 @Override
1411 public int describeContents() {
1412 return 0;
1413 }
1414
1415 @Override
1416 public void writeToParcel(Parcel out, int flags) {
1417 if (id != null) {
1418 out.writeInt(1);
1419 out.writeString(id);
1420 } else {
1421 out.writeInt(0);
1422 }
1423 TextUtils.writeToParcel(label, out, flags);
1424 }
1425 public static final Parcelable.Creator<Topic> CREATOR =
1426 new Parcelable.Creator<Topic>() {
1427 public Topic createFromParcel(Parcel in) {
1428 return new Topic(in);
1429 }
1430 public Topic[] newArray(int size) {
1431 return new Topic[size];
1432 }
1433 };
1434 }
1435
Julia Reynolds233a5f92015-10-19 13:51:23 -04001436 @SystemApi
1437 public static final String TOPIC_DEFAULT = "system_default_topic";
1438
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001439 private Topic topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001440
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001441 public Topic getTopic() {
1442 return topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001443 }
1444
1445 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001446 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001447 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448 */
1449 public Notification()
1450 {
1451 this.when = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001452 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 }
1454
1455 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001456 * @hide
1457 */
1458 public Notification(Context context, int icon, CharSequence tickerText, long when,
1459 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1460 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001461 new Builder(context)
1462 .setWhen(when)
1463 .setSmallIcon(icon)
1464 .setTicker(tickerText)
1465 .setContentTitle(contentTitle)
1466 .setContentText(contentText)
1467 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1468 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 }
1470
1471 /**
1472 * Constructs a Notification object with the information needed to
1473 * have a status bar icon without the standard expanded view.
1474 *
1475 * @param icon The resource id of the icon to put in the status bar.
1476 * @param tickerText The text that flows by in the status bar when the notification first
1477 * activates.
1478 * @param when The time to show in the time field. In the System.currentTimeMillis
1479 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001480 *
1481 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001483 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 public Notification(int icon, CharSequence tickerText, long when)
1485 {
1486 this.icon = icon;
1487 this.tickerText = tickerText;
1488 this.when = when;
1489 }
1490
1491 /**
1492 * Unflatten the notification from a parcel.
1493 */
1494 public Notification(Parcel parcel)
1495 {
1496 int version = parcel.readInt();
1497
1498 when = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001499 if (parcel.readInt() != 0) {
1500 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001501 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1502 icon = mSmallIcon.getResId();
1503 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001504 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 number = parcel.readInt();
1506 if (parcel.readInt() != 0) {
1507 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1508 }
1509 if (parcel.readInt() != 0) {
1510 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1511 }
1512 if (parcel.readInt() != 0) {
1513 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1514 }
1515 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001516 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001517 }
1518 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001519 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1520 }
Joe Onorato561d3852010-11-20 18:09:34 -08001521 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001522 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001523 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 defaults = parcel.readInt();
1525 flags = parcel.readInt();
1526 if (parcel.readInt() != 0) {
1527 sound = Uri.CREATOR.createFromParcel(parcel);
1528 }
1529
1530 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001531 if (parcel.readInt() != 0) {
1532 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1533 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 vibrate = parcel.createLongArray();
1535 ledARGB = parcel.readInt();
1536 ledOnMS = parcel.readInt();
1537 ledOffMS = parcel.readInt();
1538 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001539
1540 if (parcel.readInt() != 0) {
1541 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1542 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001543
1544 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001545
John Spurlockfd7f1e02014-03-18 16:41:57 -04001546 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001547
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001548 mGroupKey = parcel.readString();
1549
1550 mSortKey = parcel.readString();
1551
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001552 extras = parcel.readBundle(); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001553
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001554 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1555
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001556 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001557 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1558 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001559
Chris Wren8fd39ec2014-02-27 17:43:26 -05001560 if (parcel.readInt() != 0) {
1561 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1562 }
1563
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001564 visibility = parcel.readInt();
1565
1566 if (parcel.readInt() != 0) {
1567 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1568 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001569
1570 color = parcel.readInt();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001571
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001572 if (parcel.readInt() != 0) {
1573 topic = Topic.CREATOR.createFromParcel(parcel);
1574 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001575 }
1576
Andy Stadler110988c2010-12-03 14:29:16 -08001577 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001578 public Notification clone() {
1579 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001580 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001581 return that;
1582 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001583
Daniel Sandler1a497d32013-04-18 14:52:45 -04001584 /**
1585 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1586 * of this into that.
1587 * @hide
1588 */
1589 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001590 that.when = this.when;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001591 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001592 that.number = this.number;
1593
1594 // PendingIntents are global, so there's no reason (or way) to clone them.
1595 that.contentIntent = this.contentIntent;
1596 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001597 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001598
1599 if (this.tickerText != null) {
1600 that.tickerText = this.tickerText.toString();
1601 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001602 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001603 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001604 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001605 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001606 that.contentView = this.contentView.clone();
1607 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001608 if (heavy && this.mLargeIcon != null) {
1609 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001610 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001611 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001612 that.sound = this.sound; // android.net.Uri is immutable
1613 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001614 if (this.audioAttributes != null) {
1615 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1616 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001617
1618 final long[] vibrate = this.vibrate;
1619 if (vibrate != null) {
1620 final int N = vibrate.length;
1621 final long[] vib = that.vibrate = new long[N];
1622 System.arraycopy(vibrate, 0, vib, 0, N);
1623 }
1624
1625 that.ledARGB = this.ledARGB;
1626 that.ledOnMS = this.ledOnMS;
1627 that.ledOffMS = this.ledOffMS;
1628 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001629
Joe Onorato18e69df2010-05-17 22:26:12 -07001630 that.flags = this.flags;
1631
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001632 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001633
John Spurlockfd7f1e02014-03-18 16:41:57 -04001634 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001635
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001636 that.mGroupKey = this.mGroupKey;
1637
1638 that.mSortKey = this.mSortKey;
1639
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001640 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001641 try {
1642 that.extras = new Bundle(this.extras);
1643 // will unparcel
1644 that.extras.size();
1645 } catch (BadParcelableException e) {
1646 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1647 that.extras = null;
1648 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001649 }
1650
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001651 if (this.actions != null) {
1652 that.actions = new Action[this.actions.length];
1653 for(int i=0; i<this.actions.length; i++) {
1654 that.actions[i] = this.actions[i].clone();
1655 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001656 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001657
Daniel Sandler1a497d32013-04-18 14:52:45 -04001658 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001659 that.bigContentView = this.bigContentView.clone();
1660 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001661
Chris Wren8fd39ec2014-02-27 17:43:26 -05001662 if (heavy && this.headsUpContentView != null) {
1663 that.headsUpContentView = this.headsUpContentView.clone();
1664 }
1665
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001666 that.visibility = this.visibility;
1667
1668 if (this.publicVersion != null) {
1669 that.publicVersion = new Notification();
1670 this.publicVersion.cloneInto(that.publicVersion, heavy);
1671 }
1672
Dan Sandler26e81cf2014-05-06 10:01:27 -04001673 that.color = this.color;
1674
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001675 if (this.topic != null) {
1676 that.topic = this.topic.clone();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001677 }
1678
Daniel Sandler1a497d32013-04-18 14:52:45 -04001679 if (!heavy) {
1680 that.lightenPayload(); // will clean out extras
1681 }
1682 }
1683
1684 /**
1685 * Removes heavyweight parts of the Notification object for archival or for sending to
1686 * listeners when the full contents are not necessary.
1687 * @hide
1688 */
1689 public final void lightenPayload() {
1690 tickerView = null;
1691 contentView = null;
1692 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001693 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001694 mLargeIcon = null;
Daniel Sandler1a497d32013-04-18 14:52:45 -04001695 if (extras != null) {
1696 extras.remove(Notification.EXTRA_LARGE_ICON);
1697 extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1698 extras.remove(Notification.EXTRA_PICTURE);
Christoph Studer223f44e2014-09-02 14:59:32 +02001699 extras.remove(Notification.EXTRA_BIG_TEXT);
Daniel Sandler1a497d32013-04-18 14:52:45 -04001700 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001701 }
1702
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001703 /**
1704 * Make sure this CharSequence is safe to put into a bundle, which basically
1705 * means it had better not be some custom Parcelable implementation.
1706 * @hide
1707 */
1708 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001709 if (cs == null) return cs;
1710 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1711 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1712 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001713 if (cs instanceof Parcelable) {
1714 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1715 + " instance is a custom Parcelable and not allowed in Notification");
1716 return cs.toString();
1717 }
1718
1719 return cs;
1720 }
1721
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 public int describeContents() {
1723 return 0;
1724 }
1725
1726 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001727 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 */
1729 public void writeToParcel(Parcel parcel, int flags)
1730 {
1731 parcel.writeInt(1);
1732
1733 parcel.writeLong(when);
Dan Sandler4e787062015-06-17 15:09:48 -04001734 if (mSmallIcon == null && icon != 0) {
1735 // you snuck an icon in here without using the builder; let's try to keep it
1736 mSmallIcon = Icon.createWithResource("", icon);
1737 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001738 if (mSmallIcon != null) {
1739 parcel.writeInt(1);
1740 mSmallIcon.writeToParcel(parcel, 0);
1741 } else {
1742 parcel.writeInt(0);
1743 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001744 parcel.writeInt(number);
1745 if (contentIntent != null) {
1746 parcel.writeInt(1);
1747 contentIntent.writeToParcel(parcel, 0);
1748 } else {
1749 parcel.writeInt(0);
1750 }
1751 if (deleteIntent != null) {
1752 parcel.writeInt(1);
1753 deleteIntent.writeToParcel(parcel, 0);
1754 } else {
1755 parcel.writeInt(0);
1756 }
1757 if (tickerText != null) {
1758 parcel.writeInt(1);
1759 TextUtils.writeToParcel(tickerText, parcel, flags);
1760 } else {
1761 parcel.writeInt(0);
1762 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001763 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001764 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001765 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001766 } else {
1767 parcel.writeInt(0);
1768 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 if (contentView != null) {
1770 parcel.writeInt(1);
1771 contentView.writeToParcel(parcel, 0);
1772 } else {
1773 parcel.writeInt(0);
1774 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001775 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001776 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001777 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001778 } else {
1779 parcel.writeInt(0);
1780 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781
1782 parcel.writeInt(defaults);
1783 parcel.writeInt(this.flags);
1784
1785 if (sound != null) {
1786 parcel.writeInt(1);
1787 sound.writeToParcel(parcel, 0);
1788 } else {
1789 parcel.writeInt(0);
1790 }
1791 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001792
1793 if (audioAttributes != null) {
1794 parcel.writeInt(1);
1795 audioAttributes.writeToParcel(parcel, 0);
1796 } else {
1797 parcel.writeInt(0);
1798 }
1799
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001800 parcel.writeLongArray(vibrate);
1801 parcel.writeInt(ledARGB);
1802 parcel.writeInt(ledOnMS);
1803 parcel.writeInt(ledOffMS);
1804 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001805
1806 if (fullScreenIntent != null) {
1807 parcel.writeInt(1);
1808 fullScreenIntent.writeToParcel(parcel, 0);
1809 } else {
1810 parcel.writeInt(0);
1811 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001812
1813 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001814
John Spurlockfd7f1e02014-03-18 16:41:57 -04001815 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001816
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001817 parcel.writeString(mGroupKey);
1818
1819 parcel.writeString(mSortKey);
1820
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001821 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001822
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001823 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001824
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001825 if (bigContentView != null) {
1826 parcel.writeInt(1);
1827 bigContentView.writeToParcel(parcel, 0);
1828 } else {
1829 parcel.writeInt(0);
1830 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001831
Chris Wren8fd39ec2014-02-27 17:43:26 -05001832 if (headsUpContentView != null) {
1833 parcel.writeInt(1);
1834 headsUpContentView.writeToParcel(parcel, 0);
1835 } else {
1836 parcel.writeInt(0);
1837 }
1838
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001839 parcel.writeInt(visibility);
1840
1841 if (publicVersion != null) {
1842 parcel.writeInt(1);
1843 publicVersion.writeToParcel(parcel, 0);
1844 } else {
1845 parcel.writeInt(0);
1846 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001847
1848 parcel.writeInt(color);
Julia Reynolds74303cf2015-10-16 11:37:55 -04001849
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001850 if (topic != null) {
1851 parcel.writeInt(1);
1852 topic.writeToParcel(parcel, 0);
1853 } else {
1854 parcel.writeInt(0);
1855 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001856 }
1857
1858 /**
1859 * Parcelable.Creator that instantiates Notification objects
1860 */
1861 public static final Parcelable.Creator<Notification> CREATOR
1862 = new Parcelable.Creator<Notification>()
1863 {
1864 public Notification createFromParcel(Parcel parcel)
1865 {
1866 return new Notification(parcel);
1867 }
1868
1869 public Notification[] newArray(int size)
1870 {
1871 return new Notification[size];
1872 }
1873 };
1874
1875 /**
1876 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1877 * layout.
1878 *
1879 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1880 * in the view.</p>
1881 * @param context The context for your application / activity.
1882 * @param contentTitle The title that goes in the expanded entry.
1883 * @param contentText The text that goes in the expanded entry.
1884 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1885 * If this is an activity, it must include the
1886 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001887 * that you take care of task management as described in the
1888 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1889 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001890 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001891 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001892 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001893 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001894 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001895 public void setLatestEventInfo(Context context,
1896 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001897 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1898 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1899 new Throwable());
1900 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001901
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001902 // ensure that any information already set directly is preserved
1903 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001904
1905 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001907 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001908 }
1909 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001910 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001911 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001912 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001913
1914 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001915 }
1916
Julia Reynoldsda303542015-11-23 14:00:20 -05001917 /**
1918 * @hide
1919 */
1920 public static void addFieldsFromContext(Context context, Notification notification) {
1921 if (notification.extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO) == null) {
1922 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
1923 context.getApplicationInfo());
1924 }
1925 if (!notification.extras.containsKey(EXTRA_ORIGINATING_USERID)) {
1926 notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
1927 }
1928 }
1929
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 @Override
1931 public String toString() {
1932 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001933 sb.append("Notification(pri=");
1934 sb.append(priority);
1935 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08001936 if (contentView != null) {
1937 sb.append(contentView.getPackage());
1938 sb.append("/0x");
1939 sb.append(Integer.toHexString(contentView.getLayoutId()));
1940 } else {
1941 sb.append("null");
1942 }
1943 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001944 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1945 sb.append("default");
1946 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001947 int N = this.vibrate.length-1;
1948 sb.append("[");
1949 for (int i=0; i<N; i++) {
1950 sb.append(this.vibrate[i]);
1951 sb.append(',');
1952 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02001953 if (N != -1) {
1954 sb.append(this.vibrate[N]);
1955 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001956 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 } else {
1958 sb.append("null");
1959 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001960 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001961 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001962 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001963 } else if (this.sound != null) {
1964 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001965 } else {
1966 sb.append("null");
1967 }
Chris Wren365b6d32015-07-16 10:39:26 -04001968 if (this.tickerText != null) {
1969 sb.append(" tick");
1970 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001971 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001972 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001973 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001974 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04001975 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001976 if (this.category != null) {
1977 sb.append(" category=");
1978 sb.append(this.category);
1979 }
1980 if (this.mGroupKey != null) {
1981 sb.append(" groupKey=");
1982 sb.append(this.mGroupKey);
1983 }
1984 if (this.mSortKey != null) {
1985 sb.append(" sortKey=");
1986 sb.append(this.mSortKey);
1987 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001988 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04001989 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001990 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04001991 }
1992 sb.append(" vis=");
1993 sb.append(visibilityToString(this.visibility));
1994 if (this.publicVersion != null) {
1995 sb.append(" publicVersion=");
1996 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001997 }
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001998 if (topic != null) {
1999 sb.append("topic=");
2000 sb.append(topic.toString());
Julia Reynolds74303cf2015-10-16 11:37:55 -04002001 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002002 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002003 return sb.toString();
2004 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002005
Dan Sandler1b718782014-07-18 12:43:45 -04002006 /**
2007 * {@hide}
2008 */
2009 public static String visibilityToString(int vis) {
2010 switch (vis) {
2011 case VISIBILITY_PRIVATE:
2012 return "PRIVATE";
2013 case VISIBILITY_PUBLIC:
2014 return "PUBLIC";
2015 case VISIBILITY_SECRET:
2016 return "SECRET";
2017 default:
2018 return "UNKNOWN(" + String.valueOf(vis) + ")";
2019 }
2020 }
2021
Joe Onoratocb109a02011-01-18 17:57:41 -08002022 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002023 * {@hide}
2024 */
2025 public static String priorityToString(@Priority int pri) {
2026 switch (pri) {
2027 case PRIORITY_MIN:
2028 return "MIN";
2029 case PRIORITY_LOW:
2030 return "LOW";
2031 case PRIORITY_DEFAULT:
2032 return "DEFAULT";
2033 case PRIORITY_HIGH:
2034 return "HIGH";
2035 case PRIORITY_MAX:
2036 return "MAX";
2037 default:
2038 return "UNKNOWN(" + String.valueOf(pri) + ")";
2039 }
2040 }
2041
2042 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002043 * The small icon representing this notification in the status bar and content view.
2044 *
2045 * @return the small icon representing this notification.
2046 *
2047 * @see Builder#getSmallIcon()
2048 * @see Builder#setSmallIcon(Icon)
2049 */
2050 public Icon getSmallIcon() {
2051 return mSmallIcon;
2052 }
2053
2054 /**
2055 * Used when notifying to clean up legacy small icons.
2056 * @hide
2057 */
2058 public void setSmallIcon(Icon icon) {
2059 mSmallIcon = icon;
2060 }
2061
2062 /**
2063 * The large icon shown in this notification's content view.
2064 * @see Builder#getLargeIcon()
2065 * @see Builder#setLargeIcon(Icon)
2066 */
2067 public Icon getLargeIcon() {
2068 return mLargeIcon;
2069 }
2070
2071 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002072 * @hide
2073 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002074 public boolean isGroupSummary() {
2075 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2076 }
2077
2078 /**
2079 * @hide
2080 */
2081 public boolean isGroupChild() {
2082 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2083 }
2084
2085 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002086 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002087 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002088 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002089 * content views using the platform's notification layout template. If your app supports
2090 * versions of Android as old as API level 4, you can instead use
2091 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2092 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2093 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002094 *
Scott Main183bf112012-08-13 19:12:13 -07002095 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002096 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002097 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002098 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002099 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2100 * .setContentText(subject)
2101 * .setSmallIcon(R.drawable.new_mail)
2102 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002103 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002104 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002105 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002106 public static class Builder {
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002107 private static final int MAX_ACTION_BUTTONS = 3;
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002108 private static final float LARGE_TEXT_SCALE = 1.3f;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002109
Joe Onorato46439ce2010-11-19 13:56:21 -08002110 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002111 private Notification mN;
2112 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002113 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002114 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2115 private ArrayList<String> mPersonList = new ArrayList<String>();
2116 private NotificationColorUtil mColorUtil;
2117 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002118
2119 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002120 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002121 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002122
2123 * <table>
2124 * <tr><th align=right>priority</th>
2125 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2126 * <tr><th align=right>when</th>
2127 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2128 * <tr><th align=right>audio stream</th>
2129 * <td>{@link #STREAM_DEFAULT}</td></tr>
2130 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002131 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002132
2133 * @param context
2134 * A {@link Context} that will be used by the Builder to construct the
2135 * RemoteViews. The Context will not be held past the lifetime of this Builder
2136 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002137 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002138 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002139 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002140 }
2141
Joe Onoratocb109a02011-01-18 17:57:41 -08002142 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002143 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002144 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002145 public Builder(Context context, Notification toAdopt) {
2146 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002147
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002148 if (toAdopt == null) {
2149 mN = new Notification();
2150 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2151 mN.priority = PRIORITY_DEFAULT;
2152 mN.visibility = VISIBILITY_PRIVATE;
2153 } else {
2154 mN = toAdopt;
2155 if (mN.actions != null) {
2156 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002157 }
2158
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002159 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2160 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2161 }
2162
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002163 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2164 if (!TextUtils.isEmpty(templateClass)) {
2165 final Class<? extends Style> styleClass
2166 = getNotificationStyleClass(templateClass);
2167 if (styleClass == null) {
2168 Log.d(TAG, "Unknown style class: " + templateClass);
2169 } else {
2170 try {
2171 final Constructor<? extends Style> ctor = styleClass.getConstructor();
2172 ctor.setAccessible(true);
2173 final Style style = ctor.newInstance();
2174 style.restoreFromExtras(mN.extras);
2175
2176 if (style != null) {
2177 setStyle(style);
2178 }
2179 } catch (Throwable t) {
2180 Log.e(TAG, "Could not create Style", t);
2181 }
2182 }
2183 }
2184
2185 }
2186 }
2187
2188 private NotificationColorUtil getColorUtil() {
2189 if (!mColorUtilInited) {
2190 mColorUtilInited = true;
2191 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2192 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002193 }
2194 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002195 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002196 }
2197
2198 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002199 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Daniel Sandler0c890492012-09-12 17:23:10 -07002200 * It will be shown in the notification content view by default; use
Griff Hazen50c11652014-05-16 09:46:31 -07002201 * {@link #setShowWhen(boolean) setShowWhen} to control this.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002202 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002203 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002204 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002205 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002206 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002207 return this;
2208 }
2209
Joe Onoratocb109a02011-01-18 17:57:41 -08002210 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002211 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002212 * in the content view.
2213 */
2214 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002215 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002216 return this;
2217 }
2218
2219 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002220 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002221 *
2222 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002223 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002224 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002225 * Useful when showing an elapsed time (like an ongoing phone call).
2226 *
2227 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002228 * @see Notification#when
2229 */
2230 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002231 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002232 return this;
2233 }
2234
2235 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002236 * Set the small icon resource, which will be used to represent the notification in the
2237 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002238 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002239
2240 * The platform template for the expanded view will draw this icon in the left, unless a
2241 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2242 * icon will be moved to the right-hand side.
2243 *
2244
2245 * @param icon
2246 * A resource ID in the application's package of the drawable to use.
2247 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002248 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002249 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002250 return setSmallIcon(icon != 0
2251 ? Icon.createWithResource(mContext, icon)
2252 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002253 }
2254
Joe Onoratocb109a02011-01-18 17:57:41 -08002255 /**
2256 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2257 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2258 * LevelListDrawable}.
2259 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002260 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002261 * @param level The level to use for the icon.
2262 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002263 * @see Notification#icon
2264 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002265 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002266 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002267 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002268 return setSmallIcon(icon);
2269 }
2270
2271 /**
2272 * Set the small icon, which will be used to represent the notification in the
2273 * status bar and content view (unless overriden there by a
2274 * {@link #setLargeIcon(Bitmap) large icon}).
2275 *
2276 * @param icon An Icon object to use.
2277 * @see Notification#icon
2278 */
2279 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002280 mN.setSmallIcon(icon);
2281 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2282 mN.icon = icon.getResId();
2283 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002284 return this;
2285 }
2286
Joe Onoratocb109a02011-01-18 17:57:41 -08002287 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002288 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002289 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002290 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002291 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002292 return this;
2293 }
2294
Joe Onoratocb109a02011-01-18 17:57:41 -08002295 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002296 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002297 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002298 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002299 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002300 return this;
2301 }
2302
Joe Onoratocb109a02011-01-18 17:57:41 -08002303 /**
Joe Malin8d40d042012-11-05 11:36:40 -08002304 * Set the third line of text in the platform notification template.
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002305 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2306 * same location in the standard template.
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002307 */
2308 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002309 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002310 return this;
2311 }
2312
2313 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002314 * Set the large number at the right-hand side of the notification. This is
2315 * equivalent to setContentInfo, although it might show the number in a different
2316 * font size for readability.
2317 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002318 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002319 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002320 return this;
2321 }
2322
Joe Onoratocb109a02011-01-18 17:57:41 -08002323 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002324 * A small piece of additional information pertaining to this notification.
2325 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002326 * The platform template will draw this on the last line of the notification, at the far
2327 * right (to the right of a smallIcon if it has been placed there).
Joe Onoratocb109a02011-01-18 17:57:41 -08002328 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002329 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002330 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002331 return this;
2332 }
2333
Joe Onoratocb109a02011-01-18 17:57:41 -08002334 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002335 * Set the progress this notification represents.
2336 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002337 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002338 */
2339 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002340 mN.extras.putInt(EXTRA_PROGRESS, progress);
2341 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2342 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002343 return this;
2344 }
2345
2346 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002347 * Supply a custom RemoteViews to use instead of the platform template.
2348 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002349 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002350 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002351 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002352 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002353 return setCustomContentView(views);
2354 }
2355
2356 /**
2357 * Supply custom RemoteViews to use instead of the platform template.
2358 *
2359 * This will override the layout that would otherwise be constructed by this Builder
2360 * object.
2361 */
2362 public Builder setCustomContentView(RemoteViews contentView) {
2363 mN.contentView = contentView;
2364 return this;
2365 }
2366
2367 /**
2368 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2369 *
2370 * This will override the expanded layout that would otherwise be constructed by this
2371 * Builder object.
2372 */
2373 public Builder setCustomBigContentView(RemoteViews contentView) {
2374 mN.bigContentView = contentView;
2375 return this;
2376 }
2377
2378 /**
2379 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2380 *
2381 * This will override the heads-up layout that would otherwise be constructed by this
2382 * Builder object.
2383 */
2384 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2385 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002386 return this;
2387 }
2388
Joe Onoratocb109a02011-01-18 17:57:41 -08002389 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002390 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2391 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002392 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2393 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2394 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002395 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002396 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002397 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002398 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002399 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002400 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002401 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002402 return this;
2403 }
2404
Joe Onoratocb109a02011-01-18 17:57:41 -08002405 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002406 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2407 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002408 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002409 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002410 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002411 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002412 return this;
2413 }
2414
Joe Onoratocb109a02011-01-18 17:57:41 -08002415 /**
2416 * An intent to launch instead of posting the notification to the status bar.
2417 * Only for use with extremely high-priority notifications demanding the user's
2418 * <strong>immediate</strong> attention, such as an incoming phone call or
2419 * alarm clock that the user has explicitly set to a particular time.
2420 * If this facility is used for something else, please give the user an option
2421 * to turn it off and use a normal notification, as this can be extremely
2422 * disruptive.
2423 *
Chris Wren47c20a12014-06-18 17:27:29 -04002424 * <p>
2425 * The system UI may choose to display a heads-up notification, instead of
2426 * launching this intent, while the user is using the device.
2427 * </p>
2428 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002429 * @param intent The pending intent to launch.
2430 * @param highPriority Passing true will cause this notification to be sent
2431 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002432 *
2433 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002434 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002435 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002436 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002437 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2438 return this;
2439 }
2440
Joe Onoratocb109a02011-01-18 17:57:41 -08002441 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002442 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002443 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002444 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002445 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002446 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002447 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002448 return this;
2449 }
2450
Joe Onoratocb109a02011-01-18 17:57:41 -08002451 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002452 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002453 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002454 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002455 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002456 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002457 setTicker(tickerText);
2458 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002459 return this;
2460 }
2461
Joe Onoratocb109a02011-01-18 17:57:41 -08002462 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002463 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002464 *
2465 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002466 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2467 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002468 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002469 public Builder setLargeIcon(Bitmap b) {
2470 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2471 }
2472
2473 /**
2474 * Add a large icon to the notification content view.
2475 *
2476 * In the platform template, this image will be shown on the left of the notification view
2477 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2478 * badge atop the large icon).
2479 */
2480 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002481 mN.mLargeIcon = icon;
2482 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002483 return this;
2484 }
2485
Joe Onoratocb109a02011-01-18 17:57:41 -08002486 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002487 * Set the sound to play.
2488 *
John Spurlockc0650f022014-07-19 13:22:39 -04002489 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2490 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002491 *
Chris Wren47c20a12014-06-18 17:27:29 -04002492 * <p>
2493 * A notification that is noisy is more likely to be presented as a heads-up notification.
2494 * </p>
2495 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002496 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002497 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002498 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002499 mN.sound = sound;
2500 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002501 return this;
2502 }
2503
Joe Onoratocb109a02011-01-18 17:57:41 -08002504 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002505 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002506 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002507 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2508 *
Chris Wren47c20a12014-06-18 17:27:29 -04002509 * <p>
2510 * A notification that is noisy is more likely to be presented as a heads-up notification.
2511 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002512 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002513 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002514 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002515 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002516 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002517 mN.sound = sound;
2518 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002519 return this;
2520 }
2521
Joe Onoratocb109a02011-01-18 17:57:41 -08002522 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002523 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2524 * use during playback.
2525 *
2526 * <p>
2527 * A notification that is noisy is more likely to be presented as a heads-up notification.
2528 * </p>
2529 *
2530 * @see Notification#sound
2531 */
2532 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002533 mN.sound = sound;
2534 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002535 return this;
2536 }
2537
2538 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002539 * Set the vibration pattern to use.
2540 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002541 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2542 * <code>pattern</code> parameter.
2543 *
Chris Wren47c20a12014-06-18 17:27:29 -04002544 * <p>
2545 * A notification that vibrates is more likely to be presented as a heads-up notification.
2546 * </p>
2547 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002548 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002549 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002550 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002551 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002552 return this;
2553 }
2554
Joe Onoratocb109a02011-01-18 17:57:41 -08002555 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002556 * Set the desired color for the indicator LED on the device, as well as the
2557 * blink duty cycle (specified in milliseconds).
2558 *
2559
2560 * Not all devices will honor all (or even any) of these values.
2561 *
2562
2563 * @see Notification#ledARGB
2564 * @see Notification#ledOnMS
2565 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002566 */
Tor Norbye80756e32015-03-02 09:39:27 -08002567 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002568 mN.ledARGB = argb;
2569 mN.ledOnMS = onMs;
2570 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002571 if (onMs != 0 || offMs != 0) {
2572 mN.flags |= FLAG_SHOW_LIGHTS;
2573 }
2574 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
2575 mN.flags |= FLAG_SHOW_LIGHTS;
2576 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002577 return this;
2578 }
2579
Joe Onoratocb109a02011-01-18 17:57:41 -08002580 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002581 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002582 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002583
2584 * Ongoing notifications cannot be dismissed by the user, so your application or service
2585 * must take care of canceling them.
2586 *
2587
2588 * They are typically used to indicate a background task that the user is actively engaged
2589 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2590 * (e.g., a file download, sync operation, active network connection).
2591 *
2592
2593 * @see Notification#FLAG_ONGOING_EVENT
2594 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002595 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002596 public Builder setOngoing(boolean ongoing) {
2597 setFlag(FLAG_ONGOING_EVENT, ongoing);
2598 return this;
2599 }
2600
Joe Onoratocb109a02011-01-18 17:57:41 -08002601 /**
2602 * Set this flag if you would only like the sound, vibrate
2603 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002604 *
2605 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002606 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002607 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2608 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2609 return this;
2610 }
2611
Joe Onoratocb109a02011-01-18 17:57:41 -08002612 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002613 * Make this notification automatically dismissed when the user touches it. The
2614 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2615 *
2616 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002617 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002618 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002619 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002620 return this;
2621 }
2622
Joe Onoratocb109a02011-01-18 17:57:41 -08002623 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002624 * Set whether or not this notification should not bridge to other devices.
2625 *
2626 * <p>Some notifications can be bridged to other devices for remote display.
2627 * This hint can be set to recommend this notification not be bridged.
2628 */
2629 public Builder setLocalOnly(boolean localOnly) {
2630 setFlag(FLAG_LOCAL_ONLY, localOnly);
2631 return this;
2632 }
2633
2634 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002635 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002636 * <p>
2637 * The value should be one or more of the following fields combined with
2638 * bitwise-or:
2639 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2640 * <p>
2641 * For all default values, use {@link #DEFAULT_ALL}.
2642 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002643 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002644 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002645 return this;
2646 }
2647
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002648 /**
2649 * Set the priority of this notification.
2650 *
2651 * @see Notification#priority
2652 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002653 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002654 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002655 return this;
2656 }
Joe Malin8d40d042012-11-05 11:36:40 -08002657
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002658 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002659 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002660 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002661 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002662 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002663 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002664 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002665 return this;
2666 }
2667
2668 /**
Chris Wrendde75302014-03-26 17:24:15 -04002669 * Add a person that is relevant to this notification.
2670 *
Chris Wrene6c48932014-09-29 17:19:27 -04002671 * <P>
2672 * Depending on user preferences, this annotation may allow the notification to pass
2673 * through interruption filters, and to appear more prominently in the user interface.
2674 * </P>
2675 *
2676 * <P>
2677 * The person should be specified by the {@code String} representation of a
2678 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2679 * </P>
2680 *
2681 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2682 * URIs. The path part of these URIs must exist in the contacts database, in the
2683 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2684 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2685 * </P>
2686 *
2687 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002688 * @see Notification#EXTRA_PEOPLE
2689 */
Chris Wrene6c48932014-09-29 17:19:27 -04002690 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002691 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002692 return this;
2693 }
2694
2695 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002696 * Set this notification to be part of a group of notifications sharing the same key.
2697 * Grouped notifications may display in a cluster or stack on devices which
2698 * support such rendering.
2699 *
2700 * <p>To make this notification the summary for its group, also call
2701 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2702 * {@link #setSortKey}.
2703 * @param groupKey The group key of the group.
2704 * @return this object for method chaining
2705 */
2706 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002707 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002708 return this;
2709 }
2710
2711 /**
2712 * Set this notification to be the group summary for a group of notifications.
2713 * Grouped notifications may display in a cluster or stack on devices which
2714 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2715 * @param isGroupSummary Whether this notification should be a group summary.
2716 * @return this object for method chaining
2717 */
2718 public Builder setGroupSummary(boolean isGroupSummary) {
2719 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2720 return this;
2721 }
2722
2723 /**
2724 * Set a sort key that orders this notification among other notifications from the
2725 * same package. This can be useful if an external sort was already applied and an app
2726 * would like to preserve this. Notifications will be sorted lexicographically using this
2727 * value, although providing different priorities in addition to providing sort key may
2728 * cause this value to be ignored.
2729 *
2730 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002731 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002732 *
2733 * @see String#compareTo(String)
2734 */
2735 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002736 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002737 return this;
2738 }
2739
2740 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002741 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002742 *
Griff Hazen720042b2014-02-24 15:46:56 -08002743 * <p>Values within the Bundle will replace existing extras values in this Builder.
2744 *
2745 * @see Notification#extras
2746 */
Griff Hazen959591e2014-05-15 22:26:18 -07002747 public Builder addExtras(Bundle extras) {
2748 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002749 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002750 }
2751 return this;
2752 }
2753
2754 /**
2755 * Set metadata for this notification.
2756 *
2757 * <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 -04002758 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002759 * called.
2760 *
Griff Hazen720042b2014-02-24 15:46:56 -08002761 * <p>Replaces any existing extras values with those from the provided Bundle.
2762 * Use {@link #addExtras} to merge in metadata instead.
2763 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002764 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002765 */
Griff Hazen959591e2014-05-15 22:26:18 -07002766 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002767 if (extras != null) {
2768 mUserExtras = extras;
2769 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002770 return this;
2771 }
2772
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002773 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002774 * Get the current metadata Bundle used by this notification Builder.
2775 *
2776 * <p>The returned Bundle is shared with this Builder.
2777 *
2778 * <p>The current contents of this Bundle are copied into the Notification each time
2779 * {@link #build()} is called.
2780 *
2781 * @see Notification#extras
2782 */
2783 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002784 return mUserExtras;
2785 }
2786
2787 private Bundle getAllExtras() {
2788 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2789 saveExtras.putAll(mN.extras);
2790 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002791 }
2792
2793 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002794 * Add an action to this notification. Actions are typically displayed by
2795 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002796 * <p>
2797 * Every action must have an icon (32dp square and matching the
2798 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2799 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2800 * <p>
2801 * A notification in its expanded form can display up to 3 actions, from left to right in
2802 * the order they were added. Actions will not be displayed when the notification is
2803 * collapsed, however, so be sure that any essential functions may be accessed by the user
2804 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002805 *
2806 * @param icon Resource ID of a drawable that represents the action.
2807 * @param title Text describing the action.
2808 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002809 *
2810 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002811 */
Dan Sandler86647982015-05-13 23:41:13 -04002812 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002813 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002814 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002815 return this;
2816 }
2817
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002818 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002819 * Add an action to this notification. Actions are typically displayed by
2820 * the system as a button adjacent to the notification content.
2821 * <p>
2822 * Every action must have an icon (32dp square and matching the
2823 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2824 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2825 * <p>
2826 * A notification in its expanded form can display up to 3 actions, from left to right in
2827 * the order they were added. Actions will not be displayed when the notification is
2828 * collapsed, however, so be sure that any essential functions may be accessed by the user
2829 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2830 *
2831 * @param action The action to add.
2832 */
2833 public Builder addAction(Action action) {
2834 mActions.add(action);
2835 return this;
2836 }
2837
2838 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002839 * Alter the complete list of actions attached to this notification.
2840 * @see #addAction(Action).
2841 *
2842 * @param actions
2843 * @return
2844 */
2845 public Builder setActions(Action... actions) {
2846 mActions.clear();
2847 for (int i = 0; i < actions.length; i++) {
2848 mActions.add(actions[i]);
2849 }
2850 return this;
2851 }
2852
2853 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002854 * Add a rich notification style to be applied at build time.
2855 *
2856 * @param style Object responsible for modifying the notification style.
2857 */
2858 public Builder setStyle(Style style) {
2859 if (mStyle != style) {
2860 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002861 if (mStyle != null) {
2862 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002863 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
2864 } else {
2865 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002866 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002867 }
2868 return this;
2869 }
2870
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002871 /**
2872 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07002873 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002874 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2875 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2876 *
2877 * @return The same Builder.
2878 */
2879 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002880 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002881 return this;
2882 }
2883
2884 /**
2885 * Supply a replacement Notification whose contents should be shown in insecure contexts
2886 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2887 * @param n A replacement notification, presumably with some or all info redacted.
2888 * @return The same Builder.
2889 */
2890 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002891 if (n != null) {
2892 mN.publicVersion = new Notification();
2893 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
2894 } else {
2895 mN.publicVersion = null;
2896 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002897 return this;
2898 }
2899
Griff Hazenb720abe2014-05-20 13:15:30 -07002900 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002901 * Apply an extender to this notification builder. Extenders may be used to add
2902 * metadata or change options on this builder.
2903 */
Griff Hazen61a9e862014-05-22 16:05:19 -07002904 public Builder extend(Extender extender) {
2905 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002906 return this;
2907 }
2908
Dan Sandler4e787062015-06-17 15:09:48 -04002909 /**
2910 * @hide
2911 */
2912 public void setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002913 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002914 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002915 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002916 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002917 }
2918 }
2919
Dan Sandler26e81cf2014-05-06 10:01:27 -04002920 /**
2921 * Sets {@link Notification#color}.
2922 *
2923 * @param argb The accent color to use
2924 *
2925 * @return The same Builder.
2926 */
Tor Norbye80756e32015-03-02 09:39:27 -08002927 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002928 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002929 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04002930 return this;
2931 }
2932
Julia Reynolds74303cf2015-10-16 11:37:55 -04002933 /**
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002934 * Sets the topic of this notification. Topics are typically displayed in Notification
Julia Reynolds74303cf2015-10-16 11:37:55 -04002935 * settings.
2936 * <p>
2937 * Every topic must have an id and a textual label.
2938 *
2939 * @param topic The topic to add.
2940 */
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002941 public Builder setTopic(Topic topic) {
2942 mN.topic = topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04002943 return this;
2944 }
2945
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002946 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02002947 // Note: This assumes that the current user can read the profile badge of the
2948 // originating user.
Svetoslavc7d62f02014-09-04 15:39:54 -07002949 return mContext.getPackageManager().getUserBadgeForDensity(
Julia Reynoldsda303542015-11-23 14:00:20 -05002950 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002951 }
2952
2953 private Bitmap getProfileBadge() {
2954 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01002955 if (badge == null) {
2956 return null;
2957 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002958 final int size = mContext.getResources().getDimensionPixelSize(
2959 R.dimen.notification_badge_size);
2960 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002961 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002962 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002963 badge.draw(canvas);
2964 return bitmap;
2965 }
2966
Kenny Guy98193ea2014-07-24 19:54:37 +01002967 private boolean addProfileBadge(RemoteViews contentView, int resId) {
2968 Bitmap profileBadge = getProfileBadge();
2969
2970 contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
Kenny Guy98193ea2014-07-24 19:54:37 +01002971 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2972
2973 if (profileBadge != null) {
2974 contentView.setImageViewBitmap(resId, profileBadge);
2975 contentView.setViewVisibility(resId, View.VISIBLE);
2976
2977 // Make sure Line 3 is visible. As badge will be here if there
2978 // is no text to display.
2979 if (resId == R.id.profile_badge_line3) {
2980 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2981 }
2982 return true;
2983 }
2984 return false;
2985 }
2986
Christoph Studerfe718432014-09-01 18:21:18 +02002987 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002988 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002989 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02002990 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02002991 contentView.setTextViewText(R.id.title, null);
2992 contentView.setTextViewText(R.id.text, null);
Christoph Studerfe718432014-09-01 18:21:18 +02002993 contentView.setViewVisibility(R.id.line3, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08002994 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02002995 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002996 }
2997
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002998 /**
2999 * Resets the notification header to its original state
3000 */
3001 private void resetNotificationHeader(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003002 contentView.setImageViewResource(R.id.icon, 0);
3003 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003004 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003005 contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003006 contentView.setViewVisibility(R.id.header_content_info, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003007 contentView.setViewVisibility(R.id.number_of_children, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003008 contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
3009 contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
3010 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003011 }
3012
3013 private void resetContentMargins(RemoteViews contentView) {
3014 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003015 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003016 }
3017
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003018 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003019 return applyStandardTemplate(resId, true /* hasProgress */);
3020 }
3021
3022 /**
3023 * @param hasProgress whether the progress bar should be shown and set
3024 */
3025 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003026 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003027
Christoph Studerfe718432014-09-01 18:21:18 +02003028 resetStandardTemplate(contentView);
3029
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003030 boolean showLine3 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003031 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003032
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003033 bindNotificationHeader(contentView);
3034 bindLargeIcon(contentView);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003035 if (ex.getCharSequence(EXTRA_TITLE) != null) {
3036 contentView.setTextViewText(R.id.title,
3037 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003038 }
Selim Cinek29603462015-11-17 19:04:39 -08003039 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003040 if (ex.getCharSequence(EXTRA_TEXT) != null) {
Selim Cinek29603462015-11-17 19:04:39 -08003041 contentView.setTextViewText(showProgress ? R.id.text_line_1 : R.id.text,
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003042 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Selim Cinek29603462015-11-17 19:04:39 -08003043 if (showProgress) {
3044 contentView.setViewVisibility(R.id.text_line_1, View.VISIBLE);
Joe Onorato059a2f82011-01-04 10:27:01 -08003045 }
Selim Cinek29603462015-11-17 19:04:39 -08003046 showLine3 = !showProgress;
Joe Onorato561d3852010-11-20 18:09:34 -08003047 }
Selim Cinek29603462015-11-17 19:04:39 -08003048 // We want to add badge to first line of text.
3049 if (addProfileBadge(contentView, R.id.profile_badge_line3)) {
3050 showLine3 = true;
3051 }
3052 // Note getStandardView may hide line 3 again.
3053 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003054
Selim Cinek29603462015-11-17 19:04:39 -08003055 return contentView;
3056 }
3057
3058 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003059 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3060 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3061 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3062 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003063 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003064 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003065 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003066 contentView.setProgressBackgroundTintList(
3067 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3068 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003069 if (mN.color != COLOR_DEFAULT) {
3070 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003071 contentView.setProgressTintList(R.id.progress, colorStateList);
3072 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003073 }
Selim Cinek29603462015-11-17 19:04:39 -08003074 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003075 } else {
3076 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003077 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003078 }
Joe Onorato561d3852010-11-20 18:09:34 -08003079 }
3080
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003081 private void bindLargeIcon(RemoteViews contentView) {
3082 if (mN.mLargeIcon != null) {
3083 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3084 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3085 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3086 int endMargin = mContext.getResources().getDimensionPixelSize(
3087 R.dimen.notification_content_picture_margin);
3088 contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003089 contentView.setViewLayoutMarginEnd(R.id.line3, endMargin);
Selim Cinek29603462015-11-17 19:04:39 -08003090 contentView.setViewLayoutMarginEnd(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003091 }
3092 }
3093
3094 private void bindNotificationHeader(RemoteViews contentView) {
3095 bindSmallIcon(contentView);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003096 bindChildCountColor(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003097 bindHeaderAppName(contentView);
Selim Cinek03d0d652015-11-13 13:18:09 -05003098 bindHeaderSubText(contentView);
Selim Cinek29603462015-11-17 19:04:39 -08003099 bindContentInfo(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003100 bindHeaderChronometerAndTime(contentView);
3101 bindExpandButton(contentView);
3102 }
3103
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003104 private void bindChildCountColor(RemoteViews contentView) {
3105 contentView.setTextColor(R.id.number_of_children, resolveColor());
3106 }
3107
Selim Cinek29603462015-11-17 19:04:39 -08003108 private void bindContentInfo(RemoteViews contentView) {
3109 boolean visible = false;
3110 if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3111 contentView.setTextViewText(R.id.header_content_info,
3112 processLegacyText(mN.extras.getCharSequence(EXTRA_INFO_TEXT)));
3113 contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
3114 visible = true;
3115 } else if (mN.number > 0) {
3116 final int tooBig = mContext.getResources().getInteger(
3117 R.integer.status_bar_notification_info_maxnum);
3118 if (mN.number > tooBig) {
3119 contentView.setTextViewText(R.id.header_content_info, processLegacyText(
3120 mContext.getResources().getString(
3121 R.string.status_bar_notification_info_overflow)));
3122 } else {
3123 contentView.setTextViewText(R.id.header_content_info,
3124 processLegacyText(String.valueOf(mN.number)));
3125 }
3126 contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
3127 visible = true;
3128 }
3129 if (visible) {
3130 contentView.setViewVisibility(R.id.content_info_divider, View.VISIBLE);
3131 }
3132 }
3133
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003134 private void bindExpandButton(RemoteViews contentView) {
3135 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
3136 PorterDuff.Mode.SRC_ATOP, -1);
3137 }
3138
3139 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3140 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003141 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003142 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3143 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3144 contentView.setLong(R.id.chronometer, "setBase",
3145 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3146 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3147 } else {
3148 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3149 contentView.setLong(R.id.time, "setTime", mN.when);
3150 }
3151 }
3152 }
3153
Selim Cinek03d0d652015-11-13 13:18:09 -05003154 private void bindHeaderSubText(RemoteViews contentView) {
3155 CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3156 if (subText == null && mStyle != null && mStyle.mSummaryTextSet
3157 && mStyle.hasSummaryInHeader()) {
3158 subText = mStyle.mSummaryText;
3159 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003160 if (subText != null) {
3161 // TODO: Remove the span entirely to only have the string with propper formating.
3162 contentView.setTextViewText(R.id.header_sub_text, processLegacyText(subText));
3163 contentView.setViewVisibility(R.id.header_sub_text, View.VISIBLE);
Selim Cinek29603462015-11-17 19:04:39 -08003164 contentView.setViewVisibility(R.id.sub_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003165 }
3166 }
3167
3168 private void bindHeaderAppName(RemoteViews contentView) {
3169 PackageManager packageManager = mContext.getPackageManager();
3170 ApplicationInfo info = null;
3171 try {
3172 info = packageManager.getApplicationInfo(mContext.getApplicationInfo().packageName,
3173 0);
3174 } catch (final NameNotFoundException e) {
3175 return;
3176 }
3177 CharSequence appName = info != null ? packageManager.getApplicationLabel(info)
3178 : null;
3179 if (TextUtils.isEmpty(appName)) {
3180 return;
3181 }
3182 contentView.setTextViewText(R.id.app_name_text, appName);
3183 }
3184
3185 private void bindSmallIcon(RemoteViews contentView) {
3186 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3187 processSmallIconColor(mN.mSmallIcon, contentView);
3188 }
3189
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003190 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003191 * @return true if the built notification will show the time or the chronometer; false
3192 * otherwise
3193 */
3194 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003195 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003196 }
3197
Christoph Studerfe718432014-09-01 18:21:18 +02003198 private void resetStandardTemplateWithActions(RemoteViews big) {
3199 big.setViewVisibility(R.id.actions, View.GONE);
3200 big.setViewVisibility(R.id.action_divider, View.GONE);
3201 big.removeAllViews(R.id.actions);
3202 }
3203
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003204 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003205 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003206
Christoph Studerfe718432014-09-01 18:21:18 +02003207 resetStandardTemplateWithActions(big);
3208
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003209 int N = mActions.size();
3210 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003211 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003212 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003213 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003214 for (int i=0; i<N; i++) {
3215 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003216 big.addView(R.id.actions, button);
3217 }
3218 }
3219 return big;
3220 }
3221
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003222 /**
3223 * Construct a RemoteViews for the final 1U notification layout. In order:
3224 * 1. Custom contentView from the caller
3225 * 2. Style's proposed content view
3226 * 3. Standard template view
3227 */
3228 public RemoteViews makeContentView() {
3229 if (mN.contentView != null) {
3230 return mN.contentView;
3231 } else if (mStyle != null) {
3232 final RemoteViews styleView = mStyle.makeContentView();
3233 if (styleView != null) {
3234 return styleView;
3235 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003236 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003237 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003238 }
3239
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003240 /**
3241 * Construct a RemoteViews for the final big notification layout.
3242 */
3243 public RemoteViews makeBigContentView() {
Selim Cinek850a8542015-11-11 11:48:36 -05003244 RemoteViews result = null;
Julia Reynolds089e3e42015-11-18 09:59:57 -05003245 if (mN.bigContentView != null) {
3246 return mN.bigContentView;
3247 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003248 result = mStyle.makeBigContentView();
Julia Reynolds089e3e42015-11-18 09:59:57 -05003249 } else if (mActions.size() == 0) {
3250 return null;
3251 }
Selim Cinek850a8542015-11-11 11:48:36 -05003252 if (result == null) {
3253 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003254 } else {
3255 hideLine1Text(result);
Selim Cinek850a8542015-11-11 11:48:36 -05003256 }
3257 adaptNotificationHeaderForBigContentView(result);
3258 return result;
3259 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003260
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003261 /**
3262 * Construct a RemoteViews for the final notification header only
3263 *
3264 * @hide
3265 */
3266 public RemoteViews makeNotificationHeader() {
3267 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3268 R.layout.notification_template_header);
3269 resetNotificationHeader(header);
3270 bindNotificationHeader(header);
3271 return header;
3272 }
3273
Selim Cinek29603462015-11-17 19:04:39 -08003274 private void hideLine1Text(RemoteViews result) {
3275 result.setViewVisibility(R.id.text_line_1, View.GONE);
3276 }
3277
Selim Cinek850a8542015-11-11 11:48:36 -05003278 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
3279 // We have to set the collapse button instead
3280 result.setImageViewResource(R.id.expand_button, R.drawable.ic_arrow_up_14dp);
3281 // Apply the color again
3282 result.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
3283 PorterDuff.Mode.SRC_ATOP, -1);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003284 }
3285
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003286 /**
3287 * Construct a RemoteViews for the final heads-up notification layout.
3288 */
3289 public RemoteViews makeHeadsUpContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003290 if (mN.headsUpContentView != null) {
3291 return mN.headsUpContentView;
3292 } else if (mStyle != null) {
3293 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3294 if (styleView != null) {
3295 return styleView;
3296 }
3297 } else if (mActions.size() == 0) {
3298 return null;
3299 }
3300
Chris Wren8fd39ec2014-02-27 17:43:26 -05003301
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003302 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003303 }
3304
3305
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003306 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003307 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003308 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003309 tombstone ? getActionTombstoneLayoutResource()
3310 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003311 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003312 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003313 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003314 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003315 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003316 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003317 if (action.mRemoteInputs != null) {
3318 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3319 }
3320 if (mN.color != COLOR_DEFAULT) {
3321 button.setTextColor(R.id.action0, mN.color);
3322 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003323 processLegacyAction(action, button);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003324 return button;
3325 }
3326
Joe Onoratocb109a02011-01-18 17:57:41 -08003327 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003328 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003329 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003330 */
3331 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003332 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003333 }
3334
3335 private void processLegacyAction(Action action, RemoteViews button) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003336 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
Christoph Studer239f8352014-08-25 15:13:18 +02003337 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
Alan Viverette4a357cd2015-03-18 18:37:18 -07003338 mContext.getColor(R.color.notification_action_color_filter),
Christoph Studer239f8352014-08-25 15:13:18 +02003339 PorterDuff.Mode.MULTIPLY);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003340 }
3341 }
3342
3343 private CharSequence processLegacyText(CharSequence charSequence) {
3344 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003345 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003346 } else {
3347 return charSequence;
3348 }
3349 }
3350
Dan Sandler26e81cf2014-05-06 10:01:27 -04003351 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003352 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003353 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003354 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
3355 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon)) {
3356 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003357 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003358 }
3359 }
3360
Dan Sandler26e81cf2014-05-06 10:01:27 -04003361 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003362 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003363 * if it's grayscale).
3364 */
3365 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003366 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3367 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003368 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003369 // resolve color will fall back to the default when legacy
3370 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003371 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003372 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003373 }
3374
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003375 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003376 if (mN.color != COLOR_DEFAULT) {
3377 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003378 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003379 }
3380
Selim Cinek5bf069a2015-11-10 19:14:27 -05003381 int resolveColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003382 if (mN.color == COLOR_DEFAULT) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003383 return mContext.getColor(R.color.notification_icon_default_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003384 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003385 return mN.color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003386 }
3387
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003388 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003389 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003390 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003391 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003392 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003393 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003394 mN.actions = new Action[mActions.size()];
3395 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003396 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003397 if (!mPersonList.isEmpty()) {
3398 mN.extras.putStringArray(EXTRA_PEOPLE,
3399 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003400 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003401 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003402 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003403
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003404 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003405 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003406 ApplicationInfo applicationInfo = n.extras.getParcelable(
3407 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003408 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003409 if (applicationInfo != null) {
3410 try {
3411 builderContext = context.createApplicationContext(applicationInfo,
3412 Context.CONTEXT_RESTRICTED);
3413 } catch (NameNotFoundException e) {
3414 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3415 builderContext = context; // try with our context
3416 }
3417 } else {
3418 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003419 }
3420
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003421 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003422 }
3423
3424 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3425 Class<? extends Style>[] classes = new Class[]{
3426 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3427 for (Class<? extends Style> innerClass : classes) {
3428 if (templateClass.equals(innerClass.getName())) {
3429 return innerClass;
3430 }
3431 }
3432 return null;
3433 }
3434
3435 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3436 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003437 }
3438
3439 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3440 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003441 }
3442
3443 private void setBuilderHeadsUpContentView(Notification n,
3444 RemoteViews headsUpContentView) {
3445 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003446 }
3447
3448 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003449 * @deprecated Use {@link #build()} instead.
3450 */
3451 @Deprecated
3452 public Notification getNotification() {
3453 return build();
3454 }
3455
3456 /**
3457 * Combine all of the options that have been set and return a new {@link Notification}
3458 * object.
3459 */
3460 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003461 // first, add any extras from the calling code
3462 if (mUserExtras != null) {
3463 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003464 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003465
3466 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003467 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003468
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003469 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003470
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003471 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003472 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003473 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003474
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003475 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003476 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003477
3478 /**
3479 * Apply this Builder to an existing {@link Notification} object.
3480 *
3481 * @hide
3482 */
3483 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003484 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003485 return n;
3486 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003487
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003488 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003489 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003490 }
3491
3492 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003493 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003494 }
3495
3496 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003497 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003498 }
3499
3500 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003501 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003502 }
3503
3504 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003505 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003506 }
3507
3508 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003509 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003510 }
3511
3512 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003513 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003514 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003515 }
3516
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003517 /**
3518 * An object that can apply a rich notification style to a {@link Notification.Builder}
3519 * object.
3520 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003521 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003522 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003523
3524 /**
3525 * @hide
3526 */
3527 protected CharSequence mSummaryText = null;
3528
3529 /**
3530 * @hide
3531 */
3532 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003533
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003534 protected Builder mBuilder;
3535
Chris Wrend6297db2012-05-03 16:20:13 -04003536 /**
3537 * Overrides ContentTitle in the big form of the template.
3538 * This defaults to the value passed to setContentTitle().
3539 */
3540 protected void internalSetBigContentTitle(CharSequence title) {
3541 mBigContentTitle = title;
3542 }
3543
3544 /**
3545 * Set the first line of text after the detail section in the big form of the template.
3546 */
3547 protected void internalSetSummaryText(CharSequence cs) {
3548 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003549 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003550 }
3551
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003552 public void setBuilder(Builder builder) {
3553 if (mBuilder != builder) {
3554 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003555 if (mBuilder != null) {
3556 mBuilder.setStyle(this);
3557 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003558 }
3559 }
3560
Chris Wrend6297db2012-05-03 16:20:13 -04003561 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003562 if (mBuilder == null) {
3563 throw new IllegalArgumentException("Style requires a valid Builder object");
3564 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003565 }
Chris Wrend6297db2012-05-03 16:20:13 -04003566
3567 protected RemoteViews getStandardView(int layoutId) {
3568 checkBuilder();
3569
Christoph Studer4600f9b2014-07-22 22:44:43 +02003570 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003571 CharSequence oldBuilderContentTitle =
3572 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003573 if (mBigContentTitle != null) {
3574 mBuilder.setContentTitle(mBigContentTitle);
3575 }
3576
Chris Wrend6297db2012-05-03 16:20:13 -04003577 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3578
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003579 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003580
Chris Wrend6297db2012-05-03 16:20:13 -04003581 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3582 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003583 } else {
3584 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003585 }
3586
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05003587 // Clear text in case we use the line to show the profile badge.
3588 contentView.setTextViewText(com.android.internal.R.id.text, "");
3589 contentView.setViewVisibility(com.android.internal.R.id.line3, View.GONE);
3590
Chris Wrend6297db2012-05-03 16:20:13 -04003591 return contentView;
3592 }
3593
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003594 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003595 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3596 * The default implementation has nothing additional to add.
3597 * @hide
3598 */
3599 public RemoteViews makeContentView() {
3600 return null;
3601 }
3602
3603 /**
3604 * Construct a Style-specific RemoteViews for the final big notification layout.
3605 * @hide
3606 */
3607 public RemoteViews makeBigContentView() {
3608 return null;
3609 }
3610
3611 /**
3612 * Construct a Style-specific RemoteViews for the final HUN layout.
3613 * @hide
3614 */
3615 public RemoteViews makeHeadsUpContentView() {
3616 return null;
3617 }
3618
3619 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003620 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003621 * @hide
3622 */
3623 public void addExtras(Bundle extras) {
3624 if (mSummaryTextSet) {
3625 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3626 }
3627 if (mBigContentTitle != null) {
3628 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3629 }
Chris Wren91ad5632013-06-05 15:05:57 -04003630 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003631 }
3632
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003633 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003634 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003635 * @hide
3636 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003637 protected void restoreFromExtras(Bundle extras) {
3638 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3639 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3640 mSummaryTextSet = true;
3641 }
3642 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3643 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3644 }
3645 }
3646
3647
3648 /**
3649 * @hide
3650 */
3651 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003652 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003653 return wip;
3654 }
3655
Daniel Sandler0ec46202015-06-24 01:27:05 -04003656 /**
3657 * @hide
3658 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003659 public void purgeResources() {}
3660
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003661 /**
3662 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3663 * attached to.
3664 *
3665 * @return the fully constructed Notification.
3666 */
3667 public Notification build() {
3668 checkBuilder();
3669 return mBuilder.build();
3670 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003671
3672 /**
3673 * @hide
3674 * @return true if the style positions the progress bar on the second line; false if the
3675 * style hides the progress bar
3676 */
3677 protected boolean hasProgress() {
3678 return true;
3679 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003680
3681 /**
3682 * @hide
3683 * @return Whether we should put the summary be put into the notification header
3684 */
3685 public boolean hasSummaryInHeader() {
3686 return true;
3687 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003688 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003689
3690 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003691 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003692 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003693 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003694 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003695 * Notification notif = new Notification.Builder(mContext)
3696 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3697 * .setContentText(subject)
3698 * .setSmallIcon(R.drawable.new_post)
3699 * .setLargeIcon(aBitmap)
3700 * .setStyle(new Notification.BigPictureStyle()
3701 * .bigPicture(aBigBitmap))
3702 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003703 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003704 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003705 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003706 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003707 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003708 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003709 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003710 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003711
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003712 public BigPictureStyle() {
3713 }
3714
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003715 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003716 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003717 }
3718
Chris Wrend6297db2012-05-03 16:20:13 -04003719 /**
3720 * Overrides ContentTitle in the big form of the template.
3721 * This defaults to the value passed to setContentTitle().
3722 */
3723 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003724 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003725 return this;
3726 }
3727
3728 /**
3729 * Set the first line of text after the detail section in the big form of the template.
3730 */
3731 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003732 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003733 return this;
3734 }
3735
Chris Wren0bd664d2012-08-01 13:56:56 -04003736 /**
3737 * Provide the bitmap to be used as the payload for the BigPicture notification.
3738 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003739 public BigPictureStyle bigPicture(Bitmap b) {
3740 mPicture = b;
3741 return this;
3742 }
3743
Chris Wren3745a3d2012-05-22 15:11:52 -04003744 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003745 * Override the large icon when the big notification is shown.
3746 */
3747 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003748 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3749 }
3750
3751 /**
3752 * Override the large icon when the big notification is shown.
3753 */
3754 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003755 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003756 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003757 return this;
3758 }
3759
Riley Andrews0394a0c2015-11-03 23:36:52 -08003760 /** @hide */
3761 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
3762
Daniel Sandler0ec46202015-06-24 01:27:05 -04003763 /**
3764 * @hide
3765 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003766 @Override
3767 public void purgeResources() {
3768 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08003769 if (mPicture != null &&
3770 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08003771 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003772 mPicture = mPicture.createAshmemBitmap();
3773 }
3774 if (mBigLargeIcon != null) {
3775 mBigLargeIcon.convertToAshmem();
3776 }
3777 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003778
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003779 /**
3780 * @hide
3781 */
3782 public RemoteViews makeBigContentView() {
3783 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003784 // This covers the following cases:
3785 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003786 // mN.mLargeIcon
3787 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003788 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003789 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003790 oldLargeIcon = mBuilder.mN.mLargeIcon;
3791 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003792 }
3793
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003794 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05003795 if (mSummaryTextSet) {
3796 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
3797 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3798 }
Selim Cinek53e64a42015-11-16 10:40:56 -08003799 int imageMinHeight = mBuilder.mContext.getResources().getDimensionPixelSize(
3800 R.dimen.notification_big_picture_content_min_height_with_picture);
3801 // We need to make space for the right image, so we're enforcing a minheight if there
3802 // is a picture.
3803 int minHeight = (mBuilder.mN.mLargeIcon == null) ? 0 : imageMinHeight;
3804 contentView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3805
Christoph Studer5c510ee2014-12-15 16:32:27 +01003806 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003807 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003808 }
3809
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003810 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3811
Selim Cinek29603462015-11-17 19:04:39 -08003812 mBuilder.addProfileBadge(contentView, R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003813 return contentView;
3814 }
3815
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003816 /**
3817 * @hide
3818 */
3819 public void addExtras(Bundle extras) {
3820 super.addExtras(extras);
3821
3822 if (mBigLargeIconSet) {
3823 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3824 }
3825 extras.putParcelable(EXTRA_PICTURE, mPicture);
3826 }
3827
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003828 /**
3829 * @hide
3830 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003831 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003832 protected void restoreFromExtras(Bundle extras) {
3833 super.restoreFromExtras(extras);
3834
3835 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003836 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003837 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003838 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003839 mPicture = extras.getParcelable(EXTRA_PICTURE);
3840 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003841
3842 /**
3843 * @hide
3844 */
3845 @Override
3846 public boolean hasSummaryInHeader() {
3847 return false;
3848 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003849 }
3850
3851 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003852 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003853 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003854 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003855 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003856 * Notification notif = new Notification.Builder(mContext)
3857 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3858 * .setContentText(subject)
3859 * .setSmallIcon(R.drawable.new_mail)
3860 * .setLargeIcon(aBitmap)
3861 * .setStyle(new Notification.BigTextStyle()
3862 * .bigText(aVeryLongString))
3863 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003864 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003865 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003866 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003867 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003868 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003869
3870 private static final int MAX_LINES = 13;
3871 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3872 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3873
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003874 private CharSequence mBigText;
3875
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003876 public BigTextStyle() {
3877 }
3878
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003879 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003880 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003881 }
3882
Chris Wrend6297db2012-05-03 16:20:13 -04003883 /**
3884 * Overrides ContentTitle in the big form of the template.
3885 * This defaults to the value passed to setContentTitle().
3886 */
3887 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003888 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003889 return this;
3890 }
3891
3892 /**
3893 * Set the first line of text after the detail section in the big form of the template.
3894 */
3895 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003896 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003897 return this;
3898 }
3899
Chris Wren0bd664d2012-08-01 13:56:56 -04003900 /**
3901 * Provide the longer text to be displayed in the big form of the
3902 * template in place of the content text.
3903 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003904 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003905 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003906 return this;
3907 }
3908
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003909 /**
3910 * @hide
3911 */
3912 public void addExtras(Bundle extras) {
3913 super.addExtras(extras);
3914
Christoph Studer4600f9b2014-07-22 22:44:43 +02003915 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3916 }
3917
3918 /**
3919 * @hide
3920 */
3921 @Override
3922 protected void restoreFromExtras(Bundle extras) {
3923 super.restoreFromExtras(extras);
3924
3925 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003926 }
3927
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003928 /**
3929 * @hide
3930 */
3931 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003932
3933 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003934 CharSequence oldBuilderContentText =
3935 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3936 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003937
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003938 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003939
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003940 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003941
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003942 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003943 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003944 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003945
Kenny Guy98193ea2014-07-24 19:54:37 +01003946 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3947
Selim Cinek4fb12d32015-11-19 18:10:48 -08003948 contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.mLargeIcon != null);
3949
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003950 return contentView;
3951 }
3952
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003953 private int calculateMaxLines() {
3954 int lineCount = MAX_LINES;
3955 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003956 boolean hasSummary = (mSummaryTextSet ? mSummaryText
3957 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003958 if (hasActions) {
3959 lineCount -= LINES_CONSUMED_BY_ACTIONS;
3960 }
3961 if (hasSummary) {
3962 lineCount -= LINES_CONSUMED_BY_SUMMARY;
3963 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003964 return lineCount;
3965 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003966 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04003967
3968 /**
3969 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08003970 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003971 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04003972 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003973 * Notification notif = new Notification.Builder(mContext)
3974 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
3975 * .setContentText(subject)
3976 * .setSmallIcon(R.drawable.new_mail)
3977 * .setLargeIcon(aBitmap)
3978 * .setStyle(new Notification.InboxStyle()
3979 * .addLine(str1)
3980 * .addLine(str2)
3981 * .setContentTitle(&quot;&quot;)
3982 * .setSummaryText(&quot;+3 more&quot;))
3983 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04003984 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003985 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04003986 * @see Notification#bigContentView
3987 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003988 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04003989 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
3990
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003991 public InboxStyle() {
3992 }
3993
Daniel Sandler879c5e02012-04-17 16:46:51 -04003994 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003995 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04003996 }
3997
Chris Wrend6297db2012-05-03 16:20:13 -04003998 /**
3999 * Overrides ContentTitle in the big form of the template.
4000 * This defaults to the value passed to setContentTitle().
4001 */
4002 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004003 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004004 return this;
4005 }
4006
4007 /**
4008 * Set the first line of text after the detail section in the big form of the template.
4009 */
4010 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004011 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004012 return this;
4013 }
4014
Chris Wren0bd664d2012-08-01 13:56:56 -04004015 /**
4016 * Append a line to the digest section of the Inbox notification.
4017 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004018 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004019 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004020 return this;
4021 }
4022
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004023 /**
4024 * @hide
4025 */
4026 public void addExtras(Bundle extras) {
4027 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004028
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004029 CharSequence[] a = new CharSequence[mTexts.size()];
4030 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4031 }
4032
Christoph Studer4600f9b2014-07-22 22:44:43 +02004033 /**
4034 * @hide
4035 */
4036 @Override
4037 protected void restoreFromExtras(Bundle extras) {
4038 super.restoreFromExtras(extras);
4039
4040 mTexts.clear();
4041 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4042 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4043 }
4044 }
4045
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004046 /**
4047 * @hide
4048 */
4049 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004050 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004051 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004052 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4053 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004054
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004055 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004056
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004057 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004058
Chris Wrend6297db2012-05-03 16:20:13 -04004059 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 -04004060 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004061
Chris Wren4ed80d52012-05-17 09:30:03 -04004062 // Make sure all rows are gone in case we reuse a view.
4063 for (int rowId : rowIds) {
4064 contentView.setViewVisibility(rowId, View.GONE);
4065 }
4066
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004067 final boolean largeText =
4068 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4069 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4070 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004071 int i=0;
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004072 final float density = mBuilder.mContext.getResources().getDisplayMetrics().density;
4073 int topPadding = (int) (5 * density);
4074 int bottomPadding = (int) (13 * density);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004075 while (i < mTexts.size() && i < rowIds.length) {
4076 CharSequence str = mTexts.get(i);
4077 if (str != null && !str.equals("")) {
4078 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004079 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004080 if (largeText) {
4081 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4082 subTextSize);
4083 }
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004084 contentView.setViewPadding(rowIds[i], 0, topPadding, 0,
4085 i == rowIds.length - 1 || i == mTexts.size() - 1 ? bottomPadding : 0);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004086 }
4087 i++;
4088 }
Kenny Guy98193ea2014-07-24 19:54:37 +01004089 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4090
Selim Cinek1e0bf612015-11-20 15:57:26 -08004091 handleInboxImageMargin(contentView, rowIds[0]);
4092
Daniel Sandler879c5e02012-04-17 16:46:51 -04004093 return contentView;
4094 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004095
4096 private void handleInboxImageMargin(RemoteViews contentView, int id) {
4097 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
4098 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4099 boolean hasProgress = max != 0 || ind;
4100 int endMargin = 0;
4101 if (mTexts.size() > 0 && mBuilder.mN.mLargeIcon != null && !hasProgress) {
4102 endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
4103 R.dimen.notification_content_picture_margin);
4104 }
4105 contentView.setViewLayoutMarginEnd(id, endMargin);
4106 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004107 }
Dan Sandler842dd772014-05-15 09:36:47 -04004108
4109 /**
4110 * Notification style for media playback notifications.
4111 *
4112 * In the expanded form, {@link Notification#bigContentView}, up to 5
4113 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004114 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004115 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4116 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4117 * treated as album artwork.
4118 *
4119 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4120 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004121 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004122 * in the standard view alongside the usual content.
4123 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004124 * Notifications created with MediaStyle will have their category set to
4125 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4126 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4127 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004128 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4129 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004130 * the System UI can identify this as a notification representing an active media session
4131 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4132 *
4133 * To use this style with your Notification, feed it to
4134 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4135 * <pre class="prettyprint">
4136 * Notification noti = new Notification.Builder()
4137 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004138 * .setContentTitle(&quot;Track title&quot;)
4139 * .setContentText(&quot;Artist - Album&quot;)
4140 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004141 * .setStyle(<b>new Notification.MediaStyle()</b>
4142 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004143 * .build();
4144 * </pre>
4145 *
4146 * @see Notification#bigContentView
4147 */
4148 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004149 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004150 static final int MAX_MEDIA_BUTTONS = 5;
4151
4152 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004153 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004154
4155 public MediaStyle() {
4156 }
4157
4158 public MediaStyle(Builder builder) {
4159 setBuilder(builder);
4160 }
4161
4162 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004163 * 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 -04004164 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004165 *
4166 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004167 */
4168 public MediaStyle setShowActionsInCompactView(int...actions) {
4169 mActionsToShowInCompact = actions;
4170 return this;
4171 }
4172
4173 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004174 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4175 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004176 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004177 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004178 mToken = token;
4179 return this;
4180 }
4181
Christoph Studer4600f9b2014-07-22 22:44:43 +02004182 /**
4183 * @hide
4184 */
Dan Sandler842dd772014-05-15 09:36:47 -04004185 @Override
4186 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004187 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004188 if (wip.category == null) {
4189 wip.category = Notification.CATEGORY_TRANSPORT;
4190 }
Dan Sandler842dd772014-05-15 09:36:47 -04004191 return wip;
4192 }
4193
Christoph Studer4600f9b2014-07-22 22:44:43 +02004194 /**
4195 * @hide
4196 */
4197 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004198 public RemoteViews makeContentView() {
4199 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004200 }
4201
4202 /**
4203 * @hide
4204 */
4205 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004206 public RemoteViews makeBigContentView() {
4207 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004208 }
4209
Dan Sandler842dd772014-05-15 09:36:47 -04004210 /** @hide */
4211 @Override
4212 public void addExtras(Bundle extras) {
4213 super.addExtras(extras);
4214
4215 if (mToken != null) {
4216 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4217 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004218 if (mActionsToShowInCompact != null) {
4219 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4220 }
Dan Sandler842dd772014-05-15 09:36:47 -04004221 }
4222
Christoph Studer4600f9b2014-07-22 22:44:43 +02004223 /**
4224 * @hide
4225 */
4226 @Override
4227 protected void restoreFromExtras(Bundle extras) {
4228 super.restoreFromExtras(extras);
4229
4230 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4231 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4232 }
4233 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4234 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4235 }
4236 }
4237
Selim Cinek5bf069a2015-11-10 19:14:27 -05004238 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04004239 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004240 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004241 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004242 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05004243 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
4244 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004245 if (!tombstone) {
4246 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4247 }
4248 button.setContentDescription(R.id.action0, action.title);
4249 return button;
4250 }
4251
4252 private RemoteViews makeMediaContentView() {
4253 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004254 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004255
4256 final int numActions = mBuilder.mActions.size();
4257 final int N = mActionsToShowInCompact == null
4258 ? 0
4259 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4260 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004261 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004262 for (int i = 0; i < N; i++) {
4263 if (i >= numActions) {
4264 throw new IllegalArgumentException(String.format(
4265 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4266 i, numActions - 1));
4267 }
4268
4269 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004270 final RemoteViews button = generateMediaActionButton(action,
4271 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004272 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004273 }
4274 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004275 handleImage(view /* addPaddingToMainColumn */);
Dan Sandler842dd772014-05-15 09:36:47 -04004276 return view;
4277 }
4278
4279 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004280 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004281 RemoteViews big = mBuilder.applyStandardTemplate(
4282 R.layout.notification_template_material_big_media,
4283 false);
Dan Sandler842dd772014-05-15 09:36:47 -04004284
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004285 if (actionCount > 0) {
4286 big.removeAllViews(com.android.internal.R.id.media_actions);
4287 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05004288 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
4289 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004290 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004291 }
4292 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004293 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04004294 return big;
4295 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004296
Selim Cinek5bf069a2015-11-10 19:14:27 -05004297 private void handleImage(RemoteViews contentView) {
4298 if (mBuilder.mN.mLargeIcon != null) {
4299 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004300 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004301 }
4302 }
4303
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004304 /**
4305 * @hide
4306 */
4307 @Override
4308 protected boolean hasProgress() {
4309 return false;
4310 }
Dan Sandler842dd772014-05-15 09:36:47 -04004311 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004312
Christoph Studer4600f9b2014-07-22 22:44:43 +02004313 // When adding a new Style subclass here, don't forget to update
4314 // Builder.getNotificationStyleClass.
4315
Griff Hazen61a9e862014-05-22 16:05:19 -07004316 /**
4317 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4318 * metadata or change options on a notification builder.
4319 */
4320 public interface Extender {
4321 /**
4322 * Apply this extender to a notification builder.
4323 * @param builder the builder to be modified.
4324 * @return the build object for chaining.
4325 */
4326 public Builder extend(Builder builder);
4327 }
4328
4329 /**
4330 * Helper class to add wearable extensions to notifications.
4331 * <p class="note"> See
4332 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4333 * for Android Wear</a> for more information on how to use this class.
4334 * <p>
4335 * To create a notification with wearable extensions:
4336 * <ol>
4337 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4338 * properties.
4339 * <li>Create a {@link android.app.Notification.WearableExtender}.
4340 * <li>Set wearable-specific properties using the
4341 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4342 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4343 * notification.
4344 * <li>Post the notification to the notification system with the
4345 * {@code NotificationManager.notify(...)} methods.
4346 * </ol>
4347 *
4348 * <pre class="prettyprint">
4349 * Notification notif = new Notification.Builder(mContext)
4350 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4351 * .setContentText(subject)
4352 * .setSmallIcon(R.drawable.new_mail)
4353 * .extend(new Notification.WearableExtender()
4354 * .setContentIcon(R.drawable.new_mail))
4355 * .build();
4356 * NotificationManager notificationManger =
4357 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4358 * notificationManger.notify(0, notif);</pre>
4359 *
4360 * <p>Wearable extensions can be accessed on an existing notification by using the
4361 * {@code WearableExtender(Notification)} constructor,
4362 * and then using the {@code get} methods to access values.
4363 *
4364 * <pre class="prettyprint">
4365 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4366 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004367 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004368 */
4369 public static final class WearableExtender implements Extender {
4370 /**
4371 * Sentinel value for an action index that is unset.
4372 */
4373 public static final int UNSET_ACTION_INDEX = -1;
4374
4375 /**
4376 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4377 * default sizing.
4378 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004379 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004380 * on their content.
4381 */
4382 public static final int SIZE_DEFAULT = 0;
4383
4384 /**
4385 * Size value for use with {@link #setCustomSizePreset} to show this notification
4386 * with an extra small size.
4387 * <p>This value is only applicable for custom display notifications created using
4388 * {@link #setDisplayIntent}.
4389 */
4390 public static final int SIZE_XSMALL = 1;
4391
4392 /**
4393 * Size value for use with {@link #setCustomSizePreset} to show this notification
4394 * with a small size.
4395 * <p>This value is only applicable for custom display notifications created using
4396 * {@link #setDisplayIntent}.
4397 */
4398 public static final int SIZE_SMALL = 2;
4399
4400 /**
4401 * Size value for use with {@link #setCustomSizePreset} to show this notification
4402 * with a medium size.
4403 * <p>This value is only applicable for custom display notifications created using
4404 * {@link #setDisplayIntent}.
4405 */
4406 public static final int SIZE_MEDIUM = 3;
4407
4408 /**
4409 * Size value for use with {@link #setCustomSizePreset} to show this notification
4410 * with a large size.
4411 * <p>This value is only applicable for custom display notifications created using
4412 * {@link #setDisplayIntent}.
4413 */
4414 public static final int SIZE_LARGE = 4;
4415
Griff Hazend5f11f92014-05-27 15:40:09 -07004416 /**
4417 * Size value for use with {@link #setCustomSizePreset} to show this notification
4418 * full screen.
4419 * <p>This value is only applicable for custom display notifications created using
4420 * {@link #setDisplayIntent}.
4421 */
4422 public static final int SIZE_FULL_SCREEN = 5;
4423
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004424 /**
4425 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4426 * short amount of time when this notification is displayed on the screen. This
4427 * is the default value.
4428 */
4429 public static final int SCREEN_TIMEOUT_SHORT = 0;
4430
4431 /**
4432 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4433 * for a longer amount of time when this notification is displayed on the screen.
4434 */
4435 public static final int SCREEN_TIMEOUT_LONG = -1;
4436
Griff Hazen61a9e862014-05-22 16:05:19 -07004437 /** Notification extra which contains wearable extensions */
4438 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4439
Pete Gastaf6781d2014-10-07 15:17:05 -04004440 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004441 private static final String KEY_ACTIONS = "actions";
4442 private static final String KEY_FLAGS = "flags";
4443 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4444 private static final String KEY_PAGES = "pages";
4445 private static final String KEY_BACKGROUND = "background";
4446 private static final String KEY_CONTENT_ICON = "contentIcon";
4447 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4448 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4449 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4450 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4451 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004452 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004453
4454 // Flags bitwise-ored to mFlags
4455 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4456 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4457 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4458 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004459 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004460
4461 // Default value for flags integer
4462 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4463
4464 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4465 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4466
4467 private ArrayList<Action> mActions = new ArrayList<Action>();
4468 private int mFlags = DEFAULT_FLAGS;
4469 private PendingIntent mDisplayIntent;
4470 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4471 private Bitmap mBackground;
4472 private int mContentIcon;
4473 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4474 private int mContentActionIndex = UNSET_ACTION_INDEX;
4475 private int mCustomSizePreset = SIZE_DEFAULT;
4476 private int mCustomContentHeight;
4477 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004478 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004479
4480 /**
4481 * Create a {@link android.app.Notification.WearableExtender} with default
4482 * options.
4483 */
4484 public WearableExtender() {
4485 }
4486
4487 public WearableExtender(Notification notif) {
4488 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4489 if (wearableBundle != null) {
4490 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4491 if (actions != null) {
4492 mActions.addAll(actions);
4493 }
4494
4495 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4496 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4497
4498 Notification[] pages = getNotificationArrayFromBundle(
4499 wearableBundle, KEY_PAGES);
4500 if (pages != null) {
4501 Collections.addAll(mPages, pages);
4502 }
4503
4504 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4505 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4506 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4507 DEFAULT_CONTENT_ICON_GRAVITY);
4508 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4509 UNSET_ACTION_INDEX);
4510 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4511 SIZE_DEFAULT);
4512 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4513 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004514 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004515 }
4516 }
4517
4518 /**
4519 * Apply wearable extensions to a notification that is being built. This is typically
4520 * called by the {@link android.app.Notification.Builder#extend} method of
4521 * {@link android.app.Notification.Builder}.
4522 */
4523 @Override
4524 public Notification.Builder extend(Notification.Builder builder) {
4525 Bundle wearableBundle = new Bundle();
4526
4527 if (!mActions.isEmpty()) {
4528 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4529 }
4530 if (mFlags != DEFAULT_FLAGS) {
4531 wearableBundle.putInt(KEY_FLAGS, mFlags);
4532 }
4533 if (mDisplayIntent != null) {
4534 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4535 }
4536 if (!mPages.isEmpty()) {
4537 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4538 new Notification[mPages.size()]));
4539 }
4540 if (mBackground != null) {
4541 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4542 }
4543 if (mContentIcon != 0) {
4544 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4545 }
4546 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4547 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4548 }
4549 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4550 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4551 mContentActionIndex);
4552 }
4553 if (mCustomSizePreset != SIZE_DEFAULT) {
4554 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4555 }
4556 if (mCustomContentHeight != 0) {
4557 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4558 }
4559 if (mGravity != DEFAULT_GRAVITY) {
4560 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4561 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004562 if (mHintScreenTimeout != 0) {
4563 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4564 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004565
4566 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4567 return builder;
4568 }
4569
4570 @Override
4571 public WearableExtender clone() {
4572 WearableExtender that = new WearableExtender();
4573 that.mActions = new ArrayList<Action>(this.mActions);
4574 that.mFlags = this.mFlags;
4575 that.mDisplayIntent = this.mDisplayIntent;
4576 that.mPages = new ArrayList<Notification>(this.mPages);
4577 that.mBackground = this.mBackground;
4578 that.mContentIcon = this.mContentIcon;
4579 that.mContentIconGravity = this.mContentIconGravity;
4580 that.mContentActionIndex = this.mContentActionIndex;
4581 that.mCustomSizePreset = this.mCustomSizePreset;
4582 that.mCustomContentHeight = this.mCustomContentHeight;
4583 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004584 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004585 return that;
4586 }
4587
4588 /**
4589 * Add a wearable action to this notification.
4590 *
4591 * <p>When wearable actions are added using this method, the set of actions that
4592 * show on a wearable device splits from devices that only show actions added
4593 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4594 * of which actions display on different devices.
4595 *
4596 * @param action the action to add to this notification
4597 * @return this object for method chaining
4598 * @see android.app.Notification.Action
4599 */
4600 public WearableExtender addAction(Action action) {
4601 mActions.add(action);
4602 return this;
4603 }
4604
4605 /**
4606 * Adds wearable actions to this notification.
4607 *
4608 * <p>When wearable actions are added using this method, the set of actions that
4609 * show on a wearable device splits from devices that only show actions added
4610 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4611 * of which actions display on different devices.
4612 *
4613 * @param actions the actions to add to this notification
4614 * @return this object for method chaining
4615 * @see android.app.Notification.Action
4616 */
4617 public WearableExtender addActions(List<Action> actions) {
4618 mActions.addAll(actions);
4619 return this;
4620 }
4621
4622 /**
4623 * Clear all wearable actions present on this builder.
4624 * @return this object for method chaining.
4625 * @see #addAction
4626 */
4627 public WearableExtender clearActions() {
4628 mActions.clear();
4629 return this;
4630 }
4631
4632 /**
4633 * Get the wearable actions present on this notification.
4634 */
4635 public List<Action> getActions() {
4636 return mActions;
4637 }
4638
4639 /**
4640 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004641 * this notification. The {@link PendingIntent} provided should be for an activity.
4642 *
4643 * <pre class="prettyprint">
4644 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4645 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4646 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4647 * Notification notif = new Notification.Builder(context)
4648 * .extend(new Notification.WearableExtender()
4649 * .setDisplayIntent(displayPendingIntent)
4650 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4651 * .build();</pre>
4652 *
4653 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004654 * should have an empty task affinity. It is also recommended to use the device
4655 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004656 *
4657 * <p>Example AndroidManifest.xml entry:
4658 * <pre class="prettyprint">
4659 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4660 * android:exported=&quot;true&quot;
4661 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004662 * android:taskAffinity=&quot;&quot;
4663 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004664 *
4665 * @param intent the {@link PendingIntent} for an activity
4666 * @return this object for method chaining
4667 * @see android.app.Notification.WearableExtender#getDisplayIntent
4668 */
4669 public WearableExtender setDisplayIntent(PendingIntent intent) {
4670 mDisplayIntent = intent;
4671 return this;
4672 }
4673
4674 /**
4675 * Get the intent to launch inside of an activity view when displaying this
4676 * notification. This {@code PendingIntent} should be for an activity.
4677 */
4678 public PendingIntent getDisplayIntent() {
4679 return mDisplayIntent;
4680 }
4681
4682 /**
4683 * Add an additional page of content to display with this notification. The current
4684 * notification forms the first page, and pages added using this function form
4685 * subsequent pages. This field can be used to separate a notification into multiple
4686 * sections.
4687 *
4688 * @param page the notification to add as another page
4689 * @return this object for method chaining
4690 * @see android.app.Notification.WearableExtender#getPages
4691 */
4692 public WearableExtender addPage(Notification page) {
4693 mPages.add(page);
4694 return this;
4695 }
4696
4697 /**
4698 * Add additional pages of content to display with this notification. The current
4699 * notification forms the first page, and pages added using this function form
4700 * subsequent pages. This field can be used to separate a notification into multiple
4701 * sections.
4702 *
4703 * @param pages a list of notifications
4704 * @return this object for method chaining
4705 * @see android.app.Notification.WearableExtender#getPages
4706 */
4707 public WearableExtender addPages(List<Notification> pages) {
4708 mPages.addAll(pages);
4709 return this;
4710 }
4711
4712 /**
4713 * Clear all additional pages present on this builder.
4714 * @return this object for method chaining.
4715 * @see #addPage
4716 */
4717 public WearableExtender clearPages() {
4718 mPages.clear();
4719 return this;
4720 }
4721
4722 /**
4723 * Get the array of additional pages of content for displaying this notification. The
4724 * current notification forms the first page, and elements within this array form
4725 * subsequent pages. This field can be used to separate a notification into multiple
4726 * sections.
4727 * @return the pages for this notification
4728 */
4729 public List<Notification> getPages() {
4730 return mPages;
4731 }
4732
4733 /**
4734 * Set a background image to be displayed behind the notification content.
4735 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4736 * will work with any notification style.
4737 *
4738 * @param background the background bitmap
4739 * @return this object for method chaining
4740 * @see android.app.Notification.WearableExtender#getBackground
4741 */
4742 public WearableExtender setBackground(Bitmap background) {
4743 mBackground = background;
4744 return this;
4745 }
4746
4747 /**
4748 * Get a background image to be displayed behind the notification content.
4749 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4750 * will work with any notification style.
4751 *
4752 * @return the background image
4753 * @see android.app.Notification.WearableExtender#setBackground
4754 */
4755 public Bitmap getBackground() {
4756 return mBackground;
4757 }
4758
4759 /**
4760 * Set an icon that goes with the content of this notification.
4761 */
4762 public WearableExtender setContentIcon(int icon) {
4763 mContentIcon = icon;
4764 return this;
4765 }
4766
4767 /**
4768 * Get an icon that goes with the content of this notification.
4769 */
4770 public int getContentIcon() {
4771 return mContentIcon;
4772 }
4773
4774 /**
4775 * Set the gravity that the content icon should have within the notification display.
4776 * Supported values include {@link android.view.Gravity#START} and
4777 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4778 * @see #setContentIcon
4779 */
4780 public WearableExtender setContentIconGravity(int contentIconGravity) {
4781 mContentIconGravity = contentIconGravity;
4782 return this;
4783 }
4784
4785 /**
4786 * Get the gravity that the content icon should have within the notification display.
4787 * Supported values include {@link android.view.Gravity#START} and
4788 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4789 * @see #getContentIcon
4790 */
4791 public int getContentIconGravity() {
4792 return mContentIconGravity;
4793 }
4794
4795 /**
4796 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004797 * this notification. This action will no longer display separately from the
4798 * notification's content.
4799 *
Griff Hazenca48d352014-05-28 22:37:13 -07004800 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004801 * set, although the list of available actions comes from the main notification and not
4802 * from the child page's notification.
4803 *
4804 * @param actionIndex The index of the action to hoist onto the current notification page.
4805 * If wearable actions were added to the main notification, this index
4806 * will apply to that list, otherwise it will apply to the regular
4807 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004808 */
4809 public WearableExtender setContentAction(int actionIndex) {
4810 mContentActionIndex = actionIndex;
4811 return this;
4812 }
4813
4814 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004815 * Get the index of the notification action, if any, that was specified as being clickable
4816 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004817 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004818 *
Griff Hazenca48d352014-05-28 22:37:13 -07004819 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004820 * set, although the list of available actions comes from the main notification and not
4821 * from the child page's notification.
4822 *
4823 * <p>If wearable specific actions were added to the main notification, this index will
4824 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004825 *
4826 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004827 */
4828 public int getContentAction() {
4829 return mContentActionIndex;
4830 }
4831
4832 /**
4833 * Set the gravity that this notification should have within the available viewport space.
4834 * Supported values include {@link android.view.Gravity#TOP},
4835 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4836 * The default value is {@link android.view.Gravity#BOTTOM}.
4837 */
4838 public WearableExtender setGravity(int gravity) {
4839 mGravity = gravity;
4840 return this;
4841 }
4842
4843 /**
4844 * Get the gravity that this notification should have within the available viewport space.
4845 * Supported values include {@link android.view.Gravity#TOP},
4846 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4847 * The default value is {@link android.view.Gravity#BOTTOM}.
4848 */
4849 public int getGravity() {
4850 return mGravity;
4851 }
4852
4853 /**
4854 * Set the custom size preset for the display of this notification out of the available
4855 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4856 * {@link #SIZE_LARGE}.
4857 * <p>Some custom size presets are only applicable for custom display notifications created
4858 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4859 * documentation for the preset in question. See also
4860 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4861 */
4862 public WearableExtender setCustomSizePreset(int sizePreset) {
4863 mCustomSizePreset = sizePreset;
4864 return this;
4865 }
4866
4867 /**
4868 * Get the custom size preset for the display of this notification out of the available
4869 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4870 * {@link #SIZE_LARGE}.
4871 * <p>Some custom size presets are only applicable for custom display notifications created
4872 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4873 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4874 */
4875 public int getCustomSizePreset() {
4876 return mCustomSizePreset;
4877 }
4878
4879 /**
4880 * Set the custom height in pixels for the display of this notification's content.
4881 * <p>This option is only available for custom display notifications created
4882 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4883 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4884 * {@link #getCustomContentHeight}.
4885 */
4886 public WearableExtender setCustomContentHeight(int height) {
4887 mCustomContentHeight = height;
4888 return this;
4889 }
4890
4891 /**
4892 * Get the custom height in pixels for the display of this notification's content.
4893 * <p>This option is only available for custom display notifications created
4894 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4895 * {@link #setCustomContentHeight}.
4896 */
4897 public int getCustomContentHeight() {
4898 return mCustomContentHeight;
4899 }
4900
4901 /**
4902 * Set whether the scrolling position for the contents of this notification should start
4903 * at the bottom of the contents instead of the top when the contents are too long to
4904 * display within the screen. Default is false (start scroll at the top).
4905 */
4906 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4907 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4908 return this;
4909 }
4910
4911 /**
4912 * Get whether the scrolling position for the contents of this notification should start
4913 * at the bottom of the contents instead of the top when the contents are too long to
4914 * display within the screen. Default is false (start scroll at the top).
4915 */
4916 public boolean getStartScrollBottom() {
4917 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4918 }
4919
4920 /**
4921 * Set whether the content intent is available when the wearable device is not connected
4922 * to a companion device. The user can still trigger this intent when the wearable device
4923 * is offline, but a visual hint will indicate that the content intent may not be available.
4924 * Defaults to true.
4925 */
4926 public WearableExtender setContentIntentAvailableOffline(
4927 boolean contentIntentAvailableOffline) {
4928 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4929 return this;
4930 }
4931
4932 /**
4933 * Get whether the content intent is available when the wearable device is not connected
4934 * to a companion device. The user can still trigger this intent when the wearable device
4935 * is offline, but a visual hint will indicate that the content intent may not be available.
4936 * Defaults to true.
4937 */
4938 public boolean getContentIntentAvailableOffline() {
4939 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
4940 }
4941
4942 /**
4943 * Set a hint that this notification's icon should not be displayed.
4944 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
4945 * @return this object for method chaining
4946 */
4947 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
4948 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
4949 return this;
4950 }
4951
4952 /**
4953 * Get a hint that this notification's icon should not be displayed.
4954 * @return {@code true} if this icon should not be displayed, false otherwise.
4955 * The default value is {@code false} if this was never set.
4956 */
4957 public boolean getHintHideIcon() {
4958 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
4959 }
4960
4961 /**
4962 * Set a visual hint that only the background image of this notification should be
4963 * displayed, and other semantic content should be hidden. This hint is only applicable
4964 * to sub-pages added using {@link #addPage}.
4965 */
4966 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
4967 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
4968 return this;
4969 }
4970
4971 /**
4972 * Get a visual hint that only the background image of this notification should be
4973 * displayed, and other semantic content should be hidden. This hint is only applicable
4974 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
4975 */
4976 public boolean getHintShowBackgroundOnly() {
4977 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
4978 }
4979
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004980 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08004981 * Set a hint that this notification's background should not be clipped if possible,
4982 * and should instead be resized to fully display on the screen, retaining the aspect
4983 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004984 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
4985 * @return this object for method chaining
4986 */
4987 public WearableExtender setHintAvoidBackgroundClipping(
4988 boolean hintAvoidBackgroundClipping) {
4989 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
4990 return this;
4991 }
4992
4993 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08004994 * Get a hint that this notification's background should not be clipped if possible,
4995 * and should instead be resized to fully display on the screen, retaining the aspect
4996 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004997 * @return {@code true} if it's ok if the background is clipped on the screen, false
4998 * otherwise. The default value is {@code false} if this was never set.
4999 */
5000 public boolean getHintAvoidBackgroundClipping() {
5001 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5002 }
5003
5004 /**
5005 * Set a hint that the screen should remain on for at least this duration when
5006 * this notification is displayed on the screen.
5007 * @param timeout The requested screen timeout in milliseconds. Can also be either
5008 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5009 * @return this object for method chaining
5010 */
5011 public WearableExtender setHintScreenTimeout(int timeout) {
5012 mHintScreenTimeout = timeout;
5013 return this;
5014 }
5015
5016 /**
5017 * Get the duration, in milliseconds, that the screen should remain on for
5018 * when this notification is displayed.
5019 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5020 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5021 */
5022 public int getHintScreenTimeout() {
5023 return mHintScreenTimeout;
5024 }
5025
Griff Hazen61a9e862014-05-22 16:05:19 -07005026 private void setFlag(int mask, boolean value) {
5027 if (value) {
5028 mFlags |= mask;
5029 } else {
5030 mFlags &= ~mask;
5031 }
5032 }
5033 }
5034
5035 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005036 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5037 * with car extensions:
5038 *
5039 * <ol>
5040 * <li>Create an {@link Notification.Builder}, setting any desired
5041 * properties.
5042 * <li>Create a {@link CarExtender}.
5043 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5044 * {@link CarExtender}.
5045 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5046 * to apply the extensions to a notification.
5047 * </ol>
5048 *
5049 * <pre class="prettyprint">
5050 * Notification notification = new Notification.Builder(context)
5051 * ...
5052 * .extend(new CarExtender()
5053 * .set*(...))
5054 * .build();
5055 * </pre>
5056 *
5057 * <p>Car extensions can be accessed on an existing notification by using the
5058 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5059 * to access values.
5060 */
5061 public static final class CarExtender implements Extender {
5062 private static final String TAG = "CarExtender";
5063
5064 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5065 private static final String EXTRA_LARGE_ICON = "large_icon";
5066 private static final String EXTRA_CONVERSATION = "car_conversation";
5067 private static final String EXTRA_COLOR = "app_color";
5068
5069 private Bitmap mLargeIcon;
5070 private UnreadConversation mUnreadConversation;
5071 private int mColor = Notification.COLOR_DEFAULT;
5072
5073 /**
5074 * Create a {@link CarExtender} with default options.
5075 */
5076 public CarExtender() {
5077 }
5078
5079 /**
5080 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5081 *
5082 * @param notif The notification from which to copy options.
5083 */
5084 public CarExtender(Notification notif) {
5085 Bundle carBundle = notif.extras == null ?
5086 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5087 if (carBundle != null) {
5088 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5089 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5090
5091 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5092 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5093 }
5094 }
5095
5096 /**
5097 * Apply car extensions to a notification that is being built. This is typically called by
5098 * the {@link Notification.Builder#extend(Notification.Extender)}
5099 * method of {@link Notification.Builder}.
5100 */
5101 @Override
5102 public Notification.Builder extend(Notification.Builder builder) {
5103 Bundle carExtensions = new Bundle();
5104
5105 if (mLargeIcon != null) {
5106 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5107 }
5108 if (mColor != Notification.COLOR_DEFAULT) {
5109 carExtensions.putInt(EXTRA_COLOR, mColor);
5110 }
5111
5112 if (mUnreadConversation != null) {
5113 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5114 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5115 }
5116
5117 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5118 return builder;
5119 }
5120
5121 /**
5122 * Sets the accent color to use when Android Auto presents the notification.
5123 *
5124 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5125 * to accent the displayed notification. However, not all colors are acceptable in an
5126 * automotive setting. This method can be used to override the color provided in the
5127 * notification in such a situation.
5128 */
Tor Norbye80756e32015-03-02 09:39:27 -08005129 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005130 mColor = color;
5131 return this;
5132 }
5133
5134 /**
5135 * Gets the accent color.
5136 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005137 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005138 */
Tor Norbye80756e32015-03-02 09:39:27 -08005139 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005140 public int getColor() {
5141 return mColor;
5142 }
5143
5144 /**
5145 * Sets the large icon of the car notification.
5146 *
5147 * If no large icon is set in the extender, Android Auto will display the icon
5148 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5149 *
5150 * @param largeIcon The large icon to use in the car notification.
5151 * @return This object for method chaining.
5152 */
5153 public CarExtender setLargeIcon(Bitmap largeIcon) {
5154 mLargeIcon = largeIcon;
5155 return this;
5156 }
5157
5158 /**
5159 * Gets the large icon used in this car notification, or null if no icon has been set.
5160 *
5161 * @return The large icon for the car notification.
5162 * @see CarExtender#setLargeIcon
5163 */
5164 public Bitmap getLargeIcon() {
5165 return mLargeIcon;
5166 }
5167
5168 /**
5169 * Sets the unread conversation in a message notification.
5170 *
5171 * @param unreadConversation The unread part of the conversation this notification conveys.
5172 * @return This object for method chaining.
5173 */
5174 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5175 mUnreadConversation = unreadConversation;
5176 return this;
5177 }
5178
5179 /**
5180 * Returns the unread conversation conveyed by this notification.
5181 * @see #setUnreadConversation(UnreadConversation)
5182 */
5183 public UnreadConversation getUnreadConversation() {
5184 return mUnreadConversation;
5185 }
5186
5187 /**
5188 * A class which holds the unread messages from a conversation.
5189 */
5190 public static class UnreadConversation {
5191 private static final String KEY_AUTHOR = "author";
5192 private static final String KEY_TEXT = "text";
5193 private static final String KEY_MESSAGES = "messages";
5194 private static final String KEY_REMOTE_INPUT = "remote_input";
5195 private static final String KEY_ON_REPLY = "on_reply";
5196 private static final String KEY_ON_READ = "on_read";
5197 private static final String KEY_PARTICIPANTS = "participants";
5198 private static final String KEY_TIMESTAMP = "timestamp";
5199
5200 private final String[] mMessages;
5201 private final RemoteInput mRemoteInput;
5202 private final PendingIntent mReplyPendingIntent;
5203 private final PendingIntent mReadPendingIntent;
5204 private final String[] mParticipants;
5205 private final long mLatestTimestamp;
5206
5207 UnreadConversation(String[] messages, RemoteInput remoteInput,
5208 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5209 String[] participants, long latestTimestamp) {
5210 mMessages = messages;
5211 mRemoteInput = remoteInput;
5212 mReadPendingIntent = readPendingIntent;
5213 mReplyPendingIntent = replyPendingIntent;
5214 mParticipants = participants;
5215 mLatestTimestamp = latestTimestamp;
5216 }
5217
5218 /**
5219 * Gets the list of messages conveyed by this notification.
5220 */
5221 public String[] getMessages() {
5222 return mMessages;
5223 }
5224
5225 /**
5226 * Gets the remote input that will be used to convey the response to a message list, or
5227 * null if no such remote input exists.
5228 */
5229 public RemoteInput getRemoteInput() {
5230 return mRemoteInput;
5231 }
5232
5233 /**
5234 * Gets the pending intent that will be triggered when the user replies to this
5235 * notification.
5236 */
5237 public PendingIntent getReplyPendingIntent() {
5238 return mReplyPendingIntent;
5239 }
5240
5241 /**
5242 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5243 * in this object's message list.
5244 */
5245 public PendingIntent getReadPendingIntent() {
5246 return mReadPendingIntent;
5247 }
5248
5249 /**
5250 * Gets the participants in the conversation.
5251 */
5252 public String[] getParticipants() {
5253 return mParticipants;
5254 }
5255
5256 /**
5257 * Gets the firs participant in the conversation.
5258 */
5259 public String getParticipant() {
5260 return mParticipants.length > 0 ? mParticipants[0] : null;
5261 }
5262
5263 /**
5264 * Gets the timestamp of the conversation.
5265 */
5266 public long getLatestTimestamp() {
5267 return mLatestTimestamp;
5268 }
5269
5270 Bundle getBundleForUnreadConversation() {
5271 Bundle b = new Bundle();
5272 String author = null;
5273 if (mParticipants != null && mParticipants.length > 1) {
5274 author = mParticipants[0];
5275 }
5276 Parcelable[] messages = new Parcelable[mMessages.length];
5277 for (int i = 0; i < messages.length; i++) {
5278 Bundle m = new Bundle();
5279 m.putString(KEY_TEXT, mMessages[i]);
5280 m.putString(KEY_AUTHOR, author);
5281 messages[i] = m;
5282 }
5283 b.putParcelableArray(KEY_MESSAGES, messages);
5284 if (mRemoteInput != null) {
5285 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5286 }
5287 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5288 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5289 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5290 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5291 return b;
5292 }
5293
5294 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5295 if (b == null) {
5296 return null;
5297 }
5298 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5299 String[] messages = null;
5300 if (parcelableMessages != null) {
5301 String[] tmp = new String[parcelableMessages.length];
5302 boolean success = true;
5303 for (int i = 0; i < tmp.length; i++) {
5304 if (!(parcelableMessages[i] instanceof Bundle)) {
5305 success = false;
5306 break;
5307 }
5308 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5309 if (tmp[i] == null) {
5310 success = false;
5311 break;
5312 }
5313 }
5314 if (success) {
5315 messages = tmp;
5316 } else {
5317 return null;
5318 }
5319 }
5320
5321 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5322 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5323
5324 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5325
5326 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5327 if (participants == null || participants.length != 1) {
5328 return null;
5329 }
5330
5331 return new UnreadConversation(messages,
5332 remoteInput,
5333 onReply,
5334 onRead,
5335 participants, b.getLong(KEY_TIMESTAMP));
5336 }
5337 };
5338
5339 /**
5340 * Builder class for {@link CarExtender.UnreadConversation} objects.
5341 */
5342 public static class Builder {
5343 private final List<String> mMessages = new ArrayList<String>();
5344 private final String mParticipant;
5345 private RemoteInput mRemoteInput;
5346 private PendingIntent mReadPendingIntent;
5347 private PendingIntent mReplyPendingIntent;
5348 private long mLatestTimestamp;
5349
5350 /**
5351 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5352 *
5353 * @param name The name of the other participant in the conversation.
5354 */
5355 public Builder(String name) {
5356 mParticipant = name;
5357 }
5358
5359 /**
5360 * Appends a new unread message to the list of messages for this conversation.
5361 *
5362 * The messages should be added from oldest to newest.
5363 *
5364 * @param message The text of the new unread message.
5365 * @return This object for method chaining.
5366 */
5367 public Builder addMessage(String message) {
5368 mMessages.add(message);
5369 return this;
5370 }
5371
5372 /**
5373 * Sets the pending intent and remote input which will convey the reply to this
5374 * notification.
5375 *
5376 * @param pendingIntent The pending intent which will be triggered on a reply.
5377 * @param remoteInput The remote input parcelable which will carry the reply.
5378 * @return This object for method chaining.
5379 *
5380 * @see CarExtender.UnreadConversation#getRemoteInput
5381 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5382 */
5383 public Builder setReplyAction(
5384 PendingIntent pendingIntent, RemoteInput remoteInput) {
5385 mRemoteInput = remoteInput;
5386 mReplyPendingIntent = pendingIntent;
5387
5388 return this;
5389 }
5390
5391 /**
5392 * Sets the pending intent that will be sent once the messages in this notification
5393 * are read.
5394 *
5395 * @param pendingIntent The pending intent to use.
5396 * @return This object for method chaining.
5397 */
5398 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5399 mReadPendingIntent = pendingIntent;
5400 return this;
5401 }
5402
5403 /**
5404 * Sets the timestamp of the most recent message in an unread conversation.
5405 *
5406 * If a messaging notification has been posted by your application and has not
5407 * yet been cancelled, posting a later notification with the same id and tag
5408 * but without a newer timestamp may result in Android Auto not displaying a
5409 * heads up notification for the later notification.
5410 *
5411 * @param timestamp The timestamp of the most recent message in the conversation.
5412 * @return This object for method chaining.
5413 */
5414 public Builder setLatestTimestamp(long timestamp) {
5415 mLatestTimestamp = timestamp;
5416 return this;
5417 }
5418
5419 /**
5420 * Builds a new unread conversation object.
5421 *
5422 * @return The new unread conversation object.
5423 */
5424 public UnreadConversation build() {
5425 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5426 String[] participants = { mParticipant };
5427 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5428 mReadPendingIntent, participants, mLatestTimestamp);
5429 }
5430 }
5431 }
5432
5433 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005434 * Get an array of Notification objects from a parcelable array bundle field.
5435 * Update the bundle to have a typed array so fetches in the future don't need
5436 * to do an array copy.
5437 */
5438 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5439 Parcelable[] array = bundle.getParcelableArray(key);
5440 if (array instanceof Notification[] || array == null) {
5441 return (Notification[]) array;
5442 }
5443 Notification[] typedArray = Arrays.copyOf(array, array.length,
5444 Notification[].class);
5445 bundle.putParcelableArray(key, typedArray);
5446 return typedArray;
5447 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005448
5449 private static class BuilderRemoteViews extends RemoteViews {
5450 public BuilderRemoteViews(Parcel parcel) {
5451 super(parcel);
5452 }
5453
Kenny Guy77320062014-08-27 21:37:15 +01005454 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5455 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005456 }
5457
5458 @Override
5459 public BuilderRemoteViews clone() {
5460 Parcel p = Parcel.obtain();
5461 writeToParcel(p, 0);
5462 p.setDataPosition(0);
5463 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5464 p.recycle();
5465 return brv;
5466 }
5467 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005468}