blob: 390c280e6b8d8111e58787849ff473638507d9d3 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
25import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010026import android.content.pm.ApplicationInfo;
Christoph Studer4600f9b2014-07-22 22:44:43 +020027import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020028import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040029import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010030import android.graphics.Canvas;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010031import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010032import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040033import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040034import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070035import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070036import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040038import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020039import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050040import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.os.Parcel;
42import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040043import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070044import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.text.TextUtils;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040046import android.util.Log;
Jorim Jaggi445d3c02014-08-19 22:33:42 +020047import android.util.MathUtils;
Daniel Sandler9f7936a2012-05-21 16:14:28 -040048import android.util.TypedValue;
Griff Hazen61a9e862014-05-22 16:05:19 -070049import android.view.Gravity;
Joe Onorato8595a3d2010-11-19 18:12:07 -080050import android.view.View;
Jeff Sharkey1c400132011-08-05 14:50:13 -070051import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.widget.RemoteViews;
53
Griff Hazen959591e2014-05-15 22:26:18 -070054import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070055import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070056
Tor Norbyed9273d62013-05-30 15:59:53 -070057import java.lang.annotation.Retention;
58import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020059import java.lang.reflect.Constructor;
Andy Stadler110988c2010-12-03 14:29:16 -080060import java.text.NumberFormat;
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 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400841 * {@link #extras} key: used to provide hints about the appropriateness of
842 * displaying this notification as a heads-up notification.
Chris Wren51c75102013-07-16 20:49:17 -0400843 * @hide
844 */
845 public static final String EXTRA_AS_HEADS_UP = "headsup";
846
847 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400848 * Allow certain system-generated notifications to appear before the device is provisioned.
849 * Only available to notifications coming from the android package.
850 * @hide
851 */
852 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
853
854 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700855 * {@link #extras} key: A
856 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
857 * in the background when the notification is selected. The URI must point to an image stream
858 * suitable for passing into
859 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
860 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
861 * URI used for this purpose must require no permissions to read the image data.
862 */
863 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
864
865 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400866 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700867 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400868 * {@link android.app.Notification.MediaStyle} notification.
869 */
870 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
871
872 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100873 * {@link #extras} key: the indices of actions to be shown in the compact view,
874 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
875 */
876 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
877
Christoph Studer943aa672014-08-03 20:31:16 +0200878 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100879 * {@link #extras} key: the user that built the notification.
880 *
881 * @hide
882 */
883 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
884
885 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400886 * @hide
887 */
888 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
889
890 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400891 * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
892 * displayed in the heads up space.
893 *
894 * <p>
895 * If this notification has a {@link #fullScreenIntent}, then it will always launch the
896 * full-screen intent when posted.
897 * </p>
Chris Wren51c75102013-07-16 20:49:17 -0400898 * @hide
899 */
900 public static final int HEADS_UP_NEVER = 0;
901
902 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400903 * Default value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification may be
904 * displayed as a heads up.
Chris Wren51c75102013-07-16 20:49:17 -0400905 * @hide
906 */
907 public static final int HEADS_UP_ALLOWED = 1;
908
909 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400910 * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification is a
911 * good candidate for display as a heads up.
Chris Wren51c75102013-07-16 20:49:17 -0400912 * @hide
913 */
914 public static final int HEADS_UP_REQUESTED = 2;
915
Dan Sandlerd63f9322015-05-06 15:18:49 -0400916 private Icon mSmallIcon;
917 private Icon mLargeIcon;
918
Chris Wren51c75102013-07-16 20:49:17 -0400919 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400920 * Structure to encapsulate a named action that can be shown as part of this notification.
921 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
922 * selected by the user.
923 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700924 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
925 * or {@link Notification.Builder#addAction(Notification.Action)}
926 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400927 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500928 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700929 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -0400930 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -0700931 private final RemoteInput[] mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700932
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400933 /**
934 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -0400935 *
936 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400937 */
Dan Sandler86647982015-05-13 23:41:13 -0400938 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400939 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700940
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400941 /**
942 * Title of the action.
943 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400944 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700945
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400946 /**
947 * Intent to send when the user invokes this action. May be null, in which case the action
948 * may be rendered in a disabled presentation by the system UI.
949 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400950 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -0700951
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400952 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -0400953 if (in.readInt() != 0) {
954 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -0400955 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
956 icon = mIcon.getResId();
957 }
Dan Sandler86647982015-05-13 23:41:13 -0400958 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400959 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
960 if (in.readInt() == 1) {
961 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
962 }
Griff Hazen959591e2014-05-15 22:26:18 -0700963 mExtras = in.readBundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700964 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400965 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700966
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400967 /**
Dan Sandler86647982015-05-13 23:41:13 -0400968 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400969 */
Dan Sandler86647982015-05-13 23:41:13 -0400970 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400971 public Action(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -0400972 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -0700973 }
974
Dan Sandler86647982015-05-13 23:41:13 -0400975 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700976 RemoteInput[] remoteInputs) {
Dan Sandler86647982015-05-13 23:41:13 -0400977 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -0400978 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
979 this.icon = icon.getResId();
980 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400981 this.title = title;
982 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -0700983 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700984 this.mRemoteInputs = remoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700985 }
986
987 /**
Dan Sandler86647982015-05-13 23:41:13 -0400988 * Return an icon representing the action.
989 */
990 public Icon getIcon() {
991 if (mIcon == null && icon != 0) {
992 // you snuck an icon in here without using the builder; let's try to keep it
993 mIcon = Icon.createWithResource("", icon);
994 }
995 return mIcon;
996 }
997
998 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700999 * Get additional metadata carried around with this Action.
1000 */
1001 public Bundle getExtras() {
1002 return mExtras;
1003 }
1004
1005 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001006 * Get the list of inputs to be collected from the user when this action is sent.
1007 * May return null if no remote inputs were added.
1008 */
1009 public RemoteInput[] getRemoteInputs() {
1010 return mRemoteInputs;
1011 }
1012
1013 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001014 * Builder class for {@link Action} objects.
1015 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001016 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -04001017 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -07001018 private final CharSequence mTitle;
1019 private final PendingIntent mIntent;
1020 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001021 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -07001022
1023 /**
1024 * Construct a new builder for {@link Action} object.
1025 * @param icon icon to show for this action
1026 * @param title the title of the action
1027 * @param intent the {@link PendingIntent} to fire when users trigger this action
1028 */
Dan Sandler86647982015-05-13 23:41:13 -04001029 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001030 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -04001031 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
1032 }
1033
1034 /**
1035 * Construct a new builder for {@link Action} object.
1036 * @param icon icon to show for this action
1037 * @param title the title of the action
1038 * @param intent the {@link PendingIntent} to fire when users trigger this action
1039 */
1040 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001041 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001042 }
1043
1044 /**
1045 * Construct a new builder for {@link Action} object using the fields from an
1046 * {@link Action}.
1047 * @param action the action to read fields from.
1048 */
1049 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001050 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001051 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001052 }
1053
Dan Sandler86647982015-05-13 23:41:13 -04001054 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001055 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001056 mIcon = icon;
1057 mTitle = title;
1058 mIntent = intent;
1059 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001060 if (remoteInputs != null) {
1061 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1062 Collections.addAll(mRemoteInputs, remoteInputs);
1063 }
Griff Hazen959591e2014-05-15 22:26:18 -07001064 }
1065
1066 /**
1067 * Merge additional metadata into this builder.
1068 *
1069 * <p>Values within the Bundle will replace existing extras values in this Builder.
1070 *
1071 * @see Notification.Action#extras
1072 */
1073 public Builder addExtras(Bundle extras) {
1074 if (extras != null) {
1075 mExtras.putAll(extras);
1076 }
1077 return this;
1078 }
1079
1080 /**
1081 * Get the metadata Bundle used by this Builder.
1082 *
1083 * <p>The returned Bundle is shared with this Builder.
1084 */
1085 public Bundle getExtras() {
1086 return mExtras;
1087 }
1088
1089 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001090 * Add an input to be collected from the user when this action is sent.
1091 * Response values can be retrieved from the fired intent by using the
1092 * {@link RemoteInput#getResultsFromIntent} function.
1093 * @param remoteInput a {@link RemoteInput} to add to the action
1094 * @return this object for method chaining
1095 */
1096 public Builder addRemoteInput(RemoteInput remoteInput) {
1097 if (mRemoteInputs == null) {
1098 mRemoteInputs = new ArrayList<RemoteInput>();
1099 }
1100 mRemoteInputs.add(remoteInput);
1101 return this;
1102 }
1103
1104 /**
1105 * Apply an extender to this action builder. Extenders may be used to add
1106 * metadata or change options on this builder.
1107 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001108 public Builder extend(Extender extender) {
1109 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001110 return this;
1111 }
1112
1113 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001114 * Combine all of the options that have been set and return a new {@link Action}
1115 * object.
1116 * @return the built action
1117 */
1118 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001119 RemoteInput[] remoteInputs = mRemoteInputs != null
1120 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1121 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
Griff Hazen959591e2014-05-15 22:26:18 -07001122 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001123 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001124
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001125 @Override
1126 public Action clone() {
1127 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001128 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001129 title,
1130 actionIntent, // safe to alias
1131 new Bundle(mExtras),
1132 getRemoteInputs());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001133 }
1134 @Override
1135 public int describeContents() {
1136 return 0;
1137 }
1138 @Override
1139 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001140 final Icon ic = getIcon();
1141 if (ic != null) {
1142 out.writeInt(1);
1143 ic.writeToParcel(out, 0);
1144 } else {
1145 out.writeInt(0);
1146 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001147 TextUtils.writeToParcel(title, out, flags);
1148 if (actionIntent != null) {
1149 out.writeInt(1);
1150 actionIntent.writeToParcel(out, flags);
1151 } else {
1152 out.writeInt(0);
1153 }
Griff Hazen959591e2014-05-15 22:26:18 -07001154 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001155 out.writeTypedArray(mRemoteInputs, flags);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001156 }
Griff Hazen959591e2014-05-15 22:26:18 -07001157 public static final Parcelable.Creator<Action> CREATOR =
1158 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001159 public Action createFromParcel(Parcel in) {
1160 return new Action(in);
1161 }
1162 public Action[] newArray(int size) {
1163 return new Action[size];
1164 }
1165 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001166
1167 /**
1168 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1169 * metadata or change options on an action builder.
1170 */
1171 public interface Extender {
1172 /**
1173 * Apply this extender to a notification action builder.
1174 * @param builder the builder to be modified.
1175 * @return the build object for chaining.
1176 */
1177 public Builder extend(Builder builder);
1178 }
1179
1180 /**
1181 * Wearable extender for notification actions. To add extensions to an action,
1182 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1183 * the {@code WearableExtender()} constructor and apply it to a
1184 * {@link android.app.Notification.Action.Builder} using
1185 * {@link android.app.Notification.Action.Builder#extend}.
1186 *
1187 * <pre class="prettyprint">
1188 * Notification.Action action = new Notification.Action.Builder(
1189 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001190 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001191 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001192 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001193 */
1194 public static final class WearableExtender implements Extender {
1195 /** Notification action extra which contains wearable extensions */
1196 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1197
Pete Gastaf6781d2014-10-07 15:17:05 -04001198 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001199 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001200 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1201 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1202 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001203
1204 // Flags bitwise-ored to mFlags
1205 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1206
1207 // Default value for flags integer
1208 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1209
1210 private int mFlags = DEFAULT_FLAGS;
1211
Pete Gastaf6781d2014-10-07 15:17:05 -04001212 private CharSequence mInProgressLabel;
1213 private CharSequence mConfirmLabel;
1214 private CharSequence mCancelLabel;
1215
Griff Hazen61a9e862014-05-22 16:05:19 -07001216 /**
1217 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1218 * options.
1219 */
1220 public WearableExtender() {
1221 }
1222
1223 /**
1224 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1225 * wearable options present in an existing notification action.
1226 * @param action the notification action to inspect.
1227 */
1228 public WearableExtender(Action action) {
1229 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1230 if (wearableBundle != null) {
1231 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001232 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1233 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1234 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001235 }
1236 }
1237
1238 /**
1239 * Apply wearable extensions to a notification action that is being built. This is
1240 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1241 * method of {@link android.app.Notification.Action.Builder}.
1242 */
1243 @Override
1244 public Action.Builder extend(Action.Builder builder) {
1245 Bundle wearableBundle = new Bundle();
1246
1247 if (mFlags != DEFAULT_FLAGS) {
1248 wearableBundle.putInt(KEY_FLAGS, mFlags);
1249 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001250 if (mInProgressLabel != null) {
1251 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1252 }
1253 if (mConfirmLabel != null) {
1254 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1255 }
1256 if (mCancelLabel != null) {
1257 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1258 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001259
1260 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1261 return builder;
1262 }
1263
1264 @Override
1265 public WearableExtender clone() {
1266 WearableExtender that = new WearableExtender();
1267 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001268 that.mInProgressLabel = this.mInProgressLabel;
1269 that.mConfirmLabel = this.mConfirmLabel;
1270 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001271 return that;
1272 }
1273
1274 /**
1275 * Set whether this action is available when the wearable device is not connected to
1276 * a companion device. The user can still trigger this action when the wearable device is
1277 * offline, but a visual hint will indicate that the action may not be available.
1278 * Defaults to true.
1279 */
1280 public WearableExtender setAvailableOffline(boolean availableOffline) {
1281 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1282 return this;
1283 }
1284
1285 /**
1286 * Get whether this action is available when the wearable device is not connected to
1287 * a companion device. The user can still trigger this action when the wearable device is
1288 * offline, but a visual hint will indicate that the action may not be available.
1289 * Defaults to true.
1290 */
1291 public boolean isAvailableOffline() {
1292 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1293 }
1294
1295 private void setFlag(int mask, boolean value) {
1296 if (value) {
1297 mFlags |= mask;
1298 } else {
1299 mFlags &= ~mask;
1300 }
1301 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001302
1303 /**
1304 * Set a label to display while the wearable is preparing to automatically execute the
1305 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1306 *
1307 * @param label the label to display while the action is being prepared to execute
1308 * @return this object for method chaining
1309 */
1310 public WearableExtender setInProgressLabel(CharSequence label) {
1311 mInProgressLabel = label;
1312 return this;
1313 }
1314
1315 /**
1316 * Get the label to display while the wearable is preparing to automatically execute
1317 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1318 *
1319 * @return the label to display while the action is being prepared to execute
1320 */
1321 public CharSequence getInProgressLabel() {
1322 return mInProgressLabel;
1323 }
1324
1325 /**
1326 * Set a label to display to confirm that the action should be executed.
1327 * This is usually an imperative verb like "Send".
1328 *
1329 * @param label the label to confirm the action should be executed
1330 * @return this object for method chaining
1331 */
1332 public WearableExtender setConfirmLabel(CharSequence label) {
1333 mConfirmLabel = label;
1334 return this;
1335 }
1336
1337 /**
1338 * Get the label to display to confirm that the action should be executed.
1339 * This is usually an imperative verb like "Send".
1340 *
1341 * @return the label to confirm the action should be executed
1342 */
1343 public CharSequence getConfirmLabel() {
1344 return mConfirmLabel;
1345 }
1346
1347 /**
1348 * Set a label to display to cancel the action.
1349 * This is usually an imperative verb, like "Cancel".
1350 *
1351 * @param label the label to display to cancel the action
1352 * @return this object for method chaining
1353 */
1354 public WearableExtender setCancelLabel(CharSequence label) {
1355 mCancelLabel = label;
1356 return this;
1357 }
1358
1359 /**
1360 * Get the label to display to cancel the action.
1361 * This is usually an imperative verb like "Cancel".
1362 *
1363 * @return the label to display to cancel the action
1364 */
1365 public CharSequence getCancelLabel() {
1366 return mCancelLabel;
1367 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001368 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001369 }
1370
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001371 /**
1372 * Array of all {@link Action} structures attached to this notification by
1373 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1374 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1375 * interface for invoking actions.
1376 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001377 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001378
1379 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001380 * Replacement version of this notification whose content will be shown
1381 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1382 * and {@link #VISIBILITY_PUBLIC}.
1383 */
1384 public Notification publicVersion;
1385
1386 /**
Julia Reynolds74303cf2015-10-16 11:37:55 -04001387 * Structure to encapsulate a topic that is shown in Notification settings.
1388 * It must include an id and label.
1389 */
1390 public static class Topic implements Parcelable {
1391 private final String id;
1392 private final CharSequence label;
1393
1394 public Topic(String id, CharSequence label) {
1395 this.id = id;
1396 this.label = safeCharSequence(label);
1397 }
1398
1399 private Topic(Parcel in) {
1400 if (in.readInt() != 0) {
1401 id = in.readString();
1402 } else {
1403 id = null;
1404 }
1405 label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1406 }
1407
1408 public String getId() {
1409 return id;
1410 }
1411
1412 public CharSequence getLabel() {
1413 return label;
1414 }
1415
1416 @Override
1417 public String toString() {
1418 return new StringBuilder(Topic.class.getSimpleName()).append('[')
1419 .append("id=").append(id)
1420 .append(",label=").append(label)
1421 .append(']').toString();
1422 }
1423
1424 @Override
1425 public boolean equals(Object o) {
1426 if (!(o instanceof Topic)) return false;
1427 if (o == this) return true;
1428 final Topic other = (Topic) o;
1429 return Objects.equals(other.id, id)
1430 && Objects.equals(other.label, label);
1431 }
1432
1433 @Override
1434 public int hashCode() {
1435 return Objects.hash(id, label);
1436 }
1437
1438 @Override
1439 public Topic clone() {
1440 return new Topic(id, label);
1441 }
1442
1443 @Override
1444 public int describeContents() {
1445 return 0;
1446 }
1447
1448 @Override
1449 public void writeToParcel(Parcel out, int flags) {
1450 if (id != null) {
1451 out.writeInt(1);
1452 out.writeString(id);
1453 } else {
1454 out.writeInt(0);
1455 }
1456 TextUtils.writeToParcel(label, out, flags);
1457 }
1458 public static final Parcelable.Creator<Topic> CREATOR =
1459 new Parcelable.Creator<Topic>() {
1460 public Topic createFromParcel(Parcel in) {
1461 return new Topic(in);
1462 }
1463 public Topic[] newArray(int size) {
1464 return new Topic[size];
1465 }
1466 };
1467 }
1468
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001469 private Topic topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001470
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001471 public Topic getTopic() {
1472 return topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001473 }
1474
1475 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001476 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001477 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 */
1479 public Notification()
1480 {
1481 this.when = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001482 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 }
1484
1485 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001486 * @hide
1487 */
1488 public Notification(Context context, int icon, CharSequence tickerText, long when,
1489 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1490 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001491 new Builder(context)
1492 .setWhen(when)
1493 .setSmallIcon(icon)
1494 .setTicker(tickerText)
1495 .setContentTitle(contentTitle)
1496 .setContentText(contentText)
1497 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1498 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 }
1500
1501 /**
1502 * Constructs a Notification object with the information needed to
1503 * have a status bar icon without the standard expanded view.
1504 *
1505 * @param icon The resource id of the icon to put in the status bar.
1506 * @param tickerText The text that flows by in the status bar when the notification first
1507 * activates.
1508 * @param when The time to show in the time field. In the System.currentTimeMillis
1509 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001510 *
1511 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001512 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001513 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001514 public Notification(int icon, CharSequence tickerText, long when)
1515 {
1516 this.icon = icon;
1517 this.tickerText = tickerText;
1518 this.when = when;
1519 }
1520
1521 /**
1522 * Unflatten the notification from a parcel.
1523 */
1524 public Notification(Parcel parcel)
1525 {
1526 int version = parcel.readInt();
1527
1528 when = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001529 if (parcel.readInt() != 0) {
1530 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001531 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1532 icon = mSmallIcon.getResId();
1533 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001534 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 number = parcel.readInt();
1536 if (parcel.readInt() != 0) {
1537 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1538 }
1539 if (parcel.readInt() != 0) {
1540 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1541 }
1542 if (parcel.readInt() != 0) {
1543 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1544 }
1545 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001546 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001547 }
1548 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1550 }
Joe Onorato561d3852010-11-20 18:09:34 -08001551 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001552 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001553 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 defaults = parcel.readInt();
1555 flags = parcel.readInt();
1556 if (parcel.readInt() != 0) {
1557 sound = Uri.CREATOR.createFromParcel(parcel);
1558 }
1559
1560 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001561 if (parcel.readInt() != 0) {
1562 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1563 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 vibrate = parcel.createLongArray();
1565 ledARGB = parcel.readInt();
1566 ledOnMS = parcel.readInt();
1567 ledOffMS = parcel.readInt();
1568 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001569
1570 if (parcel.readInt() != 0) {
1571 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1572 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001573
1574 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001575
John Spurlockfd7f1e02014-03-18 16:41:57 -04001576 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001577
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001578 mGroupKey = parcel.readString();
1579
1580 mSortKey = parcel.readString();
1581
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001582 extras = parcel.readBundle(); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001583
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001584 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1585
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001586 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001587 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1588 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001589
Chris Wren8fd39ec2014-02-27 17:43:26 -05001590 if (parcel.readInt() != 0) {
1591 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1592 }
1593
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001594 visibility = parcel.readInt();
1595
1596 if (parcel.readInt() != 0) {
1597 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1598 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001599
1600 color = parcel.readInt();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001601
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001602 if (parcel.readInt() != 0) {
1603 topic = Topic.CREATOR.createFromParcel(parcel);
1604 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 }
1606
Andy Stadler110988c2010-12-03 14:29:16 -08001607 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001608 public Notification clone() {
1609 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001610 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001611 return that;
1612 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001613
Daniel Sandler1a497d32013-04-18 14:52:45 -04001614 /**
1615 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1616 * of this into that.
1617 * @hide
1618 */
1619 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001620 that.when = this.when;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001621 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001622 that.number = this.number;
1623
1624 // PendingIntents are global, so there's no reason (or way) to clone them.
1625 that.contentIntent = this.contentIntent;
1626 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001627 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001628
1629 if (this.tickerText != null) {
1630 that.tickerText = this.tickerText.toString();
1631 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001632 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001633 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001634 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001635 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001636 that.contentView = this.contentView.clone();
1637 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001638 if (heavy && this.mLargeIcon != null) {
1639 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001640 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001641 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001642 that.sound = this.sound; // android.net.Uri is immutable
1643 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001644 if (this.audioAttributes != null) {
1645 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1646 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001647
1648 final long[] vibrate = this.vibrate;
1649 if (vibrate != null) {
1650 final int N = vibrate.length;
1651 final long[] vib = that.vibrate = new long[N];
1652 System.arraycopy(vibrate, 0, vib, 0, N);
1653 }
1654
1655 that.ledARGB = this.ledARGB;
1656 that.ledOnMS = this.ledOnMS;
1657 that.ledOffMS = this.ledOffMS;
1658 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001659
Joe Onorato18e69df2010-05-17 22:26:12 -07001660 that.flags = this.flags;
1661
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001662 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001663
John Spurlockfd7f1e02014-03-18 16:41:57 -04001664 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001665
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001666 that.mGroupKey = this.mGroupKey;
1667
1668 that.mSortKey = this.mSortKey;
1669
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001670 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001671 try {
1672 that.extras = new Bundle(this.extras);
1673 // will unparcel
1674 that.extras.size();
1675 } catch (BadParcelableException e) {
1676 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1677 that.extras = null;
1678 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001679 }
1680
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001681 if (this.actions != null) {
1682 that.actions = new Action[this.actions.length];
1683 for(int i=0; i<this.actions.length; i++) {
1684 that.actions[i] = this.actions[i].clone();
1685 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001686 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001687
Daniel Sandler1a497d32013-04-18 14:52:45 -04001688 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001689 that.bigContentView = this.bigContentView.clone();
1690 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001691
Chris Wren8fd39ec2014-02-27 17:43:26 -05001692 if (heavy && this.headsUpContentView != null) {
1693 that.headsUpContentView = this.headsUpContentView.clone();
1694 }
1695
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001696 that.visibility = this.visibility;
1697
1698 if (this.publicVersion != null) {
1699 that.publicVersion = new Notification();
1700 this.publicVersion.cloneInto(that.publicVersion, heavy);
1701 }
1702
Dan Sandler26e81cf2014-05-06 10:01:27 -04001703 that.color = this.color;
1704
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001705 if (this.topic != null) {
1706 that.topic = this.topic.clone();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001707 }
1708
Daniel Sandler1a497d32013-04-18 14:52:45 -04001709 if (!heavy) {
1710 that.lightenPayload(); // will clean out extras
1711 }
1712 }
1713
1714 /**
1715 * Removes heavyweight parts of the Notification object for archival or for sending to
1716 * listeners when the full contents are not necessary.
1717 * @hide
1718 */
1719 public final void lightenPayload() {
1720 tickerView = null;
1721 contentView = null;
1722 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001723 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001724 mLargeIcon = null;
Daniel Sandler1a497d32013-04-18 14:52:45 -04001725 if (extras != null) {
1726 extras.remove(Notification.EXTRA_LARGE_ICON);
1727 extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1728 extras.remove(Notification.EXTRA_PICTURE);
Christoph Studer223f44e2014-09-02 14:59:32 +02001729 extras.remove(Notification.EXTRA_BIG_TEXT);
Daniel Sandler1a497d32013-04-18 14:52:45 -04001730 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001731 }
1732
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001733 /**
1734 * Make sure this CharSequence is safe to put into a bundle, which basically
1735 * means it had better not be some custom Parcelable implementation.
1736 * @hide
1737 */
1738 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001739 if (cs == null) return cs;
1740 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1741 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1742 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001743 if (cs instanceof Parcelable) {
1744 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1745 + " instance is a custom Parcelable and not allowed in Notification");
1746 return cs.toString();
1747 }
1748
1749 return cs;
1750 }
1751
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 public int describeContents() {
1753 return 0;
1754 }
1755
1756 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001757 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758 */
1759 public void writeToParcel(Parcel parcel, int flags)
1760 {
1761 parcel.writeInt(1);
1762
1763 parcel.writeLong(when);
Dan Sandler4e787062015-06-17 15:09:48 -04001764 if (mSmallIcon == null && icon != 0) {
1765 // you snuck an icon in here without using the builder; let's try to keep it
1766 mSmallIcon = Icon.createWithResource("", icon);
1767 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001768 if (mSmallIcon != null) {
1769 parcel.writeInt(1);
1770 mSmallIcon.writeToParcel(parcel, 0);
1771 } else {
1772 parcel.writeInt(0);
1773 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001774 parcel.writeInt(number);
1775 if (contentIntent != null) {
1776 parcel.writeInt(1);
1777 contentIntent.writeToParcel(parcel, 0);
1778 } else {
1779 parcel.writeInt(0);
1780 }
1781 if (deleteIntent != null) {
1782 parcel.writeInt(1);
1783 deleteIntent.writeToParcel(parcel, 0);
1784 } else {
1785 parcel.writeInt(0);
1786 }
1787 if (tickerText != null) {
1788 parcel.writeInt(1);
1789 TextUtils.writeToParcel(tickerText, parcel, flags);
1790 } else {
1791 parcel.writeInt(0);
1792 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001793 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001794 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001795 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001796 } else {
1797 parcel.writeInt(0);
1798 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001799 if (contentView != null) {
1800 parcel.writeInt(1);
1801 contentView.writeToParcel(parcel, 0);
1802 } else {
1803 parcel.writeInt(0);
1804 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001805 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001806 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001807 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001808 } else {
1809 parcel.writeInt(0);
1810 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811
1812 parcel.writeInt(defaults);
1813 parcel.writeInt(this.flags);
1814
1815 if (sound != null) {
1816 parcel.writeInt(1);
1817 sound.writeToParcel(parcel, 0);
1818 } else {
1819 parcel.writeInt(0);
1820 }
1821 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001822
1823 if (audioAttributes != null) {
1824 parcel.writeInt(1);
1825 audioAttributes.writeToParcel(parcel, 0);
1826 } else {
1827 parcel.writeInt(0);
1828 }
1829
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 parcel.writeLongArray(vibrate);
1831 parcel.writeInt(ledARGB);
1832 parcel.writeInt(ledOnMS);
1833 parcel.writeInt(ledOffMS);
1834 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001835
1836 if (fullScreenIntent != null) {
1837 parcel.writeInt(1);
1838 fullScreenIntent.writeToParcel(parcel, 0);
1839 } else {
1840 parcel.writeInt(0);
1841 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001842
1843 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001844
John Spurlockfd7f1e02014-03-18 16:41:57 -04001845 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001846
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001847 parcel.writeString(mGroupKey);
1848
1849 parcel.writeString(mSortKey);
1850
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001851 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001852
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001853 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001854
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001855 if (bigContentView != null) {
1856 parcel.writeInt(1);
1857 bigContentView.writeToParcel(parcel, 0);
1858 } else {
1859 parcel.writeInt(0);
1860 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001861
Chris Wren8fd39ec2014-02-27 17:43:26 -05001862 if (headsUpContentView != null) {
1863 parcel.writeInt(1);
1864 headsUpContentView.writeToParcel(parcel, 0);
1865 } else {
1866 parcel.writeInt(0);
1867 }
1868
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001869 parcel.writeInt(visibility);
1870
1871 if (publicVersion != null) {
1872 parcel.writeInt(1);
1873 publicVersion.writeToParcel(parcel, 0);
1874 } else {
1875 parcel.writeInt(0);
1876 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001877
1878 parcel.writeInt(color);
Julia Reynolds74303cf2015-10-16 11:37:55 -04001879
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001880 if (topic != null) {
1881 parcel.writeInt(1);
1882 topic.writeToParcel(parcel, 0);
1883 } else {
1884 parcel.writeInt(0);
1885 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 }
1887
1888 /**
1889 * Parcelable.Creator that instantiates Notification objects
1890 */
1891 public static final Parcelable.Creator<Notification> CREATOR
1892 = new Parcelable.Creator<Notification>()
1893 {
1894 public Notification createFromParcel(Parcel parcel)
1895 {
1896 return new Notification(parcel);
1897 }
1898
1899 public Notification[] newArray(int size)
1900 {
1901 return new Notification[size];
1902 }
1903 };
1904
1905 /**
1906 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1907 * layout.
1908 *
1909 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1910 * in the view.</p>
1911 * @param context The context for your application / activity.
1912 * @param contentTitle The title that goes in the expanded entry.
1913 * @param contentText The text that goes in the expanded entry.
1914 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1915 * If this is an activity, it must include the
1916 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001917 * that you take care of task management as described in the
1918 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1919 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001920 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001921 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001922 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001923 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001924 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001925 public void setLatestEventInfo(Context context,
1926 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001927 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1928 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1929 new Throwable());
1930 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001931
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001932 // ensure that any information already set directly is preserved
1933 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001934
1935 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001936 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001937 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001938 }
1939 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001940 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001942 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001943
1944 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001945 }
1946
1947 @Override
1948 public String toString() {
1949 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001950 sb.append("Notification(pri=");
1951 sb.append(priority);
1952 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08001953 if (contentView != null) {
1954 sb.append(contentView.getPackage());
1955 sb.append("/0x");
1956 sb.append(Integer.toHexString(contentView.getLayoutId()));
1957 } else {
1958 sb.append("null");
1959 }
1960 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001961 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1962 sb.append("default");
1963 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001964 int N = this.vibrate.length-1;
1965 sb.append("[");
1966 for (int i=0; i<N; i++) {
1967 sb.append(this.vibrate[i]);
1968 sb.append(',');
1969 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02001970 if (N != -1) {
1971 sb.append(this.vibrate[N]);
1972 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001974 } else {
1975 sb.append("null");
1976 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001977 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001978 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001979 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001980 } else if (this.sound != null) {
1981 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001982 } else {
1983 sb.append("null");
1984 }
Chris Wren365b6d32015-07-16 10:39:26 -04001985 if (this.tickerText != null) {
1986 sb.append(" tick");
1987 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001988 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001989 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001990 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001991 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04001992 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001993 if (this.category != null) {
1994 sb.append(" category=");
1995 sb.append(this.category);
1996 }
1997 if (this.mGroupKey != null) {
1998 sb.append(" groupKey=");
1999 sb.append(this.mGroupKey);
2000 }
2001 if (this.mSortKey != null) {
2002 sb.append(" sortKey=");
2003 sb.append(this.mSortKey);
2004 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002005 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002006 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002007 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002008 }
2009 sb.append(" vis=");
2010 sb.append(visibilityToString(this.visibility));
2011 if (this.publicVersion != null) {
2012 sb.append(" publicVersion=");
2013 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002014 }
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002015 if (topic != null) {
2016 sb.append("topic=");
2017 sb.append(topic.toString());
Julia Reynolds74303cf2015-10-16 11:37:55 -04002018 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002019 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002020 return sb.toString();
2021 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002022
Dan Sandler1b718782014-07-18 12:43:45 -04002023 /**
2024 * {@hide}
2025 */
2026 public static String visibilityToString(int vis) {
2027 switch (vis) {
2028 case VISIBILITY_PRIVATE:
2029 return "PRIVATE";
2030 case VISIBILITY_PUBLIC:
2031 return "PUBLIC";
2032 case VISIBILITY_SECRET:
2033 return "SECRET";
2034 default:
2035 return "UNKNOWN(" + String.valueOf(vis) + ")";
2036 }
2037 }
2038
Joe Onoratocb109a02011-01-18 17:57:41 -08002039 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002040 * {@hide}
2041 */
2042 public static String priorityToString(@Priority int pri) {
2043 switch (pri) {
2044 case PRIORITY_MIN:
2045 return "MIN";
2046 case PRIORITY_LOW:
2047 return "LOW";
2048 case PRIORITY_DEFAULT:
2049 return "DEFAULT";
2050 case PRIORITY_HIGH:
2051 return "HIGH";
2052 case PRIORITY_MAX:
2053 return "MAX";
2054 default:
2055 return "UNKNOWN(" + String.valueOf(pri) + ")";
2056 }
2057 }
2058
2059 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002060 * The small icon representing this notification in the status bar and content view.
2061 *
2062 * @return the small icon representing this notification.
2063 *
2064 * @see Builder#getSmallIcon()
2065 * @see Builder#setSmallIcon(Icon)
2066 */
2067 public Icon getSmallIcon() {
2068 return mSmallIcon;
2069 }
2070
2071 /**
2072 * Used when notifying to clean up legacy small icons.
2073 * @hide
2074 */
2075 public void setSmallIcon(Icon icon) {
2076 mSmallIcon = icon;
2077 }
2078
2079 /**
2080 * The large icon shown in this notification's content view.
2081 * @see Builder#getLargeIcon()
2082 * @see Builder#setLargeIcon(Icon)
2083 */
2084 public Icon getLargeIcon() {
2085 return mLargeIcon;
2086 }
2087
2088 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002089 * @hide
2090 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002091 public boolean isGroupSummary() {
2092 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2093 }
2094
2095 /**
2096 * @hide
2097 */
2098 public boolean isGroupChild() {
2099 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2100 }
2101
2102 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002103 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002104 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002105 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002106 * content views using the platform's notification layout template. If your app supports
2107 * versions of Android as old as API level 4, you can instead use
2108 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2109 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2110 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002111 *
Scott Main183bf112012-08-13 19:12:13 -07002112 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002113 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002114 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002115 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002116 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2117 * .setContentText(subject)
2118 * .setSmallIcon(R.drawable.new_mail)
2119 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002120 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002121 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002122 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002123 public static class Builder {
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002124 private static final int MAX_ACTION_BUTTONS = 3;
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002125 private static final float LARGE_TEXT_SCALE = 1.3f;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002126
Joe Onorato46439ce2010-11-19 13:56:21 -08002127 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002128 private Notification mN;
2129 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002130 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002131 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2132 private ArrayList<String> mPersonList = new ArrayList<String>();
2133 private NotificationColorUtil mColorUtil;
2134 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002135
2136 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +01002137 * The user that built the notification originally.
2138 */
2139 private int mOriginatingUserId;
2140
2141 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002142 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002143 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002144
2145 * <table>
2146 * <tr><th align=right>priority</th>
2147 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2148 * <tr><th align=right>when</th>
2149 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2150 * <tr><th align=right>audio stream</th>
2151 * <td>{@link #STREAM_DEFAULT}</td></tr>
2152 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002153 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002154
2155 * @param context
2156 * A {@link Context} that will be used by the Builder to construct the
2157 * RemoteViews. The Context will not be held past the lifetime of this Builder
2158 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002159 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002160 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002161 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002162 }
2163
Joe Onoratocb109a02011-01-18 17:57:41 -08002164 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002165 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002166 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002167 public Builder(Context context, Notification toAdopt) {
2168 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002169
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002170 if (toAdopt == null) {
2171 mN = new Notification();
2172 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2173 mN.priority = PRIORITY_DEFAULT;
2174 mN.visibility = VISIBILITY_PRIVATE;
2175 } else {
2176 mN = toAdopt;
2177 if (mN.actions != null) {
2178 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002179 }
2180
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002181 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2182 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2183 }
2184
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002185 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2186 if (!TextUtils.isEmpty(templateClass)) {
2187 final Class<? extends Style> styleClass
2188 = getNotificationStyleClass(templateClass);
2189 if (styleClass == null) {
2190 Log.d(TAG, "Unknown style class: " + templateClass);
2191 } else {
2192 try {
2193 final Constructor<? extends Style> ctor = styleClass.getConstructor();
2194 ctor.setAccessible(true);
2195 final Style style = ctor.newInstance();
2196 style.restoreFromExtras(mN.extras);
2197
2198 if (style != null) {
2199 setStyle(style);
2200 }
2201 } catch (Throwable t) {
2202 Log.e(TAG, "Could not create Style", t);
2203 }
2204 }
2205 }
2206
2207 }
2208 }
2209
2210 private NotificationColorUtil getColorUtil() {
2211 if (!mColorUtilInited) {
2212 mColorUtilInited = true;
2213 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2214 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002215 }
2216 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002217 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002218 }
2219
2220 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002221 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Daniel Sandler0c890492012-09-12 17:23:10 -07002222 * It will be shown in the notification content view by default; use
Griff Hazen50c11652014-05-16 09:46:31 -07002223 * {@link #setShowWhen(boolean) setShowWhen} to control this.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002224 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002225 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002226 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002227 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002228 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002229 return this;
2230 }
2231
Joe Onoratocb109a02011-01-18 17:57:41 -08002232 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002233 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002234 * in the content view.
2235 */
2236 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002237 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002238 return this;
2239 }
2240
2241 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002242 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002243 *
2244 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002245 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002246 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002247 * Useful when showing an elapsed time (like an ongoing phone call).
2248 *
2249 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002250 * @see Notification#when
2251 */
2252 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002253 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002254 return this;
2255 }
2256
2257 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002258 * Set the small icon resource, which will be used to represent the notification in the
2259 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002260 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002261
2262 * The platform template for the expanded view will draw this icon in the left, unless a
2263 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2264 * icon will be moved to the right-hand side.
2265 *
2266
2267 * @param icon
2268 * A resource ID in the application's package of the drawable to use.
2269 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002270 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002271 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002272 return setSmallIcon(icon != 0
2273 ? Icon.createWithResource(mContext, icon)
2274 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002275 }
2276
Joe Onoratocb109a02011-01-18 17:57:41 -08002277 /**
2278 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2279 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2280 * LevelListDrawable}.
2281 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002282 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002283 * @param level The level to use for the icon.
2284 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002285 * @see Notification#icon
2286 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002287 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002288 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002289 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002290 return setSmallIcon(icon);
2291 }
2292
2293 /**
2294 * Set the small icon, which will be used to represent the notification in the
2295 * status bar and content view (unless overriden there by a
2296 * {@link #setLargeIcon(Bitmap) large icon}).
2297 *
2298 * @param icon An Icon object to use.
2299 * @see Notification#icon
2300 */
2301 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002302 mN.setSmallIcon(icon);
2303 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2304 mN.icon = icon.getResId();
2305 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002306 return this;
2307 }
2308
Joe Onoratocb109a02011-01-18 17:57:41 -08002309 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002310 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002311 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002312 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002313 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002314 return this;
2315 }
2316
Joe Onoratocb109a02011-01-18 17:57:41 -08002317 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002318 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002319 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002320 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002321 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002322 return this;
2323 }
2324
Joe Onoratocb109a02011-01-18 17:57:41 -08002325 /**
Joe Malin8d40d042012-11-05 11:36:40 -08002326 * Set the third line of text in the platform notification template.
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002327 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2328 * same location in the standard template.
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002329 */
2330 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002331 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002332 return this;
2333 }
2334
2335 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002336 * Set the large number at the right-hand side of the notification. This is
2337 * equivalent to setContentInfo, although it might show the number in a different
2338 * font size for readability.
2339 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002340 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002341 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002342 return this;
2343 }
2344
Joe Onoratocb109a02011-01-18 17:57:41 -08002345 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002346 * A small piece of additional information pertaining to this notification.
2347 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002348 * The platform template will draw this on the last line of the notification, at the far
2349 * right (to the right of a smallIcon if it has been placed there).
Joe Onoratocb109a02011-01-18 17:57:41 -08002350 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002351 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002352 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002353 return this;
2354 }
2355
Joe Onoratocb109a02011-01-18 17:57:41 -08002356 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002357 * Set the progress this notification represents.
2358 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002359 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002360 */
2361 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002362 mN.extras.putInt(EXTRA_PROGRESS, progress);
2363 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2364 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002365 return this;
2366 }
2367
2368 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002369 * Supply a custom RemoteViews to use instead of the platform template.
2370 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002371 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002372 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002373 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002374 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002375 return setCustomContentView(views);
2376 }
2377
2378 /**
2379 * Supply custom RemoteViews to use instead of the platform template.
2380 *
2381 * This will override the layout that would otherwise be constructed by this Builder
2382 * object.
2383 */
2384 public Builder setCustomContentView(RemoteViews contentView) {
2385 mN.contentView = contentView;
2386 return this;
2387 }
2388
2389 /**
2390 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2391 *
2392 * This will override the expanded layout that would otherwise be constructed by this
2393 * Builder object.
2394 */
2395 public Builder setCustomBigContentView(RemoteViews contentView) {
2396 mN.bigContentView = contentView;
2397 return this;
2398 }
2399
2400 /**
2401 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2402 *
2403 * This will override the heads-up layout that would otherwise be constructed by this
2404 * Builder object.
2405 */
2406 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2407 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002408 return this;
2409 }
2410
Joe Onoratocb109a02011-01-18 17:57:41 -08002411 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002412 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2413 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002414 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2415 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2416 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002417 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002418 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002419 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002420 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002421 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002422 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002423 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002424 return this;
2425 }
2426
Joe Onoratocb109a02011-01-18 17:57:41 -08002427 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002428 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2429 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002430 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002431 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002432 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002433 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002434 return this;
2435 }
2436
Joe Onoratocb109a02011-01-18 17:57:41 -08002437 /**
2438 * An intent to launch instead of posting the notification to the status bar.
2439 * Only for use with extremely high-priority notifications demanding the user's
2440 * <strong>immediate</strong> attention, such as an incoming phone call or
2441 * alarm clock that the user has explicitly set to a particular time.
2442 * If this facility is used for something else, please give the user an option
2443 * to turn it off and use a normal notification, as this can be extremely
2444 * disruptive.
2445 *
Chris Wren47c20a12014-06-18 17:27:29 -04002446 * <p>
2447 * The system UI may choose to display a heads-up notification, instead of
2448 * launching this intent, while the user is using the device.
2449 * </p>
2450 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002451 * @param intent The pending intent to launch.
2452 * @param highPriority Passing true will cause this notification to be sent
2453 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002454 *
2455 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002456 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002457 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002458 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002459 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2460 return this;
2461 }
2462
Joe Onoratocb109a02011-01-18 17:57:41 -08002463 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002464 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002465 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002466 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002467 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002468 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002469 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002470 return this;
2471 }
2472
Joe Onoratocb109a02011-01-18 17:57:41 -08002473 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002474 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002475 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002476 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002477 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002478 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002479 setTicker(tickerText);
2480 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002481 return this;
2482 }
2483
Joe Onoratocb109a02011-01-18 17:57:41 -08002484 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002485 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002486 *
2487 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002488 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2489 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002490 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002491 public Builder setLargeIcon(Bitmap b) {
2492 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2493 }
2494
2495 /**
2496 * Add a large icon to the notification content view.
2497 *
2498 * In the platform template, this image will be shown on the left of the notification view
2499 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2500 * badge atop the large icon).
2501 */
2502 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002503 mN.mLargeIcon = icon;
2504 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002505 return this;
2506 }
2507
Joe Onoratocb109a02011-01-18 17:57:41 -08002508 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002509 * Set the sound to play.
2510 *
John Spurlockc0650f022014-07-19 13:22:39 -04002511 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2512 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002513 *
Chris Wren47c20a12014-06-18 17:27:29 -04002514 * <p>
2515 * A notification that is noisy is more likely to be presented as a heads-up notification.
2516 * </p>
2517 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002518 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002519 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002520 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002521 mN.sound = sound;
2522 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002523 return this;
2524 }
2525
Joe Onoratocb109a02011-01-18 17:57:41 -08002526 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002527 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002528 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002529 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2530 *
Chris Wren47c20a12014-06-18 17:27:29 -04002531 * <p>
2532 * A notification that is noisy is more likely to be presented as a heads-up notification.
2533 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002534 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002535 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002536 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002537 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002538 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002539 mN.sound = sound;
2540 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002541 return this;
2542 }
2543
Joe Onoratocb109a02011-01-18 17:57:41 -08002544 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002545 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2546 * use during playback.
2547 *
2548 * <p>
2549 * A notification that is noisy is more likely to be presented as a heads-up notification.
2550 * </p>
2551 *
2552 * @see Notification#sound
2553 */
2554 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002555 mN.sound = sound;
2556 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002557 return this;
2558 }
2559
2560 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002561 * Set the vibration pattern to use.
2562 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002563 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2564 * <code>pattern</code> parameter.
2565 *
Chris Wren47c20a12014-06-18 17:27:29 -04002566 * <p>
2567 * A notification that vibrates is more likely to be presented as a heads-up notification.
2568 * </p>
2569 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002570 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002571 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002572 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002573 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002574 return this;
2575 }
2576
Joe Onoratocb109a02011-01-18 17:57:41 -08002577 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002578 * Set the desired color for the indicator LED on the device, as well as the
2579 * blink duty cycle (specified in milliseconds).
2580 *
2581
2582 * Not all devices will honor all (or even any) of these values.
2583 *
2584
2585 * @see Notification#ledARGB
2586 * @see Notification#ledOnMS
2587 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002588 */
Tor Norbye80756e32015-03-02 09:39:27 -08002589 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002590 mN.ledARGB = argb;
2591 mN.ledOnMS = onMs;
2592 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002593 if (onMs != 0 || offMs != 0) {
2594 mN.flags |= FLAG_SHOW_LIGHTS;
2595 }
2596 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
2597 mN.flags |= FLAG_SHOW_LIGHTS;
2598 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002599 return this;
2600 }
2601
Joe Onoratocb109a02011-01-18 17:57:41 -08002602 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002603 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002604 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002605
2606 * Ongoing notifications cannot be dismissed by the user, so your application or service
2607 * must take care of canceling them.
2608 *
2609
2610 * They are typically used to indicate a background task that the user is actively engaged
2611 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2612 * (e.g., a file download, sync operation, active network connection).
2613 *
2614
2615 * @see Notification#FLAG_ONGOING_EVENT
2616 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002617 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002618 public Builder setOngoing(boolean ongoing) {
2619 setFlag(FLAG_ONGOING_EVENT, ongoing);
2620 return this;
2621 }
2622
Joe Onoratocb109a02011-01-18 17:57:41 -08002623 /**
2624 * Set this flag if you would only like the sound, vibrate
2625 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002626 *
2627 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002628 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002629 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2630 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2631 return this;
2632 }
2633
Joe Onoratocb109a02011-01-18 17:57:41 -08002634 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002635 * Make this notification automatically dismissed when the user touches it. The
2636 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2637 *
2638 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002639 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002640 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002641 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002642 return this;
2643 }
2644
Joe Onoratocb109a02011-01-18 17:57:41 -08002645 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002646 * Set whether or not this notification should not bridge to other devices.
2647 *
2648 * <p>Some notifications can be bridged to other devices for remote display.
2649 * This hint can be set to recommend this notification not be bridged.
2650 */
2651 public Builder setLocalOnly(boolean localOnly) {
2652 setFlag(FLAG_LOCAL_ONLY, localOnly);
2653 return this;
2654 }
2655
2656 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002657 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002658 * <p>
2659 * The value should be one or more of the following fields combined with
2660 * bitwise-or:
2661 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2662 * <p>
2663 * For all default values, use {@link #DEFAULT_ALL}.
2664 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002665 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002666 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002667 return this;
2668 }
2669
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002670 /**
2671 * Set the priority of this notification.
2672 *
2673 * @see Notification#priority
2674 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002675 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002676 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002677 return this;
2678 }
Joe Malin8d40d042012-11-05 11:36:40 -08002679
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002680 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002681 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002682 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002683 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002684 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002685 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002686 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002687 return this;
2688 }
2689
2690 /**
Chris Wrendde75302014-03-26 17:24:15 -04002691 * Add a person that is relevant to this notification.
2692 *
Chris Wrene6c48932014-09-29 17:19:27 -04002693 * <P>
2694 * Depending on user preferences, this annotation may allow the notification to pass
2695 * through interruption filters, and to appear more prominently in the user interface.
2696 * </P>
2697 *
2698 * <P>
2699 * The person should be specified by the {@code String} representation of a
2700 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2701 * </P>
2702 *
2703 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2704 * URIs. The path part of these URIs must exist in the contacts database, in the
2705 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2706 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2707 * </P>
2708 *
2709 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002710 * @see Notification#EXTRA_PEOPLE
2711 */
Chris Wrene6c48932014-09-29 17:19:27 -04002712 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002713 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002714 return this;
2715 }
2716
2717 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002718 * Set this notification to be part of a group of notifications sharing the same key.
2719 * Grouped notifications may display in a cluster or stack on devices which
2720 * support such rendering.
2721 *
2722 * <p>To make this notification the summary for its group, also call
2723 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2724 * {@link #setSortKey}.
2725 * @param groupKey The group key of the group.
2726 * @return this object for method chaining
2727 */
2728 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002729 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002730 return this;
2731 }
2732
2733 /**
2734 * Set this notification to be the group summary for a group of notifications.
2735 * Grouped notifications may display in a cluster or stack on devices which
2736 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2737 * @param isGroupSummary Whether this notification should be a group summary.
2738 * @return this object for method chaining
2739 */
2740 public Builder setGroupSummary(boolean isGroupSummary) {
2741 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2742 return this;
2743 }
2744
2745 /**
2746 * Set a sort key that orders this notification among other notifications from the
2747 * same package. This can be useful if an external sort was already applied and an app
2748 * would like to preserve this. Notifications will be sorted lexicographically using this
2749 * value, although providing different priorities in addition to providing sort key may
2750 * cause this value to be ignored.
2751 *
2752 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002753 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002754 *
2755 * @see String#compareTo(String)
2756 */
2757 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002758 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002759 return this;
2760 }
2761
2762 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002763 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002764 *
Griff Hazen720042b2014-02-24 15:46:56 -08002765 * <p>Values within the Bundle will replace existing extras values in this Builder.
2766 *
2767 * @see Notification#extras
2768 */
Griff Hazen959591e2014-05-15 22:26:18 -07002769 public Builder addExtras(Bundle extras) {
2770 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002771 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002772 }
2773 return this;
2774 }
2775
2776 /**
2777 * Set metadata for this notification.
2778 *
2779 * <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 -04002780 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002781 * called.
2782 *
Griff Hazen720042b2014-02-24 15:46:56 -08002783 * <p>Replaces any existing extras values with those from the provided Bundle.
2784 * Use {@link #addExtras} to merge in metadata instead.
2785 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002786 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002787 */
Griff Hazen959591e2014-05-15 22:26:18 -07002788 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002789 if (extras != null) {
2790 mUserExtras = extras;
2791 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002792 return this;
2793 }
2794
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002795 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002796 * Get the current metadata Bundle used by this notification Builder.
2797 *
2798 * <p>The returned Bundle is shared with this Builder.
2799 *
2800 * <p>The current contents of this Bundle are copied into the Notification each time
2801 * {@link #build()} is called.
2802 *
2803 * @see Notification#extras
2804 */
2805 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002806 return mUserExtras;
2807 }
2808
2809 private Bundle getAllExtras() {
2810 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2811 saveExtras.putAll(mN.extras);
2812 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002813 }
2814
2815 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002816 * Add an action to this notification. Actions are typically displayed by
2817 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002818 * <p>
2819 * Every action must have an icon (32dp square and matching the
2820 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2821 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2822 * <p>
2823 * A notification in its expanded form can display up to 3 actions, from left to right in
2824 * the order they were added. Actions will not be displayed when the notification is
2825 * collapsed, however, so be sure that any essential functions may be accessed by the user
2826 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002827 *
2828 * @param icon Resource ID of a drawable that represents the action.
2829 * @param title Text describing the action.
2830 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002831 *
2832 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002833 */
Dan Sandler86647982015-05-13 23:41:13 -04002834 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002835 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002836 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002837 return this;
2838 }
2839
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002840 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002841 * Add an action to this notification. Actions are typically displayed by
2842 * the system as a button adjacent to the notification content.
2843 * <p>
2844 * Every action must have an icon (32dp square and matching the
2845 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2846 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2847 * <p>
2848 * A notification in its expanded form can display up to 3 actions, from left to right in
2849 * the order they were added. Actions will not be displayed when the notification is
2850 * collapsed, however, so be sure that any essential functions may be accessed by the user
2851 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2852 *
2853 * @param action The action to add.
2854 */
2855 public Builder addAction(Action action) {
2856 mActions.add(action);
2857 return this;
2858 }
2859
2860 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002861 * Alter the complete list of actions attached to this notification.
2862 * @see #addAction(Action).
2863 *
2864 * @param actions
2865 * @return
2866 */
2867 public Builder setActions(Action... actions) {
2868 mActions.clear();
2869 for (int i = 0; i < actions.length; i++) {
2870 mActions.add(actions[i]);
2871 }
2872 return this;
2873 }
2874
2875 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002876 * Add a rich notification style to be applied at build time.
2877 *
2878 * @param style Object responsible for modifying the notification style.
2879 */
2880 public Builder setStyle(Style style) {
2881 if (mStyle != style) {
2882 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002883 if (mStyle != null) {
2884 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002885 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
2886 } else {
2887 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002888 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002889 }
2890 return this;
2891 }
2892
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002893 /**
2894 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07002895 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002896 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2897 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2898 *
2899 * @return The same Builder.
2900 */
2901 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002902 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002903 return this;
2904 }
2905
2906 /**
2907 * Supply a replacement Notification whose contents should be shown in insecure contexts
2908 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2909 * @param n A replacement notification, presumably with some or all info redacted.
2910 * @return The same Builder.
2911 */
2912 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002913 if (n != null) {
2914 mN.publicVersion = new Notification();
2915 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
2916 } else {
2917 mN.publicVersion = null;
2918 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002919 return this;
2920 }
2921
Griff Hazenb720abe2014-05-20 13:15:30 -07002922 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002923 * Apply an extender to this notification builder. Extenders may be used to add
2924 * metadata or change options on this builder.
2925 */
Griff Hazen61a9e862014-05-22 16:05:19 -07002926 public Builder extend(Extender extender) {
2927 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002928 return this;
2929 }
2930
Dan Sandler4e787062015-06-17 15:09:48 -04002931 /**
2932 * @hide
2933 */
2934 public void setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002935 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002936 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002937 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002938 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002939 }
2940 }
2941
Dan Sandler26e81cf2014-05-06 10:01:27 -04002942 /**
2943 * Sets {@link Notification#color}.
2944 *
2945 * @param argb The accent color to use
2946 *
2947 * @return The same Builder.
2948 */
Tor Norbye80756e32015-03-02 09:39:27 -08002949 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002950 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002951 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04002952 return this;
2953 }
2954
Julia Reynolds74303cf2015-10-16 11:37:55 -04002955 /**
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002956 * Sets the topic of this notification. Topics are typically displayed in Notification
Julia Reynolds74303cf2015-10-16 11:37:55 -04002957 * settings.
2958 * <p>
2959 * Every topic must have an id and a textual label.
2960 *
2961 * @param topic The topic to add.
2962 */
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002963 public Builder setTopic(Topic topic) {
2964 mN.topic = topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04002965 return this;
2966 }
2967
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002968 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02002969 // Note: This assumes that the current user can read the profile badge of the
2970 // originating user.
Svetoslavc7d62f02014-09-04 15:39:54 -07002971 return mContext.getPackageManager().getUserBadgeForDensity(
Kenny Guy8942bcd2014-09-08 21:09:47 +01002972 new UserHandle(mOriginatingUserId), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002973 }
2974
2975 private Bitmap getProfileBadge() {
2976 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01002977 if (badge == null) {
2978 return null;
2979 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002980 final int size = mContext.getResources().getDimensionPixelSize(
2981 R.dimen.notification_badge_size);
2982 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002983 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002984 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002985 badge.draw(canvas);
2986 return bitmap;
2987 }
2988
Kenny Guy98193ea2014-07-24 19:54:37 +01002989 private boolean addProfileBadge(RemoteViews contentView, int resId) {
2990 Bitmap profileBadge = getProfileBadge();
2991
2992 contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
2993 contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
2994 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2995
2996 if (profileBadge != null) {
2997 contentView.setImageViewBitmap(resId, profileBadge);
2998 contentView.setViewVisibility(resId, View.VISIBLE);
2999
3000 // Make sure Line 3 is visible. As badge will be here if there
3001 // is no text to display.
3002 if (resId == R.id.profile_badge_line3) {
3003 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3004 }
3005 return true;
3006 }
3007 return false;
3008 }
3009
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003010 private void shrinkLine3Text(RemoteViews contentView) {
3011 float subTextSize = mContext.getResources().getDimensionPixelSize(
3012 R.dimen.notification_subtext_size);
3013 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
3014 }
3015
Christoph Studerfe718432014-09-01 18:21:18 +02003016 private void unshrinkLine3Text(RemoteViews contentView) {
3017 float regularTextSize = mContext.getResources().getDimensionPixelSize(
3018 com.android.internal.R.dimen.notification_text_size);
3019 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
3020 }
3021
3022 private void resetStandardTemplate(RemoteViews contentView) {
3023 removeLargeIconBackground(contentView);
3024 contentView.setViewPadding(R.id.icon, 0, 0, 0, 0);
3025 contentView.setImageViewResource(R.id.icon, 0);
3026 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3027 contentView.setViewVisibility(R.id.right_icon, View.GONE);
3028 contentView.setInt(R.id.right_icon, "setBackgroundResource", 0);
3029 contentView.setImageViewResource(R.id.right_icon, 0);
3030 contentView.setImageViewResource(R.id.icon, 0);
3031 contentView.setTextViewText(R.id.title, null);
3032 contentView.setTextViewText(R.id.text, null);
3033 unshrinkLine3Text(contentView);
3034 contentView.setTextViewText(R.id.text2, null);
3035 contentView.setViewVisibility(R.id.text2, View.GONE);
3036 contentView.setViewVisibility(R.id.info, View.GONE);
3037 contentView.setViewVisibility(R.id.time, View.GONE);
3038 contentView.setViewVisibility(R.id.line3, View.GONE);
3039 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3040 contentView.setViewVisibility(R.id.progress, View.GONE);
Christoph Studerca1db712014-09-10 17:31:33 +02003041 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003042 }
3043
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003044 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003045 return applyStandardTemplate(resId, true /* hasProgress */);
3046 }
3047
3048 /**
3049 * @param hasProgress whether the progress bar should be shown and set
3050 */
3051 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003052 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003053
Christoph Studerfe718432014-09-01 18:21:18 +02003054 resetStandardTemplate(contentView);
3055
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003056 boolean showLine3 = false;
3057 boolean showLine2 = false;
Kenny Guy98193ea2014-07-24 19:54:37 +01003058 boolean contentTextInLine2 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003059 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003060
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003061 if (mN.mLargeIcon != null) {
3062 contentView.setImageViewIcon(R.id.icon, mN.mLargeIcon);
3063 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3064 contentView.setImageViewIcon(R.id.right_icon, mN.mSmallIcon);
Dan Sandler190d58d2014-05-15 09:33:39 -04003065 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003066 processSmallRightIcon(mN.mSmallIcon, contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04003067 } else { // small icon at left
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003068 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Dan Sandler190d58d2014-05-15 09:33:39 -04003069 contentView.setViewVisibility(R.id.icon, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003070 processSmallIconAsLarge(mN.mSmallIcon, contentView);
Joe Onorato561d3852010-11-20 18:09:34 -08003071 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003072 if (ex.getCharSequence(EXTRA_TITLE) != null) {
3073 contentView.setTextViewText(R.id.title,
3074 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003075 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003076 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3077 contentView.setTextViewText(R.id.text,
3078 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003079 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003080 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003081 if (ex.getCharSequence(EXTRA_INFO_TEXT) != null) {
3082 contentView.setTextViewText(R.id.info,
3083 processLegacyText(ex.getCharSequence(EXTRA_INFO_TEXT)));
Jeff Sharkey1c400132011-08-05 14:50:13 -07003084 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003085 showLine3 = true;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003086 } else if (mN.number > 0) {
Daniel Sandlerebce0112011-06-16 16:44:51 -04003087 final int tooBig = mContext.getResources().getInteger(
3088 R.integer.status_bar_notification_info_maxnum);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003089 if (mN.number > tooBig) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003090 contentView.setTextViewText(R.id.info, processLegacyText(
3091 mContext.getResources().getString(
3092 R.string.status_bar_notification_info_overflow)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003093 } else {
3094 NumberFormat f = NumberFormat.getIntegerInstance();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003095 contentView.setTextViewText(R.id.info, processLegacyText(f.format(mN.number)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003096 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003097 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003098 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003099 } else {
3100 contentView.setViewVisibility(R.id.info, View.GONE);
3101 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003102
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003103 // Need to show three lines?
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003104 if (ex.getCharSequence(EXTRA_SUB_TEXT) != null) {
3105 contentView.setTextViewText(R.id.text,
3106 processLegacyText(ex.getCharSequence(EXTRA_SUB_TEXT)));
3107 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3108 contentView.setTextViewText(R.id.text2,
3109 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003110 contentView.setViewVisibility(R.id.text2, View.VISIBLE);
3111 showLine2 = true;
Kenny Guy98193ea2014-07-24 19:54:37 +01003112 contentTextInLine2 = true;
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003113 } else {
3114 contentView.setViewVisibility(R.id.text2, View.GONE);
3115 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003116 } else {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003117 contentView.setViewVisibility(R.id.text2, View.GONE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003118 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3119 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3120 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3121 if (hasProgress && (max != 0 || ind)) {
Christoph Studeraca4b252014-09-09 15:58:41 +02003122 contentView.setViewVisibility(R.id.progress, View.VISIBLE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003123 contentView.setProgressBar(
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003124 R.id.progress, max, progress, ind);
Jorim Jaggief72a192014-08-26 21:57:46 +02003125 contentView.setProgressBackgroundTintList(
Alan Viverette4a357cd2015-03-18 18:37:18 -07003126 R.id.progress, ColorStateList.valueOf(mContext.getColor(
Jorim Jaggief72a192014-08-26 21:57:46 +02003127 R.color.notification_progress_background_color)));
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003128 if (mN.color != COLOR_DEFAULT) {
3129 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Jorim Jaggief72a192014-08-26 21:57:46 +02003130 contentView.setProgressTintList(R.id.progress, colorStateList);
3131 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
3132 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003133 showLine2 = true;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003134 } else {
3135 contentView.setViewVisibility(R.id.progress, View.GONE);
3136 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003137 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003138 if (showLine2) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003139
3140 // need to shrink all the type to make sure everything fits
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003141 shrinkLine3Text(contentView);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003142 }
3143
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003144 if (showsTimeOrChronometer()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003145 if (ex.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003146 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3147 contentView.setLong(R.id.chronometer, "setBase",
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003148 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003149 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3150 } else {
3151 contentView.setViewVisibility(R.id.time, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003152 contentView.setLong(R.id.time, "setTime", mN.when);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003153 }
Joe Onorato561d3852010-11-20 18:09:34 -08003154 }
Daniel Sandler0c890492012-09-12 17:23:10 -07003155
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003156 // Adjust padding depending on line count and font size.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003157 contentView.setViewPadding(R.id.line1, 0,
3158 calculateTopPadding(mContext, hasThreeLines(),
3159 mContext.getResources().getConfiguration().fontScale),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003160 0, 0);
3161
Kenny Guy98193ea2014-07-24 19:54:37 +01003162 // We want to add badge to first line of text.
3163 boolean addedBadge = addProfileBadge(contentView,
3164 contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
3165 // If we added the badge to line 3 then we should show line 3.
3166 if (addedBadge && !contentTextInLine2) {
3167 showLine3 = true;
3168 }
3169
3170 // Note getStandardView may hide line 3 again.
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003171 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003172 contentView.setViewVisibility(R.id.overflow_divider,
3173 showLine3 ? View.VISIBLE : View.GONE);
Joe Onorato561d3852010-11-20 18:09:34 -08003174 return contentView;
3175 }
3176
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003177 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003178 * @return true if the built notification will show the time or the chronometer; false
3179 * otherwise
3180 */
3181 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003182 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003183 }
3184
3185 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003186 * Logic to find out whether the notification is going to have three lines in the contracted
3187 * layout. This is used to adjust the top padding.
3188 *
3189 * @return true if the notification is going to have three lines; false if the notification
3190 * is going to have one or two lines
3191 */
3192 private boolean hasThreeLines() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003193 final CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3194 final CharSequence text = mN.extras.getCharSequence(EXTRA_TEXT);
3195 boolean contentTextInLine2 = subText != null && text != null;
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003196 boolean hasProgress = mStyle == null || mStyle.hasProgress();
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003197 // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
3198 boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003199 boolean hasLine3 = text != null || mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null
3200 || mN.number > 0 || badgeInLine3;
3201 final Bundle ex = mN.extras;
3202 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3203 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3204 boolean hasLine2 = (subText != null && text != null) ||
3205 (hasProgress && subText == null && (max != 0 || ind));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003206 return hasLine2 && hasLine3;
3207 }
3208
3209 /**
3210 * @hide
3211 */
3212 public static int calculateTopPadding(Context ctx, boolean hasThreeLines,
3213 float fontScale) {
3214 int padding = ctx.getResources().getDimensionPixelSize(hasThreeLines
3215 ? R.dimen.notification_top_pad_narrow
3216 : R.dimen.notification_top_pad);
3217 int largePadding = ctx.getResources().getDimensionPixelSize(hasThreeLines
3218 ? R.dimen.notification_top_pad_large_text_narrow
3219 : R.dimen.notification_top_pad_large_text);
3220 float largeFactor = (MathUtils.constrain(fontScale, 1.0f, LARGE_TEXT_SCALE) - 1f)
3221 / (LARGE_TEXT_SCALE - 1f);
3222
3223 // Linearly interpolate the padding between large and normal with the font scale ranging
3224 // from 1f to LARGE_TEXT_SCALE
3225 return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
3226 }
3227
Christoph Studerfe718432014-09-01 18:21:18 +02003228 private void resetStandardTemplateWithActions(RemoteViews big) {
3229 big.setViewVisibility(R.id.actions, View.GONE);
3230 big.setViewVisibility(R.id.action_divider, View.GONE);
3231 big.removeAllViews(R.id.actions);
3232 }
3233
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003234 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003235 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003236
Christoph Studerfe718432014-09-01 18:21:18 +02003237 resetStandardTemplateWithActions(big);
3238
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003239 int N = mActions.size();
3240 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003241 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003242 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003243 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003244 for (int i=0; i<N; i++) {
3245 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003246 big.addView(R.id.actions, button);
3247 }
3248 }
3249 return big;
3250 }
3251
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003252 /**
3253 * Construct a RemoteViews for the final 1U notification layout. In order:
3254 * 1. Custom contentView from the caller
3255 * 2. Style's proposed content view
3256 * 3. Standard template view
3257 */
3258 public RemoteViews makeContentView() {
3259 if (mN.contentView != null) {
3260 return mN.contentView;
3261 } else if (mStyle != null) {
3262 final RemoteViews styleView = mStyle.makeContentView();
3263 if (styleView != null) {
3264 return styleView;
3265 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003266 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003267 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003268 }
3269
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003270 /**
3271 * Construct a RemoteViews for the final big notification layout.
3272 */
3273 public RemoteViews makeBigContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003274 if (mN.bigContentView != null) {
3275 return mN.bigContentView;
3276 } else if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003277 final RemoteViews styleView = mStyle.makeBigContentView();
3278 if (styleView != null) {
3279 return styleView;
3280 }
Julia Reynolds089e3e42015-11-18 09:59:57 -05003281 } else if (mActions.size() == 0) {
3282 return null;
3283 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003284
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003285 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003286 }
3287
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003288 /**
3289 * Construct a RemoteViews for the final heads-up notification layout.
3290 */
3291 public RemoteViews makeHeadsUpContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003292 if (mN.headsUpContentView != null) {
3293 return mN.headsUpContentView;
3294 } else if (mStyle != null) {
3295 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3296 if (styleView != null) {
3297 return styleView;
3298 }
3299 } else if (mActions.size() == 0) {
3300 return null;
3301 }
3302
Chris Wren8fd39ec2014-02-27 17:43:26 -05003303
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003304 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003305 }
3306
3307
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003308 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003309 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003310 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003311 tombstone ? getActionTombstoneLayoutResource()
3312 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003313 final Icon ai = action.getIcon();
Dan Sandler912282e2015-07-28 22:49:30 -04003314 button.setTextViewCompoundDrawablesRelative(R.id.action0, ai, null, null, null);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003315 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003316 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003317 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003318 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003319 button.setContentDescription(R.id.action0, action.title);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003320 processLegacyAction(action, button);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003321 return button;
3322 }
3323
Joe Onoratocb109a02011-01-18 17:57:41 -08003324 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003325 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003326 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003327 */
3328 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003329 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003330 }
3331
3332 private void processLegacyAction(Action action, RemoteViews button) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003333 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
Christoph Studer239f8352014-08-25 15:13:18 +02003334 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
Alan Viverette4a357cd2015-03-18 18:37:18 -07003335 mContext.getColor(R.color.notification_action_color_filter),
Christoph Studer239f8352014-08-25 15:13:18 +02003336 PorterDuff.Mode.MULTIPLY);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003337 }
3338 }
3339
3340 private CharSequence processLegacyText(CharSequence charSequence) {
3341 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003342 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003343 } else {
3344 return charSequence;
3345 }
3346 }
3347
Dan Sandler26e81cf2014-05-06 10:01:27 -04003348 /**
3349 * Apply any necessary background to smallIcons being used in the largeIcon spot.
3350 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003351 private void processSmallIconAsLarge(Icon largeIcon, RemoteViews contentView) {
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003352 if (!isLegacy()) {
3353 contentView.setDrawableParameters(R.id.icon, false, -1,
3354 0xFFFFFFFF,
3355 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003356 applyLargeIconBackground(contentView);
Dan Sandlerd63f9322015-05-06 15:18:49 -04003357 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003358 if (getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003359 applyLargeIconBackground(contentView);
3360 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003361 }
3362 }
3363
Dan Sandler26e81cf2014-05-06 10:01:27 -04003364 /**
3365 * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
3366 * if it's grayscale).
3367 */
3368 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003369 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3370 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003371 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Dan Sandler26e81cf2014-05-06 10:01:27 -04003372 applyLargeIconBackground(contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04003373 } else {
3374 removeLargeIconBackground(contentView);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003375 }
3376 }
3377
Dan Sandler26e81cf2014-05-06 10:01:27 -04003378 /**
3379 * Add a colored circle behind the largeIcon slot.
3380 */
3381 private void applyLargeIconBackground(RemoteViews contentView) {
3382 contentView.setInt(R.id.icon, "setBackgroundResource",
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003383 R.drawable.notification_icon_legacy_bg);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003384
3385 contentView.setDrawableParameters(
3386 R.id.icon,
3387 true,
3388 -1,
Jorim Jaggi74419312014-06-10 20:57:21 +02003389 resolveColor(),
Dan Sandler26e81cf2014-05-06 10:01:27 -04003390 PorterDuff.Mode.SRC_ATOP,
3391 -1);
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003392
3393 int padding = mContext.getResources().getDimensionPixelSize(
3394 R.dimen.notification_large_icon_circle_padding);
3395 contentView.setViewPadding(R.id.icon, padding, padding, padding, padding);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003396 }
3397
Dan Sandler190d58d2014-05-15 09:33:39 -04003398 private void removeLargeIconBackground(RemoteViews contentView) {
3399 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3400 }
3401
Dan Sandler26e81cf2014-05-06 10:01:27 -04003402 /**
3403 * Recolor small icons when used in the R.id.right_icon slot.
3404 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003405 private void processSmallRightIcon(Icon smallIcon, RemoteViews contentView) {
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003406 if (!isLegacy()) {
Dan Sandler190d58d2014-05-15 09:33:39 -04003407 contentView.setDrawableParameters(R.id.right_icon, false, -1,
3408 0xFFFFFFFF,
3409 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003410 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04003411 final boolean gray = isLegacy()
3412 && smallIcon.getType() == Icon.TYPE_RESOURCE
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003413 && getColorUtil().isGrayscaleIcon(mContext, smallIcon.getResId());
Dan Sandlerd63f9322015-05-06 15:18:49 -04003414 if (!isLegacy() || gray) {
Dan Sandler190d58d2014-05-15 09:33:39 -04003415 contentView.setInt(R.id.right_icon,
3416 "setBackgroundResource",
3417 R.drawable.notification_icon_legacy_bg);
3418
3419 contentView.setDrawableParameters(
3420 R.id.right_icon,
3421 true,
3422 -1,
Jorim Jaggi74419312014-06-10 20:57:21 +02003423 resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003424 PorterDuff.Mode.SRC_ATOP,
3425 -1);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003426 }
3427 }
3428
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003429 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003430 if (mN.color != COLOR_DEFAULT) {
3431 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003432 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003433 }
3434
Dan Sandler26e81cf2014-05-06 10:01:27 -04003435 private int resolveColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003436 if (mN.color == COLOR_DEFAULT) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07003437 return mContext.getColor(R.color.notification_icon_bg_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003438 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003439 return mN.color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003440 }
3441
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003442 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003443 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003444 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003445 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003446 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003447 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003448 mN.actions = new Action[mActions.size()];
3449 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003450 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003451 if (!mPersonList.isEmpty()) {
3452 mN.extras.putStringArray(EXTRA_PEOPLE,
3453 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003454 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003455 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003456 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003457
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003458 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003459 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003460 ApplicationInfo applicationInfo = n.extras.getParcelable(
3461 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003462 Context builderContext;
3463 try {
Kenny Guy77320062014-08-27 21:37:15 +01003464 builderContext = context.createApplicationContext(applicationInfo,
Christoph Studer4600f9b2014-07-22 22:44:43 +02003465 Context.CONTEXT_RESTRICTED);
3466 } catch (NameNotFoundException e) {
Kenny Guy77320062014-08-27 21:37:15 +01003467 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
Christoph Studer4600f9b2014-07-22 22:44:43 +02003468 builderContext = context; // try with our context
3469 }
3470
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003471 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003472 }
3473
3474 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3475 Class<? extends Style>[] classes = new Class[]{
3476 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3477 for (Class<? extends Style> innerClass : classes) {
3478 if (templateClass.equals(innerClass.getName())) {
3479 return innerClass;
3480 }
3481 }
3482 return null;
3483 }
3484
3485 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3486 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003487 }
3488
3489 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3490 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003491 }
3492
3493 private void setBuilderHeadsUpContentView(Notification n,
3494 RemoteViews headsUpContentView) {
3495 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003496 }
3497
3498 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003499 * @deprecated Use {@link #build()} instead.
3500 */
3501 @Deprecated
3502 public Notification getNotification() {
3503 return build();
3504 }
3505
3506 /**
3507 * Combine all of the options that have been set and return a new {@link Notification}
3508 * object.
3509 */
3510 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003511 // first, add any extras from the calling code
3512 if (mUserExtras != null) {
3513 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003514 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003515
3516 // lazy stuff from mContext; see comment in Builder(Context, Notification)
3517 mN.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, mContext.getApplicationInfo());
Kenny Guy8942bcd2014-09-08 21:09:47 +01003518 mOriginatingUserId = mContext.getUserId();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003519 mN.extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
Christoph Studer943aa672014-08-03 20:31:16 +02003520
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003521 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003522
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003523 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003524 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003525 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003526
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003527 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003528 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003529
3530 /**
3531 * Apply this Builder to an existing {@link Notification} object.
3532 *
3533 * @hide
3534 */
3535 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003536 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003537 return n;
3538 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003539
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003540 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003541 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003542 }
3543
3544 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003545 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003546 }
3547
3548 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003549 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003550 }
3551
3552 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003553 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003554 }
3555
3556 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003557 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003558 }
3559
3560 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003561 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003562 }
3563
3564 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003565 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003566 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003567 }
3568
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003569 /**
3570 * An object that can apply a rich notification style to a {@link Notification.Builder}
3571 * object.
3572 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003573 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003574 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003575
3576 /**
3577 * @hide
3578 */
3579 protected CharSequence mSummaryText = null;
3580
3581 /**
3582 * @hide
3583 */
3584 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003585
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003586 protected Builder mBuilder;
3587
Chris Wrend6297db2012-05-03 16:20:13 -04003588 /**
3589 * Overrides ContentTitle in the big form of the template.
3590 * This defaults to the value passed to setContentTitle().
3591 */
3592 protected void internalSetBigContentTitle(CharSequence title) {
3593 mBigContentTitle = title;
3594 }
3595
3596 /**
3597 * Set the first line of text after the detail section in the big form of the template.
3598 */
3599 protected void internalSetSummaryText(CharSequence cs) {
3600 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003601 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003602 }
3603
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003604 public void setBuilder(Builder builder) {
3605 if (mBuilder != builder) {
3606 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003607 if (mBuilder != null) {
3608 mBuilder.setStyle(this);
3609 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003610 }
3611 }
3612
Chris Wrend6297db2012-05-03 16:20:13 -04003613 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003614 if (mBuilder == null) {
3615 throw new IllegalArgumentException("Style requires a valid Builder object");
3616 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003617 }
Chris Wrend6297db2012-05-03 16:20:13 -04003618
3619 protected RemoteViews getStandardView(int layoutId) {
3620 checkBuilder();
3621
Christoph Studer4600f9b2014-07-22 22:44:43 +02003622 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003623 CharSequence oldBuilderContentTitle =
3624 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003625 if (mBigContentTitle != null) {
3626 mBuilder.setContentTitle(mBigContentTitle);
3627 }
3628
Chris Wrend6297db2012-05-03 16:20:13 -04003629 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3630
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003631 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003632
Chris Wrend6297db2012-05-03 16:20:13 -04003633 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3634 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003635 } else {
3636 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003637 }
3638
Daniel Sandler619738c2012-06-07 16:33:08 -04003639 // The last line defaults to the subtext, but can be replaced by mSummaryText
3640 final CharSequence overflowText =
3641 mSummaryTextSet ? mSummaryText
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003642 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT);
Daniel Sandler619738c2012-06-07 16:33:08 -04003643 if (overflowText != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003644 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
Daniel Sandler619738c2012-06-07 16:33:08 -04003645 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003646 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
Daniel Sandler916ad912012-06-13 12:17:07 -04003647 } else {
Kenny Guy98193ea2014-07-24 19:54:37 +01003648 // Clear text in case we use the line to show the profile badge.
3649 contentView.setTextViewText(R.id.text, "");
Daniel Sandler916ad912012-06-13 12:17:07 -04003650 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3651 contentView.setViewVisibility(R.id.line3, View.GONE);
Chris Wrend6297db2012-05-03 16:20:13 -04003652 }
3653
3654 return contentView;
3655 }
3656
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003657 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003658 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3659 * The default implementation has nothing additional to add.
3660 * @hide
3661 */
3662 public RemoteViews makeContentView() {
3663 return null;
3664 }
3665
3666 /**
3667 * Construct a Style-specific RemoteViews for the final big notification layout.
3668 * @hide
3669 */
3670 public RemoteViews makeBigContentView() {
3671 return null;
3672 }
3673
3674 /**
3675 * Construct a Style-specific RemoteViews for the final HUN layout.
3676 * @hide
3677 */
3678 public RemoteViews makeHeadsUpContentView() {
3679 return null;
3680 }
3681
3682 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003683 * Changes the padding of the first line such that the big and small content view have the
3684 * same top padding.
3685 *
3686 * @hide
3687 */
3688 protected void applyTopPadding(RemoteViews contentView) {
3689 int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003690 mBuilder.hasThreeLines(),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003691 mBuilder.mContext.getResources().getConfiguration().fontScale);
3692 contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
3693 }
3694
3695 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003696 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003697 * @hide
3698 */
3699 public void addExtras(Bundle extras) {
3700 if (mSummaryTextSet) {
3701 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3702 }
3703 if (mBigContentTitle != null) {
3704 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3705 }
Chris Wren91ad5632013-06-05 15:05:57 -04003706 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003707 }
3708
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003709 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003710 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003711 * @hide
3712 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003713 protected void restoreFromExtras(Bundle extras) {
3714 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3715 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3716 mSummaryTextSet = true;
3717 }
3718 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3719 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3720 }
3721 }
3722
3723
3724 /**
3725 * @hide
3726 */
3727 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003728 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003729 return wip;
3730 }
3731
Daniel Sandler0ec46202015-06-24 01:27:05 -04003732 /**
3733 * @hide
3734 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003735 public void purgeResources() {}
3736
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003737 /**
3738 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3739 * attached to.
3740 *
3741 * @return the fully constructed Notification.
3742 */
3743 public Notification build() {
3744 checkBuilder();
3745 return mBuilder.build();
3746 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003747
3748 /**
3749 * @hide
3750 * @return true if the style positions the progress bar on the second line; false if the
3751 * style hides the progress bar
3752 */
3753 protected boolean hasProgress() {
3754 return true;
3755 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003756 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003757
3758 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003759 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003760 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003761 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003762 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003763 * Notification notif = new Notification.Builder(mContext)
3764 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3765 * .setContentText(subject)
3766 * .setSmallIcon(R.drawable.new_post)
3767 * .setLargeIcon(aBitmap)
3768 * .setStyle(new Notification.BigPictureStyle()
3769 * .bigPicture(aBigBitmap))
3770 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003771 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003772 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003773 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003774 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003775 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003776 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003777 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003778 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003779
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003780 public BigPictureStyle() {
3781 }
3782
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003783 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003784 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003785 }
3786
Chris Wrend6297db2012-05-03 16:20:13 -04003787 /**
3788 * Overrides ContentTitle in the big form of the template.
3789 * This defaults to the value passed to setContentTitle().
3790 */
3791 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003792 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003793 return this;
3794 }
3795
3796 /**
3797 * Set the first line of text after the detail section in the big form of the template.
3798 */
3799 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003800 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003801 return this;
3802 }
3803
Chris Wren0bd664d2012-08-01 13:56:56 -04003804 /**
3805 * Provide the bitmap to be used as the payload for the BigPicture notification.
3806 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003807 public BigPictureStyle bigPicture(Bitmap b) {
3808 mPicture = b;
3809 return this;
3810 }
3811
Chris Wren3745a3d2012-05-22 15:11:52 -04003812 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003813 * Override the large icon when the big notification is shown.
3814 */
3815 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003816 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3817 }
3818
3819 /**
3820 * Override the large icon when the big notification is shown.
3821 */
3822 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003823 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003824 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003825 return this;
3826 }
3827
Riley Andrews0394a0c2015-11-03 23:36:52 -08003828 /** @hide */
3829 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
3830
Daniel Sandler0ec46202015-06-24 01:27:05 -04003831 /**
3832 * @hide
3833 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003834 @Override
3835 public void purgeResources() {
3836 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08003837 if (mPicture != null &&
3838 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08003839 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003840 mPicture = mPicture.createAshmemBitmap();
3841 }
3842 if (mBigLargeIcon != null) {
3843 mBigLargeIcon.convertToAshmem();
3844 }
3845 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003846
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003847 /**
3848 * @hide
3849 */
3850 public RemoteViews makeBigContentView() {
3851 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003852 // This covers the following cases:
3853 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003854 // mN.mLargeIcon
3855 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003856 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003857 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003858 oldLargeIcon = mBuilder.mN.mLargeIcon;
3859 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003860 }
3861
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003862 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003863
Christoph Studer5c510ee2014-12-15 16:32:27 +01003864 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003865 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003866 }
3867
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003868 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3869
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003870 applyTopPadding(contentView);
3871
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003872 boolean twoTextLines = mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT) != null
3873 && mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT) != null;
Kenny Guy98193ea2014-07-24 19:54:37 +01003874 mBuilder.addProfileBadge(contentView,
3875 twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003876 return contentView;
3877 }
3878
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003879 /**
3880 * @hide
3881 */
3882 public void addExtras(Bundle extras) {
3883 super.addExtras(extras);
3884
3885 if (mBigLargeIconSet) {
3886 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3887 }
3888 extras.putParcelable(EXTRA_PICTURE, mPicture);
3889 }
3890
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003891 /**
3892 * @hide
3893 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003894 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003895 protected void restoreFromExtras(Bundle extras) {
3896 super.restoreFromExtras(extras);
3897
3898 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003899 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003900 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003901 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003902 mPicture = extras.getParcelable(EXTRA_PICTURE);
3903 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003904 }
3905
3906 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003907 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003908 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003909 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003910 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003911 * Notification notif = new Notification.Builder(mContext)
3912 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3913 * .setContentText(subject)
3914 * .setSmallIcon(R.drawable.new_mail)
3915 * .setLargeIcon(aBitmap)
3916 * .setStyle(new Notification.BigTextStyle()
3917 * .bigText(aVeryLongString))
3918 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003919 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003920 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003921 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003922 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003923 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003924
3925 private static final int MAX_LINES = 13;
3926 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3927 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3928
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003929 private CharSequence mBigText;
3930
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003931 public BigTextStyle() {
3932 }
3933
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003934 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003935 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003936 }
3937
Chris Wrend6297db2012-05-03 16:20:13 -04003938 /**
3939 * Overrides ContentTitle in the big form of the template.
3940 * This defaults to the value passed to setContentTitle().
3941 */
3942 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003943 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003944 return this;
3945 }
3946
3947 /**
3948 * Set the first line of text after the detail section in the big form of the template.
3949 */
3950 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003951 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003952 return this;
3953 }
3954
Chris Wren0bd664d2012-08-01 13:56:56 -04003955 /**
3956 * Provide the longer text to be displayed in the big form of the
3957 * template in place of the content text.
3958 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003959 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003960 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003961 return this;
3962 }
3963
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003964 /**
3965 * @hide
3966 */
3967 public void addExtras(Bundle extras) {
3968 super.addExtras(extras);
3969
Christoph Studer4600f9b2014-07-22 22:44:43 +02003970 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3971 }
3972
3973 /**
3974 * @hide
3975 */
3976 @Override
3977 protected void restoreFromExtras(Bundle extras) {
3978 super.restoreFromExtras(extras);
3979
3980 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003981 }
3982
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003983 /**
3984 * @hide
3985 */
3986 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003987
3988 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003989 CharSequence oldBuilderContentText =
3990 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3991 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003992
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003993 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003994
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003995 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003996
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003997 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003998 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003999 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Chris Wren3c5f92432012-05-04 16:31:17 -04004000 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004001
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004002 applyTopPadding(contentView);
4003
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004004 mBuilder.shrinkLine3Text(contentView);
4005
Kenny Guy98193ea2014-07-24 19:54:37 +01004006 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4007
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004008 return contentView;
4009 }
4010
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004011 private int calculateMaxLines() {
4012 int lineCount = MAX_LINES;
4013 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004014 boolean hasSummary = (mSummaryTextSet ? mSummaryText
4015 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004016 if (hasActions) {
4017 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4018 }
4019 if (hasSummary) {
4020 lineCount -= LINES_CONSUMED_BY_SUMMARY;
4021 }
4022
4023 // If we have less top padding at the top, we can fit less lines.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004024 if (!mBuilder.hasThreeLines()) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004025 lineCount--;
4026 }
4027 return lineCount;
4028 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004029 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004030
4031 /**
4032 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004033 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004034 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004035 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004036 * Notification notif = new Notification.Builder(mContext)
4037 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4038 * .setContentText(subject)
4039 * .setSmallIcon(R.drawable.new_mail)
4040 * .setLargeIcon(aBitmap)
4041 * .setStyle(new Notification.InboxStyle()
4042 * .addLine(str1)
4043 * .addLine(str2)
4044 * .setContentTitle(&quot;&quot;)
4045 * .setSummaryText(&quot;+3 more&quot;))
4046 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004047 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004048 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004049 * @see Notification#bigContentView
4050 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004051 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004052 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4053
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004054 public InboxStyle() {
4055 }
4056
Daniel Sandler879c5e02012-04-17 16:46:51 -04004057 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004058 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004059 }
4060
Chris Wrend6297db2012-05-03 16:20:13 -04004061 /**
4062 * Overrides ContentTitle in the big form of the template.
4063 * This defaults to the value passed to setContentTitle().
4064 */
4065 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004066 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004067 return this;
4068 }
4069
4070 /**
4071 * Set the first line of text after the detail section in the big form of the template.
4072 */
4073 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004074 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004075 return this;
4076 }
4077
Chris Wren0bd664d2012-08-01 13:56:56 -04004078 /**
4079 * Append a line to the digest section of the Inbox notification.
4080 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004081 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004082 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004083 return this;
4084 }
4085
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004086 /**
4087 * @hide
4088 */
4089 public void addExtras(Bundle extras) {
4090 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004091
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004092 CharSequence[] a = new CharSequence[mTexts.size()];
4093 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4094 }
4095
Christoph Studer4600f9b2014-07-22 22:44:43 +02004096 /**
4097 * @hide
4098 */
4099 @Override
4100 protected void restoreFromExtras(Bundle extras) {
4101 super.restoreFromExtras(extras);
4102
4103 mTexts.clear();
4104 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4105 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4106 }
4107 }
4108
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004109 /**
4110 * @hide
4111 */
4112 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004113 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004114 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004115 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4116 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004117
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004118 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004119
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004120 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004121
Chris Wrend6297db2012-05-03 16:20:13 -04004122 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004123
Chris Wrend6297db2012-05-03 16:20:13 -04004124 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 -04004125 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004126
Chris Wren4ed80d52012-05-17 09:30:03 -04004127 // Make sure all rows are gone in case we reuse a view.
4128 for (int rowId : rowIds) {
4129 contentView.setViewVisibility(rowId, View.GONE);
4130 }
4131
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004132 final boolean largeText =
4133 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4134 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4135 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004136 int i=0;
4137 while (i < mTexts.size() && i < rowIds.length) {
4138 CharSequence str = mTexts.get(i);
4139 if (str != null && !str.equals("")) {
4140 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004141 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004142 if (largeText) {
4143 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4144 subTextSize);
4145 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004146 }
4147 i++;
4148 }
4149
Chris Wren683ab002012-09-20 10:35:54 -04004150 contentView.setViewVisibility(R.id.inbox_end_pad,
4151 mTexts.size() > 0 ? View.VISIBLE : View.GONE);
4152
4153 contentView.setViewVisibility(R.id.inbox_more,
4154 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
Chris Wren29bb6d92012-05-17 18:09:42 -04004155
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004156 applyTopPadding(contentView);
4157
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004158 mBuilder.shrinkLine3Text(contentView);
4159
Kenny Guy98193ea2014-07-24 19:54:37 +01004160 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4161
Daniel Sandler879c5e02012-04-17 16:46:51 -04004162 return contentView;
4163 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004164 }
Dan Sandler842dd772014-05-15 09:36:47 -04004165
4166 /**
4167 * Notification style for media playback notifications.
4168 *
4169 * In the expanded form, {@link Notification#bigContentView}, up to 5
4170 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004171 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004172 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4173 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4174 * treated as album artwork.
4175 *
4176 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4177 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004178 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004179 * in the standard view alongside the usual content.
4180 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004181 * Notifications created with MediaStyle will have their category set to
4182 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4183 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4184 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004185 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4186 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004187 * the System UI can identify this as a notification representing an active media session
4188 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4189 *
4190 * To use this style with your Notification, feed it to
4191 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4192 * <pre class="prettyprint">
4193 * Notification noti = new Notification.Builder()
4194 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004195 * .setContentTitle(&quot;Track title&quot;)
4196 * .setContentText(&quot;Artist - Album&quot;)
4197 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004198 * .setStyle(<b>new Notification.MediaStyle()</b>
4199 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004200 * .build();
4201 * </pre>
4202 *
4203 * @see Notification#bigContentView
4204 */
4205 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004206 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004207 static final int MAX_MEDIA_BUTTONS = 5;
4208
4209 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004210 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004211
4212 public MediaStyle() {
4213 }
4214
4215 public MediaStyle(Builder builder) {
4216 setBuilder(builder);
4217 }
4218
4219 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004220 * 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 -04004221 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004222 *
4223 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004224 */
4225 public MediaStyle setShowActionsInCompactView(int...actions) {
4226 mActionsToShowInCompact = actions;
4227 return this;
4228 }
4229
4230 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004231 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4232 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004233 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004234 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004235 mToken = token;
4236 return this;
4237 }
4238
Christoph Studer4600f9b2014-07-22 22:44:43 +02004239 /**
4240 * @hide
4241 */
Dan Sandler842dd772014-05-15 09:36:47 -04004242 @Override
4243 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004244 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004245 if (wip.category == null) {
4246 wip.category = Notification.CATEGORY_TRANSPORT;
4247 }
Dan Sandler842dd772014-05-15 09:36:47 -04004248 return wip;
4249 }
4250
Christoph Studer4600f9b2014-07-22 22:44:43 +02004251 /**
4252 * @hide
4253 */
4254 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004255 public RemoteViews makeContentView() {
4256 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004257 }
4258
4259 /**
4260 * @hide
4261 */
4262 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004263 public RemoteViews makeBigContentView() {
4264 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004265 }
4266
Dan Sandler842dd772014-05-15 09:36:47 -04004267 /** @hide */
4268 @Override
4269 public void addExtras(Bundle extras) {
4270 super.addExtras(extras);
4271
4272 if (mToken != null) {
4273 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4274 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004275 if (mActionsToShowInCompact != null) {
4276 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4277 }
Dan Sandler842dd772014-05-15 09:36:47 -04004278 }
4279
Christoph Studer4600f9b2014-07-22 22:44:43 +02004280 /**
4281 * @hide
4282 */
4283 @Override
4284 protected void restoreFromExtras(Bundle extras) {
4285 super.restoreFromExtras(extras);
4286
4287 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4288 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4289 }
4290 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4291 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4292 }
4293 }
4294
Dan Sandler842dd772014-05-15 09:36:47 -04004295 private RemoteViews generateMediaActionButton(Action action) {
4296 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004297 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004298 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004299 button.setImageViewIcon(R.id.action0, action.getIcon());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004300 button.setDrawableParameters(R.id.action0, false, -1,
4301 0xFFFFFFFF,
4302 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004303 if (!tombstone) {
4304 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4305 }
4306 button.setContentDescription(R.id.action0, action.title);
4307 return button;
4308 }
4309
4310 private RemoteViews makeMediaContentView() {
4311 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004312 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004313
4314 final int numActions = mBuilder.mActions.size();
4315 final int N = mActionsToShowInCompact == null
4316 ? 0
4317 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4318 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004319 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004320 for (int i = 0; i < N; i++) {
4321 if (i >= numActions) {
4322 throw new IllegalArgumentException(String.format(
4323 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4324 i, numActions - 1));
4325 }
4326
4327 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
4328 final RemoteViews button = generateMediaActionButton(action);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004329 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004330 }
4331 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004332 styleText(view);
4333 hideRightIcon(view);
Dan Sandler842dd772014-05-15 09:36:47 -04004334 return view;
4335 }
4336
4337 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004338 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
4339 RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount),
4340 false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004341
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004342 if (actionCount > 0) {
4343 big.removeAllViews(com.android.internal.R.id.media_actions);
4344 for (int i = 0; i < actionCount; i++) {
Dan Sandler842dd772014-05-15 09:36:47 -04004345 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004346 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004347 }
4348 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004349 styleText(big);
4350 hideRightIcon(big);
4351 applyTopPadding(big);
4352 big.setViewVisibility(android.R.id.progress, View.GONE);
Dan Sandler842dd772014-05-15 09:36:47 -04004353 return big;
4354 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004355
4356 private int getBigLayoutResource(int actionCount) {
4357 if (actionCount <= 3) {
4358 return R.layout.notification_template_material_big_media_narrow;
4359 } else {
4360 return R.layout.notification_template_material_big_media;
4361 }
4362 }
4363
4364 private void hideRightIcon(RemoteViews contentView) {
4365 contentView.setViewVisibility(R.id.right_icon, View.GONE);
4366 }
4367
4368 /**
4369 * Applies the special text colors for media notifications to all text views.
4370 */
4371 private void styleText(RemoteViews contentView) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07004372 int primaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004373 R.color.notification_media_primary_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -07004374 int secondaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004375 R.color.notification_media_secondary_color);
4376 contentView.setTextColor(R.id.title, primaryColor);
4377 if (mBuilder.showsTimeOrChronometer()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004378 if (mBuilder.getAllExtras().getBoolean(EXTRA_SHOW_CHRONOMETER)) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004379 contentView.setTextColor(R.id.chronometer, secondaryColor);
4380 } else {
4381 contentView.setTextColor(R.id.time, secondaryColor);
4382 }
4383 }
4384 contentView.setTextColor(R.id.text2, secondaryColor);
4385 contentView.setTextColor(R.id.text, secondaryColor);
4386 contentView.setTextColor(R.id.info, secondaryColor);
4387 }
4388
4389 /**
4390 * @hide
4391 */
4392 @Override
4393 protected boolean hasProgress() {
4394 return false;
4395 }
Dan Sandler842dd772014-05-15 09:36:47 -04004396 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004397
Christoph Studer4600f9b2014-07-22 22:44:43 +02004398 // When adding a new Style subclass here, don't forget to update
4399 // Builder.getNotificationStyleClass.
4400
Griff Hazen61a9e862014-05-22 16:05:19 -07004401 /**
4402 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4403 * metadata or change options on a notification builder.
4404 */
4405 public interface Extender {
4406 /**
4407 * Apply this extender to a notification builder.
4408 * @param builder the builder to be modified.
4409 * @return the build object for chaining.
4410 */
4411 public Builder extend(Builder builder);
4412 }
4413
4414 /**
4415 * Helper class to add wearable extensions to notifications.
4416 * <p class="note"> See
4417 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4418 * for Android Wear</a> for more information on how to use this class.
4419 * <p>
4420 * To create a notification with wearable extensions:
4421 * <ol>
4422 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4423 * properties.
4424 * <li>Create a {@link android.app.Notification.WearableExtender}.
4425 * <li>Set wearable-specific properties using the
4426 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4427 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4428 * notification.
4429 * <li>Post the notification to the notification system with the
4430 * {@code NotificationManager.notify(...)} methods.
4431 * </ol>
4432 *
4433 * <pre class="prettyprint">
4434 * Notification notif = new Notification.Builder(mContext)
4435 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4436 * .setContentText(subject)
4437 * .setSmallIcon(R.drawable.new_mail)
4438 * .extend(new Notification.WearableExtender()
4439 * .setContentIcon(R.drawable.new_mail))
4440 * .build();
4441 * NotificationManager notificationManger =
4442 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4443 * notificationManger.notify(0, notif);</pre>
4444 *
4445 * <p>Wearable extensions can be accessed on an existing notification by using the
4446 * {@code WearableExtender(Notification)} constructor,
4447 * and then using the {@code get} methods to access values.
4448 *
4449 * <pre class="prettyprint">
4450 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4451 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004452 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004453 */
4454 public static final class WearableExtender implements Extender {
4455 /**
4456 * Sentinel value for an action index that is unset.
4457 */
4458 public static final int UNSET_ACTION_INDEX = -1;
4459
4460 /**
4461 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4462 * default sizing.
4463 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004464 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004465 * on their content.
4466 */
4467 public static final int SIZE_DEFAULT = 0;
4468
4469 /**
4470 * Size value for use with {@link #setCustomSizePreset} to show this notification
4471 * with an extra small size.
4472 * <p>This value is only applicable for custom display notifications created using
4473 * {@link #setDisplayIntent}.
4474 */
4475 public static final int SIZE_XSMALL = 1;
4476
4477 /**
4478 * Size value for use with {@link #setCustomSizePreset} to show this notification
4479 * with a small size.
4480 * <p>This value is only applicable for custom display notifications created using
4481 * {@link #setDisplayIntent}.
4482 */
4483 public static final int SIZE_SMALL = 2;
4484
4485 /**
4486 * Size value for use with {@link #setCustomSizePreset} to show this notification
4487 * with a medium size.
4488 * <p>This value is only applicable for custom display notifications created using
4489 * {@link #setDisplayIntent}.
4490 */
4491 public static final int SIZE_MEDIUM = 3;
4492
4493 /**
4494 * Size value for use with {@link #setCustomSizePreset} to show this notification
4495 * with a large size.
4496 * <p>This value is only applicable for custom display notifications created using
4497 * {@link #setDisplayIntent}.
4498 */
4499 public static final int SIZE_LARGE = 4;
4500
Griff Hazend5f11f92014-05-27 15:40:09 -07004501 /**
4502 * Size value for use with {@link #setCustomSizePreset} to show this notification
4503 * full screen.
4504 * <p>This value is only applicable for custom display notifications created using
4505 * {@link #setDisplayIntent}.
4506 */
4507 public static final int SIZE_FULL_SCREEN = 5;
4508
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004509 /**
4510 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4511 * short amount of time when this notification is displayed on the screen. This
4512 * is the default value.
4513 */
4514 public static final int SCREEN_TIMEOUT_SHORT = 0;
4515
4516 /**
4517 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4518 * for a longer amount of time when this notification is displayed on the screen.
4519 */
4520 public static final int SCREEN_TIMEOUT_LONG = -1;
4521
Griff Hazen61a9e862014-05-22 16:05:19 -07004522 /** Notification extra which contains wearable extensions */
4523 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4524
Pete Gastaf6781d2014-10-07 15:17:05 -04004525 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004526 private static final String KEY_ACTIONS = "actions";
4527 private static final String KEY_FLAGS = "flags";
4528 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4529 private static final String KEY_PAGES = "pages";
4530 private static final String KEY_BACKGROUND = "background";
4531 private static final String KEY_CONTENT_ICON = "contentIcon";
4532 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4533 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4534 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4535 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4536 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004537 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004538
4539 // Flags bitwise-ored to mFlags
4540 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4541 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4542 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4543 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004544 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004545
4546 // Default value for flags integer
4547 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4548
4549 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4550 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4551
4552 private ArrayList<Action> mActions = new ArrayList<Action>();
4553 private int mFlags = DEFAULT_FLAGS;
4554 private PendingIntent mDisplayIntent;
4555 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4556 private Bitmap mBackground;
4557 private int mContentIcon;
4558 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4559 private int mContentActionIndex = UNSET_ACTION_INDEX;
4560 private int mCustomSizePreset = SIZE_DEFAULT;
4561 private int mCustomContentHeight;
4562 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004563 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004564
4565 /**
4566 * Create a {@link android.app.Notification.WearableExtender} with default
4567 * options.
4568 */
4569 public WearableExtender() {
4570 }
4571
4572 public WearableExtender(Notification notif) {
4573 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4574 if (wearableBundle != null) {
4575 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4576 if (actions != null) {
4577 mActions.addAll(actions);
4578 }
4579
4580 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4581 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4582
4583 Notification[] pages = getNotificationArrayFromBundle(
4584 wearableBundle, KEY_PAGES);
4585 if (pages != null) {
4586 Collections.addAll(mPages, pages);
4587 }
4588
4589 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4590 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4591 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4592 DEFAULT_CONTENT_ICON_GRAVITY);
4593 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4594 UNSET_ACTION_INDEX);
4595 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4596 SIZE_DEFAULT);
4597 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4598 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004599 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004600 }
4601 }
4602
4603 /**
4604 * Apply wearable extensions to a notification that is being built. This is typically
4605 * called by the {@link android.app.Notification.Builder#extend} method of
4606 * {@link android.app.Notification.Builder}.
4607 */
4608 @Override
4609 public Notification.Builder extend(Notification.Builder builder) {
4610 Bundle wearableBundle = new Bundle();
4611
4612 if (!mActions.isEmpty()) {
4613 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4614 }
4615 if (mFlags != DEFAULT_FLAGS) {
4616 wearableBundle.putInt(KEY_FLAGS, mFlags);
4617 }
4618 if (mDisplayIntent != null) {
4619 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4620 }
4621 if (!mPages.isEmpty()) {
4622 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4623 new Notification[mPages.size()]));
4624 }
4625 if (mBackground != null) {
4626 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4627 }
4628 if (mContentIcon != 0) {
4629 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4630 }
4631 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4632 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4633 }
4634 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4635 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4636 mContentActionIndex);
4637 }
4638 if (mCustomSizePreset != SIZE_DEFAULT) {
4639 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4640 }
4641 if (mCustomContentHeight != 0) {
4642 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4643 }
4644 if (mGravity != DEFAULT_GRAVITY) {
4645 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4646 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004647 if (mHintScreenTimeout != 0) {
4648 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4649 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004650
4651 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4652 return builder;
4653 }
4654
4655 @Override
4656 public WearableExtender clone() {
4657 WearableExtender that = new WearableExtender();
4658 that.mActions = new ArrayList<Action>(this.mActions);
4659 that.mFlags = this.mFlags;
4660 that.mDisplayIntent = this.mDisplayIntent;
4661 that.mPages = new ArrayList<Notification>(this.mPages);
4662 that.mBackground = this.mBackground;
4663 that.mContentIcon = this.mContentIcon;
4664 that.mContentIconGravity = this.mContentIconGravity;
4665 that.mContentActionIndex = this.mContentActionIndex;
4666 that.mCustomSizePreset = this.mCustomSizePreset;
4667 that.mCustomContentHeight = this.mCustomContentHeight;
4668 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004669 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004670 return that;
4671 }
4672
4673 /**
4674 * Add a wearable action to this notification.
4675 *
4676 * <p>When wearable actions are added using this method, the set of actions that
4677 * show on a wearable device splits from devices that only show actions added
4678 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4679 * of which actions display on different devices.
4680 *
4681 * @param action the action to add to this notification
4682 * @return this object for method chaining
4683 * @see android.app.Notification.Action
4684 */
4685 public WearableExtender addAction(Action action) {
4686 mActions.add(action);
4687 return this;
4688 }
4689
4690 /**
4691 * Adds wearable actions to this notification.
4692 *
4693 * <p>When wearable actions are added using this method, the set of actions that
4694 * show on a wearable device splits from devices that only show actions added
4695 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4696 * of which actions display on different devices.
4697 *
4698 * @param actions the actions to add to this notification
4699 * @return this object for method chaining
4700 * @see android.app.Notification.Action
4701 */
4702 public WearableExtender addActions(List<Action> actions) {
4703 mActions.addAll(actions);
4704 return this;
4705 }
4706
4707 /**
4708 * Clear all wearable actions present on this builder.
4709 * @return this object for method chaining.
4710 * @see #addAction
4711 */
4712 public WearableExtender clearActions() {
4713 mActions.clear();
4714 return this;
4715 }
4716
4717 /**
4718 * Get the wearable actions present on this notification.
4719 */
4720 public List<Action> getActions() {
4721 return mActions;
4722 }
4723
4724 /**
4725 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004726 * this notification. The {@link PendingIntent} provided should be for an activity.
4727 *
4728 * <pre class="prettyprint">
4729 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4730 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4731 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4732 * Notification notif = new Notification.Builder(context)
4733 * .extend(new Notification.WearableExtender()
4734 * .setDisplayIntent(displayPendingIntent)
4735 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4736 * .build();</pre>
4737 *
4738 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004739 * should have an empty task affinity. It is also recommended to use the device
4740 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004741 *
4742 * <p>Example AndroidManifest.xml entry:
4743 * <pre class="prettyprint">
4744 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4745 * android:exported=&quot;true&quot;
4746 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004747 * android:taskAffinity=&quot;&quot;
4748 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004749 *
4750 * @param intent the {@link PendingIntent} for an activity
4751 * @return this object for method chaining
4752 * @see android.app.Notification.WearableExtender#getDisplayIntent
4753 */
4754 public WearableExtender setDisplayIntent(PendingIntent intent) {
4755 mDisplayIntent = intent;
4756 return this;
4757 }
4758
4759 /**
4760 * Get the intent to launch inside of an activity view when displaying this
4761 * notification. This {@code PendingIntent} should be for an activity.
4762 */
4763 public PendingIntent getDisplayIntent() {
4764 return mDisplayIntent;
4765 }
4766
4767 /**
4768 * Add an additional page of content to display with this notification. The current
4769 * notification forms the first page, and pages added using this function form
4770 * subsequent pages. This field can be used to separate a notification into multiple
4771 * sections.
4772 *
4773 * @param page the notification to add as another page
4774 * @return this object for method chaining
4775 * @see android.app.Notification.WearableExtender#getPages
4776 */
4777 public WearableExtender addPage(Notification page) {
4778 mPages.add(page);
4779 return this;
4780 }
4781
4782 /**
4783 * Add additional pages of content to display with this notification. The current
4784 * notification forms the first page, and pages added using this function form
4785 * subsequent pages. This field can be used to separate a notification into multiple
4786 * sections.
4787 *
4788 * @param pages a list of notifications
4789 * @return this object for method chaining
4790 * @see android.app.Notification.WearableExtender#getPages
4791 */
4792 public WearableExtender addPages(List<Notification> pages) {
4793 mPages.addAll(pages);
4794 return this;
4795 }
4796
4797 /**
4798 * Clear all additional pages present on this builder.
4799 * @return this object for method chaining.
4800 * @see #addPage
4801 */
4802 public WearableExtender clearPages() {
4803 mPages.clear();
4804 return this;
4805 }
4806
4807 /**
4808 * Get the array of additional pages of content for displaying this notification. The
4809 * current notification forms the first page, and elements within this array form
4810 * subsequent pages. This field can be used to separate a notification into multiple
4811 * sections.
4812 * @return the pages for this notification
4813 */
4814 public List<Notification> getPages() {
4815 return mPages;
4816 }
4817
4818 /**
4819 * Set a background image to be displayed behind the notification content.
4820 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4821 * will work with any notification style.
4822 *
4823 * @param background the background bitmap
4824 * @return this object for method chaining
4825 * @see android.app.Notification.WearableExtender#getBackground
4826 */
4827 public WearableExtender setBackground(Bitmap background) {
4828 mBackground = background;
4829 return this;
4830 }
4831
4832 /**
4833 * Get a background image to be displayed behind the notification content.
4834 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4835 * will work with any notification style.
4836 *
4837 * @return the background image
4838 * @see android.app.Notification.WearableExtender#setBackground
4839 */
4840 public Bitmap getBackground() {
4841 return mBackground;
4842 }
4843
4844 /**
4845 * Set an icon that goes with the content of this notification.
4846 */
4847 public WearableExtender setContentIcon(int icon) {
4848 mContentIcon = icon;
4849 return this;
4850 }
4851
4852 /**
4853 * Get an icon that goes with the content of this notification.
4854 */
4855 public int getContentIcon() {
4856 return mContentIcon;
4857 }
4858
4859 /**
4860 * Set the gravity that the content icon should have within the notification display.
4861 * Supported values include {@link android.view.Gravity#START} and
4862 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4863 * @see #setContentIcon
4864 */
4865 public WearableExtender setContentIconGravity(int contentIconGravity) {
4866 mContentIconGravity = contentIconGravity;
4867 return this;
4868 }
4869
4870 /**
4871 * Get the gravity that the content icon should have within the notification display.
4872 * Supported values include {@link android.view.Gravity#START} and
4873 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4874 * @see #getContentIcon
4875 */
4876 public int getContentIconGravity() {
4877 return mContentIconGravity;
4878 }
4879
4880 /**
4881 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004882 * this notification. This action will no longer display separately from the
4883 * notification's content.
4884 *
Griff Hazenca48d352014-05-28 22:37:13 -07004885 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004886 * set, although the list of available actions comes from the main notification and not
4887 * from the child page's notification.
4888 *
4889 * @param actionIndex The index of the action to hoist onto the current notification page.
4890 * If wearable actions were added to the main notification, this index
4891 * will apply to that list, otherwise it will apply to the regular
4892 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004893 */
4894 public WearableExtender setContentAction(int actionIndex) {
4895 mContentActionIndex = actionIndex;
4896 return this;
4897 }
4898
4899 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004900 * Get the index of the notification action, if any, that was specified as being clickable
4901 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004902 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004903 *
Griff Hazenca48d352014-05-28 22:37:13 -07004904 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004905 * set, although the list of available actions comes from the main notification and not
4906 * from the child page's notification.
4907 *
4908 * <p>If wearable specific actions were added to the main notification, this index will
4909 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004910 *
4911 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004912 */
4913 public int getContentAction() {
4914 return mContentActionIndex;
4915 }
4916
4917 /**
4918 * Set the gravity that this notification should have within the available viewport space.
4919 * Supported values include {@link android.view.Gravity#TOP},
4920 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4921 * The default value is {@link android.view.Gravity#BOTTOM}.
4922 */
4923 public WearableExtender setGravity(int gravity) {
4924 mGravity = gravity;
4925 return this;
4926 }
4927
4928 /**
4929 * Get the gravity that this notification should have within the available viewport space.
4930 * Supported values include {@link android.view.Gravity#TOP},
4931 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4932 * The default value is {@link android.view.Gravity#BOTTOM}.
4933 */
4934 public int getGravity() {
4935 return mGravity;
4936 }
4937
4938 /**
4939 * Set the custom size preset for the display of this notification out of the available
4940 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4941 * {@link #SIZE_LARGE}.
4942 * <p>Some custom size presets are only applicable for custom display notifications created
4943 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4944 * documentation for the preset in question. See also
4945 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4946 */
4947 public WearableExtender setCustomSizePreset(int sizePreset) {
4948 mCustomSizePreset = sizePreset;
4949 return this;
4950 }
4951
4952 /**
4953 * Get the custom size preset for the display of this notification out of the available
4954 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4955 * {@link #SIZE_LARGE}.
4956 * <p>Some custom size presets are only applicable for custom display notifications created
4957 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4958 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4959 */
4960 public int getCustomSizePreset() {
4961 return mCustomSizePreset;
4962 }
4963
4964 /**
4965 * Set the custom height in pixels for the display of this notification's content.
4966 * <p>This option is only available for custom display notifications created
4967 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4968 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4969 * {@link #getCustomContentHeight}.
4970 */
4971 public WearableExtender setCustomContentHeight(int height) {
4972 mCustomContentHeight = height;
4973 return this;
4974 }
4975
4976 /**
4977 * Get the custom height in pixels for the display of this notification's content.
4978 * <p>This option is only available for custom display notifications created
4979 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4980 * {@link #setCustomContentHeight}.
4981 */
4982 public int getCustomContentHeight() {
4983 return mCustomContentHeight;
4984 }
4985
4986 /**
4987 * Set whether the scrolling position for the contents of this notification should start
4988 * at the bottom of the contents instead of the top when the contents are too long to
4989 * display within the screen. Default is false (start scroll at the top).
4990 */
4991 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4992 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4993 return this;
4994 }
4995
4996 /**
4997 * Get whether the scrolling position for the contents of this notification should start
4998 * at the bottom of the contents instead of the top when the contents are too long to
4999 * display within the screen. Default is false (start scroll at the top).
5000 */
5001 public boolean getStartScrollBottom() {
5002 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
5003 }
5004
5005 /**
5006 * Set whether the content intent is available when the wearable device is not connected
5007 * to a companion device. The user can still trigger this intent when the wearable device
5008 * is offline, but a visual hint will indicate that the content intent may not be available.
5009 * Defaults to true.
5010 */
5011 public WearableExtender setContentIntentAvailableOffline(
5012 boolean contentIntentAvailableOffline) {
5013 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
5014 return this;
5015 }
5016
5017 /**
5018 * Get whether the content intent is available when the wearable device is not connected
5019 * to a companion device. The user can still trigger this intent when the wearable device
5020 * is offline, but a visual hint will indicate that the content intent may not be available.
5021 * Defaults to true.
5022 */
5023 public boolean getContentIntentAvailableOffline() {
5024 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
5025 }
5026
5027 /**
5028 * Set a hint that this notification's icon should not be displayed.
5029 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
5030 * @return this object for method chaining
5031 */
5032 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
5033 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
5034 return this;
5035 }
5036
5037 /**
5038 * Get a hint that this notification's icon should not be displayed.
5039 * @return {@code true} if this icon should not be displayed, false otherwise.
5040 * The default value is {@code false} if this was never set.
5041 */
5042 public boolean getHintHideIcon() {
5043 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
5044 }
5045
5046 /**
5047 * Set a visual hint that only the background image of this notification should be
5048 * displayed, and other semantic content should be hidden. This hint is only applicable
5049 * to sub-pages added using {@link #addPage}.
5050 */
5051 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
5052 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
5053 return this;
5054 }
5055
5056 /**
5057 * Get a visual hint that only the background image of this notification should be
5058 * displayed, and other semantic content should be hidden. This hint is only applicable
5059 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
5060 */
5061 public boolean getHintShowBackgroundOnly() {
5062 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
5063 }
5064
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005065 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005066 * Set a hint that this notification's background should not be clipped if possible,
5067 * and should instead be resized to fully display on the screen, retaining the aspect
5068 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005069 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
5070 * @return this object for method chaining
5071 */
5072 public WearableExtender setHintAvoidBackgroundClipping(
5073 boolean hintAvoidBackgroundClipping) {
5074 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
5075 return this;
5076 }
5077
5078 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005079 * Get a hint that this notification's background should not be clipped if possible,
5080 * and should instead be resized to fully display on the screen, retaining the aspect
5081 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005082 * @return {@code true} if it's ok if the background is clipped on the screen, false
5083 * otherwise. The default value is {@code false} if this was never set.
5084 */
5085 public boolean getHintAvoidBackgroundClipping() {
5086 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5087 }
5088
5089 /**
5090 * Set a hint that the screen should remain on for at least this duration when
5091 * this notification is displayed on the screen.
5092 * @param timeout The requested screen timeout in milliseconds. Can also be either
5093 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5094 * @return this object for method chaining
5095 */
5096 public WearableExtender setHintScreenTimeout(int timeout) {
5097 mHintScreenTimeout = timeout;
5098 return this;
5099 }
5100
5101 /**
5102 * Get the duration, in milliseconds, that the screen should remain on for
5103 * when this notification is displayed.
5104 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5105 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5106 */
5107 public int getHintScreenTimeout() {
5108 return mHintScreenTimeout;
5109 }
5110
Griff Hazen61a9e862014-05-22 16:05:19 -07005111 private void setFlag(int mask, boolean value) {
5112 if (value) {
5113 mFlags |= mask;
5114 } else {
5115 mFlags &= ~mask;
5116 }
5117 }
5118 }
5119
5120 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005121 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5122 * with car extensions:
5123 *
5124 * <ol>
5125 * <li>Create an {@link Notification.Builder}, setting any desired
5126 * properties.
5127 * <li>Create a {@link CarExtender}.
5128 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5129 * {@link CarExtender}.
5130 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5131 * to apply the extensions to a notification.
5132 * </ol>
5133 *
5134 * <pre class="prettyprint">
5135 * Notification notification = new Notification.Builder(context)
5136 * ...
5137 * .extend(new CarExtender()
5138 * .set*(...))
5139 * .build();
5140 * </pre>
5141 *
5142 * <p>Car extensions can be accessed on an existing notification by using the
5143 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5144 * to access values.
5145 */
5146 public static final class CarExtender implements Extender {
5147 private static final String TAG = "CarExtender";
5148
5149 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5150 private static final String EXTRA_LARGE_ICON = "large_icon";
5151 private static final String EXTRA_CONVERSATION = "car_conversation";
5152 private static final String EXTRA_COLOR = "app_color";
5153
5154 private Bitmap mLargeIcon;
5155 private UnreadConversation mUnreadConversation;
5156 private int mColor = Notification.COLOR_DEFAULT;
5157
5158 /**
5159 * Create a {@link CarExtender} with default options.
5160 */
5161 public CarExtender() {
5162 }
5163
5164 /**
5165 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5166 *
5167 * @param notif The notification from which to copy options.
5168 */
5169 public CarExtender(Notification notif) {
5170 Bundle carBundle = notif.extras == null ?
5171 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5172 if (carBundle != null) {
5173 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5174 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5175
5176 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5177 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5178 }
5179 }
5180
5181 /**
5182 * Apply car extensions to a notification that is being built. This is typically called by
5183 * the {@link Notification.Builder#extend(Notification.Extender)}
5184 * method of {@link Notification.Builder}.
5185 */
5186 @Override
5187 public Notification.Builder extend(Notification.Builder builder) {
5188 Bundle carExtensions = new Bundle();
5189
5190 if (mLargeIcon != null) {
5191 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5192 }
5193 if (mColor != Notification.COLOR_DEFAULT) {
5194 carExtensions.putInt(EXTRA_COLOR, mColor);
5195 }
5196
5197 if (mUnreadConversation != null) {
5198 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5199 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5200 }
5201
5202 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5203 return builder;
5204 }
5205
5206 /**
5207 * Sets the accent color to use when Android Auto presents the notification.
5208 *
5209 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5210 * to accent the displayed notification. However, not all colors are acceptable in an
5211 * automotive setting. This method can be used to override the color provided in the
5212 * notification in such a situation.
5213 */
Tor Norbye80756e32015-03-02 09:39:27 -08005214 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005215 mColor = color;
5216 return this;
5217 }
5218
5219 /**
5220 * Gets the accent color.
5221 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005222 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005223 */
Tor Norbye80756e32015-03-02 09:39:27 -08005224 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005225 public int getColor() {
5226 return mColor;
5227 }
5228
5229 /**
5230 * Sets the large icon of the car notification.
5231 *
5232 * If no large icon is set in the extender, Android Auto will display the icon
5233 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5234 *
5235 * @param largeIcon The large icon to use in the car notification.
5236 * @return This object for method chaining.
5237 */
5238 public CarExtender setLargeIcon(Bitmap largeIcon) {
5239 mLargeIcon = largeIcon;
5240 return this;
5241 }
5242
5243 /**
5244 * Gets the large icon used in this car notification, or null if no icon has been set.
5245 *
5246 * @return The large icon for the car notification.
5247 * @see CarExtender#setLargeIcon
5248 */
5249 public Bitmap getLargeIcon() {
5250 return mLargeIcon;
5251 }
5252
5253 /**
5254 * Sets the unread conversation in a message notification.
5255 *
5256 * @param unreadConversation The unread part of the conversation this notification conveys.
5257 * @return This object for method chaining.
5258 */
5259 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5260 mUnreadConversation = unreadConversation;
5261 return this;
5262 }
5263
5264 /**
5265 * Returns the unread conversation conveyed by this notification.
5266 * @see #setUnreadConversation(UnreadConversation)
5267 */
5268 public UnreadConversation getUnreadConversation() {
5269 return mUnreadConversation;
5270 }
5271
5272 /**
5273 * A class which holds the unread messages from a conversation.
5274 */
5275 public static class UnreadConversation {
5276 private static final String KEY_AUTHOR = "author";
5277 private static final String KEY_TEXT = "text";
5278 private static final String KEY_MESSAGES = "messages";
5279 private static final String KEY_REMOTE_INPUT = "remote_input";
5280 private static final String KEY_ON_REPLY = "on_reply";
5281 private static final String KEY_ON_READ = "on_read";
5282 private static final String KEY_PARTICIPANTS = "participants";
5283 private static final String KEY_TIMESTAMP = "timestamp";
5284
5285 private final String[] mMessages;
5286 private final RemoteInput mRemoteInput;
5287 private final PendingIntent mReplyPendingIntent;
5288 private final PendingIntent mReadPendingIntent;
5289 private final String[] mParticipants;
5290 private final long mLatestTimestamp;
5291
5292 UnreadConversation(String[] messages, RemoteInput remoteInput,
5293 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5294 String[] participants, long latestTimestamp) {
5295 mMessages = messages;
5296 mRemoteInput = remoteInput;
5297 mReadPendingIntent = readPendingIntent;
5298 mReplyPendingIntent = replyPendingIntent;
5299 mParticipants = participants;
5300 mLatestTimestamp = latestTimestamp;
5301 }
5302
5303 /**
5304 * Gets the list of messages conveyed by this notification.
5305 */
5306 public String[] getMessages() {
5307 return mMessages;
5308 }
5309
5310 /**
5311 * Gets the remote input that will be used to convey the response to a message list, or
5312 * null if no such remote input exists.
5313 */
5314 public RemoteInput getRemoteInput() {
5315 return mRemoteInput;
5316 }
5317
5318 /**
5319 * Gets the pending intent that will be triggered when the user replies to this
5320 * notification.
5321 */
5322 public PendingIntent getReplyPendingIntent() {
5323 return mReplyPendingIntent;
5324 }
5325
5326 /**
5327 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5328 * in this object's message list.
5329 */
5330 public PendingIntent getReadPendingIntent() {
5331 return mReadPendingIntent;
5332 }
5333
5334 /**
5335 * Gets the participants in the conversation.
5336 */
5337 public String[] getParticipants() {
5338 return mParticipants;
5339 }
5340
5341 /**
5342 * Gets the firs participant in the conversation.
5343 */
5344 public String getParticipant() {
5345 return mParticipants.length > 0 ? mParticipants[0] : null;
5346 }
5347
5348 /**
5349 * Gets the timestamp of the conversation.
5350 */
5351 public long getLatestTimestamp() {
5352 return mLatestTimestamp;
5353 }
5354
5355 Bundle getBundleForUnreadConversation() {
5356 Bundle b = new Bundle();
5357 String author = null;
5358 if (mParticipants != null && mParticipants.length > 1) {
5359 author = mParticipants[0];
5360 }
5361 Parcelable[] messages = new Parcelable[mMessages.length];
5362 for (int i = 0; i < messages.length; i++) {
5363 Bundle m = new Bundle();
5364 m.putString(KEY_TEXT, mMessages[i]);
5365 m.putString(KEY_AUTHOR, author);
5366 messages[i] = m;
5367 }
5368 b.putParcelableArray(KEY_MESSAGES, messages);
5369 if (mRemoteInput != null) {
5370 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5371 }
5372 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5373 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5374 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5375 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5376 return b;
5377 }
5378
5379 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5380 if (b == null) {
5381 return null;
5382 }
5383 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5384 String[] messages = null;
5385 if (parcelableMessages != null) {
5386 String[] tmp = new String[parcelableMessages.length];
5387 boolean success = true;
5388 for (int i = 0; i < tmp.length; i++) {
5389 if (!(parcelableMessages[i] instanceof Bundle)) {
5390 success = false;
5391 break;
5392 }
5393 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5394 if (tmp[i] == null) {
5395 success = false;
5396 break;
5397 }
5398 }
5399 if (success) {
5400 messages = tmp;
5401 } else {
5402 return null;
5403 }
5404 }
5405
5406 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5407 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5408
5409 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5410
5411 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5412 if (participants == null || participants.length != 1) {
5413 return null;
5414 }
5415
5416 return new UnreadConversation(messages,
5417 remoteInput,
5418 onReply,
5419 onRead,
5420 participants, b.getLong(KEY_TIMESTAMP));
5421 }
5422 };
5423
5424 /**
5425 * Builder class for {@link CarExtender.UnreadConversation} objects.
5426 */
5427 public static class Builder {
5428 private final List<String> mMessages = new ArrayList<String>();
5429 private final String mParticipant;
5430 private RemoteInput mRemoteInput;
5431 private PendingIntent mReadPendingIntent;
5432 private PendingIntent mReplyPendingIntent;
5433 private long mLatestTimestamp;
5434
5435 /**
5436 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5437 *
5438 * @param name The name of the other participant in the conversation.
5439 */
5440 public Builder(String name) {
5441 mParticipant = name;
5442 }
5443
5444 /**
5445 * Appends a new unread message to the list of messages for this conversation.
5446 *
5447 * The messages should be added from oldest to newest.
5448 *
5449 * @param message The text of the new unread message.
5450 * @return This object for method chaining.
5451 */
5452 public Builder addMessage(String message) {
5453 mMessages.add(message);
5454 return this;
5455 }
5456
5457 /**
5458 * Sets the pending intent and remote input which will convey the reply to this
5459 * notification.
5460 *
5461 * @param pendingIntent The pending intent which will be triggered on a reply.
5462 * @param remoteInput The remote input parcelable which will carry the reply.
5463 * @return This object for method chaining.
5464 *
5465 * @see CarExtender.UnreadConversation#getRemoteInput
5466 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5467 */
5468 public Builder setReplyAction(
5469 PendingIntent pendingIntent, RemoteInput remoteInput) {
5470 mRemoteInput = remoteInput;
5471 mReplyPendingIntent = pendingIntent;
5472
5473 return this;
5474 }
5475
5476 /**
5477 * Sets the pending intent that will be sent once the messages in this notification
5478 * are read.
5479 *
5480 * @param pendingIntent The pending intent to use.
5481 * @return This object for method chaining.
5482 */
5483 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5484 mReadPendingIntent = pendingIntent;
5485 return this;
5486 }
5487
5488 /**
5489 * Sets the timestamp of the most recent message in an unread conversation.
5490 *
5491 * If a messaging notification has been posted by your application and has not
5492 * yet been cancelled, posting a later notification with the same id and tag
5493 * but without a newer timestamp may result in Android Auto not displaying a
5494 * heads up notification for the later notification.
5495 *
5496 * @param timestamp The timestamp of the most recent message in the conversation.
5497 * @return This object for method chaining.
5498 */
5499 public Builder setLatestTimestamp(long timestamp) {
5500 mLatestTimestamp = timestamp;
5501 return this;
5502 }
5503
5504 /**
5505 * Builds a new unread conversation object.
5506 *
5507 * @return The new unread conversation object.
5508 */
5509 public UnreadConversation build() {
5510 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5511 String[] participants = { mParticipant };
5512 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5513 mReadPendingIntent, participants, mLatestTimestamp);
5514 }
5515 }
5516 }
5517
5518 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005519 * Get an array of Notification objects from a parcelable array bundle field.
5520 * Update the bundle to have a typed array so fetches in the future don't need
5521 * to do an array copy.
5522 */
5523 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5524 Parcelable[] array = bundle.getParcelableArray(key);
5525 if (array instanceof Notification[] || array == null) {
5526 return (Notification[]) array;
5527 }
5528 Notification[] typedArray = Arrays.copyOf(array, array.length,
5529 Notification[].class);
5530 bundle.putParcelableArray(key, typedArray);
5531 return typedArray;
5532 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005533
5534 private static class BuilderRemoteViews extends RemoteViews {
5535 public BuilderRemoteViews(Parcel parcel) {
5536 super(parcel);
5537 }
5538
Kenny Guy77320062014-08-27 21:37:15 +01005539 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5540 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005541 }
5542
5543 @Override
5544 public BuilderRemoteViews clone() {
5545 Parcel p = Parcel.obtain();
5546 writeToParcel(p, 0);
5547 p.setDataPosition(0);
5548 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5549 p.recycle();
5550 return brv;
5551 }
5552 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005553}