blob: 0b77be3eda20972d530ad1893045ddd9e20135bc [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
1469 private Topic[] topics;
1470
1471 public Topic[] getTopics() {
1472 return topics;
1473 }
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
1602 topics = parcel.createTypedArray(Topic.CREATOR); // may be null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603 }
1604
Andy Stadler110988c2010-12-03 14:29:16 -08001605 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001606 public Notification clone() {
1607 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001608 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001609 return that;
1610 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001611
Daniel Sandler1a497d32013-04-18 14:52:45 -04001612 /**
1613 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1614 * of this into that.
1615 * @hide
1616 */
1617 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001618 that.when = this.when;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001619 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001620 that.number = this.number;
1621
1622 // PendingIntents are global, so there's no reason (or way) to clone them.
1623 that.contentIntent = this.contentIntent;
1624 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001625 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001626
1627 if (this.tickerText != null) {
1628 that.tickerText = this.tickerText.toString();
1629 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001630 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001631 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001632 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001633 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001634 that.contentView = this.contentView.clone();
1635 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001636 if (heavy && this.mLargeIcon != null) {
1637 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001638 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001639 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001640 that.sound = this.sound; // android.net.Uri is immutable
1641 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001642 if (this.audioAttributes != null) {
1643 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1644 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001645
1646 final long[] vibrate = this.vibrate;
1647 if (vibrate != null) {
1648 final int N = vibrate.length;
1649 final long[] vib = that.vibrate = new long[N];
1650 System.arraycopy(vibrate, 0, vib, 0, N);
1651 }
1652
1653 that.ledARGB = this.ledARGB;
1654 that.ledOnMS = this.ledOnMS;
1655 that.ledOffMS = this.ledOffMS;
1656 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001657
Joe Onorato18e69df2010-05-17 22:26:12 -07001658 that.flags = this.flags;
1659
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001660 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001661
John Spurlockfd7f1e02014-03-18 16:41:57 -04001662 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001663
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001664 that.mGroupKey = this.mGroupKey;
1665
1666 that.mSortKey = this.mSortKey;
1667
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001668 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001669 try {
1670 that.extras = new Bundle(this.extras);
1671 // will unparcel
1672 that.extras.size();
1673 } catch (BadParcelableException e) {
1674 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1675 that.extras = null;
1676 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001677 }
1678
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001679 if (this.actions != null) {
1680 that.actions = new Action[this.actions.length];
1681 for(int i=0; i<this.actions.length; i++) {
1682 that.actions[i] = this.actions[i].clone();
1683 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001684 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001685
Daniel Sandler1a497d32013-04-18 14:52:45 -04001686 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001687 that.bigContentView = this.bigContentView.clone();
1688 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001689
Chris Wren8fd39ec2014-02-27 17:43:26 -05001690 if (heavy && this.headsUpContentView != null) {
1691 that.headsUpContentView = this.headsUpContentView.clone();
1692 }
1693
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001694 that.visibility = this.visibility;
1695
1696 if (this.publicVersion != null) {
1697 that.publicVersion = new Notification();
1698 this.publicVersion.cloneInto(that.publicVersion, heavy);
1699 }
1700
Dan Sandler26e81cf2014-05-06 10:01:27 -04001701 that.color = this.color;
1702
Julia Reynolds74303cf2015-10-16 11:37:55 -04001703 if (this.topics != null) {
1704 that.topics = new Topic[this.topics.length];
1705 for(int i=0; i<this.topics.length; i++) {
1706 that.topics[i] = this.topics[i].clone();
1707 }
1708 }
1709
Daniel Sandler1a497d32013-04-18 14:52:45 -04001710 if (!heavy) {
1711 that.lightenPayload(); // will clean out extras
1712 }
1713 }
1714
1715 /**
1716 * Removes heavyweight parts of the Notification object for archival or for sending to
1717 * listeners when the full contents are not necessary.
1718 * @hide
1719 */
1720 public final void lightenPayload() {
1721 tickerView = null;
1722 contentView = null;
1723 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001724 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001725 mLargeIcon = null;
Daniel Sandler1a497d32013-04-18 14:52:45 -04001726 if (extras != null) {
1727 extras.remove(Notification.EXTRA_LARGE_ICON);
1728 extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1729 extras.remove(Notification.EXTRA_PICTURE);
Christoph Studer223f44e2014-09-02 14:59:32 +02001730 extras.remove(Notification.EXTRA_BIG_TEXT);
Daniel Sandler1a497d32013-04-18 14:52:45 -04001731 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001732 }
1733
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001734 /**
1735 * Make sure this CharSequence is safe to put into a bundle, which basically
1736 * means it had better not be some custom Parcelable implementation.
1737 * @hide
1738 */
1739 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001740 if (cs == null) return cs;
1741 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1742 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1743 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001744 if (cs instanceof Parcelable) {
1745 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1746 + " instance is a custom Parcelable and not allowed in Notification");
1747 return cs.toString();
1748 }
1749
1750 return cs;
1751 }
1752
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753 public int describeContents() {
1754 return 0;
1755 }
1756
1757 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001758 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001759 */
1760 public void writeToParcel(Parcel parcel, int flags)
1761 {
1762 parcel.writeInt(1);
1763
1764 parcel.writeLong(when);
Dan Sandler4e787062015-06-17 15:09:48 -04001765 if (mSmallIcon == null && icon != 0) {
1766 // you snuck an icon in here without using the builder; let's try to keep it
1767 mSmallIcon = Icon.createWithResource("", icon);
1768 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001769 if (mSmallIcon != null) {
1770 parcel.writeInt(1);
1771 mSmallIcon.writeToParcel(parcel, 0);
1772 } else {
1773 parcel.writeInt(0);
1774 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001775 parcel.writeInt(number);
1776 if (contentIntent != null) {
1777 parcel.writeInt(1);
1778 contentIntent.writeToParcel(parcel, 0);
1779 } else {
1780 parcel.writeInt(0);
1781 }
1782 if (deleteIntent != null) {
1783 parcel.writeInt(1);
1784 deleteIntent.writeToParcel(parcel, 0);
1785 } else {
1786 parcel.writeInt(0);
1787 }
1788 if (tickerText != null) {
1789 parcel.writeInt(1);
1790 TextUtils.writeToParcel(tickerText, parcel, flags);
1791 } else {
1792 parcel.writeInt(0);
1793 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001794 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001795 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001796 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001797 } else {
1798 parcel.writeInt(0);
1799 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001800 if (contentView != null) {
1801 parcel.writeInt(1);
1802 contentView.writeToParcel(parcel, 0);
1803 } else {
1804 parcel.writeInt(0);
1805 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001806 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001807 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001808 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001809 } else {
1810 parcel.writeInt(0);
1811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812
1813 parcel.writeInt(defaults);
1814 parcel.writeInt(this.flags);
1815
1816 if (sound != null) {
1817 parcel.writeInt(1);
1818 sound.writeToParcel(parcel, 0);
1819 } else {
1820 parcel.writeInt(0);
1821 }
1822 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001823
1824 if (audioAttributes != null) {
1825 parcel.writeInt(1);
1826 audioAttributes.writeToParcel(parcel, 0);
1827 } else {
1828 parcel.writeInt(0);
1829 }
1830
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001831 parcel.writeLongArray(vibrate);
1832 parcel.writeInt(ledARGB);
1833 parcel.writeInt(ledOnMS);
1834 parcel.writeInt(ledOffMS);
1835 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001836
1837 if (fullScreenIntent != null) {
1838 parcel.writeInt(1);
1839 fullScreenIntent.writeToParcel(parcel, 0);
1840 } else {
1841 parcel.writeInt(0);
1842 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001843
1844 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001845
John Spurlockfd7f1e02014-03-18 16:41:57 -04001846 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001847
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001848 parcel.writeString(mGroupKey);
1849
1850 parcel.writeString(mSortKey);
1851
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001852 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001853
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001854 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001855
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001856 if (bigContentView != null) {
1857 parcel.writeInt(1);
1858 bigContentView.writeToParcel(parcel, 0);
1859 } else {
1860 parcel.writeInt(0);
1861 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001862
Chris Wren8fd39ec2014-02-27 17:43:26 -05001863 if (headsUpContentView != null) {
1864 parcel.writeInt(1);
1865 headsUpContentView.writeToParcel(parcel, 0);
1866 } else {
1867 parcel.writeInt(0);
1868 }
1869
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001870 parcel.writeInt(visibility);
1871
1872 if (publicVersion != null) {
1873 parcel.writeInt(1);
1874 publicVersion.writeToParcel(parcel, 0);
1875 } else {
1876 parcel.writeInt(0);
1877 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001878
1879 parcel.writeInt(color);
Julia Reynolds74303cf2015-10-16 11:37:55 -04001880
1881 parcel.writeTypedArray(topics, 0); // null ok
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 }
1883
1884 /**
1885 * Parcelable.Creator that instantiates Notification objects
1886 */
1887 public static final Parcelable.Creator<Notification> CREATOR
1888 = new Parcelable.Creator<Notification>()
1889 {
1890 public Notification createFromParcel(Parcel parcel)
1891 {
1892 return new Notification(parcel);
1893 }
1894
1895 public Notification[] newArray(int size)
1896 {
1897 return new Notification[size];
1898 }
1899 };
1900
1901 /**
1902 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1903 * layout.
1904 *
1905 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1906 * in the view.</p>
1907 * @param context The context for your application / activity.
1908 * @param contentTitle The title that goes in the expanded entry.
1909 * @param contentText The text that goes in the expanded entry.
1910 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1911 * If this is an activity, it must include the
1912 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001913 * that you take care of task management as described in the
1914 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1915 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001916 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001917 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001918 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001919 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001920 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001921 public void setLatestEventInfo(Context context,
1922 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001923 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1924 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1925 new Throwable());
1926 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001927
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001928 // ensure that any information already set directly is preserved
1929 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001930
1931 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001932 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001933 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 }
1935 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001936 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001937 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001938 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001939
1940 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 }
1942
1943 @Override
1944 public String toString() {
1945 StringBuilder sb = new StringBuilder();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001946 sb.append("Notification(pri=");
1947 sb.append(priority);
1948 sb.append(" contentView=");
Joe Onoratoc9596d62011-01-12 17:03:11 -08001949 if (contentView != null) {
1950 sb.append(contentView.getPackage());
1951 sb.append("/0x");
1952 sb.append(Integer.toHexString(contentView.getLayoutId()));
1953 } else {
1954 sb.append("null");
1955 }
1956 sb.append(" vibrate=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001957 if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1958 sb.append("default");
1959 } else if (this.vibrate != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001960 int N = this.vibrate.length-1;
1961 sb.append("[");
1962 for (int i=0; i<N; i++) {
1963 sb.append(this.vibrate[i]);
1964 sb.append(',');
1965 }
Simon Schoar8cf97d92009-06-10 22:08:37 +02001966 if (N != -1) {
1967 sb.append(this.vibrate[N]);
1968 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 sb.append("]");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001970 } else {
1971 sb.append("null");
1972 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001973 sb.append(" sound=");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001974 if ((this.defaults & DEFAULT_SOUND) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001975 sb.append("default");
Daniel Sandler6738eee2012-11-16 12:03:32 -05001976 } else if (this.sound != null) {
1977 sb.append(this.sound.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001978 } else {
1979 sb.append("null");
1980 }
Chris Wren365b6d32015-07-16 10:39:26 -04001981 if (this.tickerText != null) {
1982 sb.append(" tick");
1983 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001984 sb.append(" defaults=0x");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 sb.append(Integer.toHexString(this.defaults));
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001986 sb.append(" flags=0x");
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001987 sb.append(Integer.toHexString(this.flags));
Dan Sandler26e81cf2014-05-06 10:01:27 -04001988 sb.append(String.format(" color=0x%08x", this.color));
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001989 if (this.category != null) {
1990 sb.append(" category=");
1991 sb.append(this.category);
1992 }
1993 if (this.mGroupKey != null) {
1994 sb.append(" groupKey=");
1995 sb.append(this.mGroupKey);
1996 }
1997 if (this.mSortKey != null) {
1998 sb.append(" sortKey=");
1999 sb.append(this.mSortKey);
2000 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002001 if (actions != null) {
Dan Sandler1b718782014-07-18 12:43:45 -04002002 sb.append(" actions=");
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002003 sb.append(actions.length);
Dan Sandler1b718782014-07-18 12:43:45 -04002004 }
2005 sb.append(" vis=");
2006 sb.append(visibilityToString(this.visibility));
2007 if (this.publicVersion != null) {
2008 sb.append(" publicVersion=");
2009 sb.append(publicVersion.toString());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002010 }
Julia Reynolds74303cf2015-10-16 11:37:55 -04002011 if (topics != null) {
2012 sb.append("topics=[");
2013 int N = topics.length;
2014 if (N > 0) {
2015 for (int i = 0; i < N-1; i++) {
2016 sb.append(topics[i]);
2017 sb.append(',');
2018 }
2019 sb.append(topics[N-1]);
2020 }
2021 sb.append("]");
2022 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002023 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 return sb.toString();
2025 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002026
Dan Sandler1b718782014-07-18 12:43:45 -04002027 /**
2028 * {@hide}
2029 */
2030 public static String visibilityToString(int vis) {
2031 switch (vis) {
2032 case VISIBILITY_PRIVATE:
2033 return "PRIVATE";
2034 case VISIBILITY_PUBLIC:
2035 return "PUBLIC";
2036 case VISIBILITY_SECRET:
2037 return "SECRET";
2038 default:
2039 return "UNKNOWN(" + String.valueOf(vis) + ")";
2040 }
2041 }
2042
Joe Onoratocb109a02011-01-18 17:57:41 -08002043 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002044 * {@hide}
2045 */
2046 public static String priorityToString(@Priority int pri) {
2047 switch (pri) {
2048 case PRIORITY_MIN:
2049 return "MIN";
2050 case PRIORITY_LOW:
2051 return "LOW";
2052 case PRIORITY_DEFAULT:
2053 return "DEFAULT";
2054 case PRIORITY_HIGH:
2055 return "HIGH";
2056 case PRIORITY_MAX:
2057 return "MAX";
2058 default:
2059 return "UNKNOWN(" + String.valueOf(pri) + ")";
2060 }
2061 }
2062
2063 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002064 * The small icon representing this notification in the status bar and content view.
2065 *
2066 * @return the small icon representing this notification.
2067 *
2068 * @see Builder#getSmallIcon()
2069 * @see Builder#setSmallIcon(Icon)
2070 */
2071 public Icon getSmallIcon() {
2072 return mSmallIcon;
2073 }
2074
2075 /**
2076 * Used when notifying to clean up legacy small icons.
2077 * @hide
2078 */
2079 public void setSmallIcon(Icon icon) {
2080 mSmallIcon = icon;
2081 }
2082
2083 /**
2084 * The large icon shown in this notification's content view.
2085 * @see Builder#getLargeIcon()
2086 * @see Builder#setLargeIcon(Icon)
2087 */
2088 public Icon getLargeIcon() {
2089 return mLargeIcon;
2090 }
2091
2092 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002093 * @hide
2094 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002095 public boolean isGroupSummary() {
2096 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2097 }
2098
2099 /**
2100 * @hide
2101 */
2102 public boolean isGroupChild() {
2103 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2104 }
2105
2106 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002107 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002108 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002109 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002110 * content views using the platform's notification layout template. If your app supports
2111 * versions of Android as old as API level 4, you can instead use
2112 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2113 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2114 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002115 *
Scott Main183bf112012-08-13 19:12:13 -07002116 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002117 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002118 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002119 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002120 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2121 * .setContentText(subject)
2122 * .setSmallIcon(R.drawable.new_mail)
2123 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002124 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002125 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002126 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002127 public static class Builder {
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002128 private static final int MAX_ACTION_BUTTONS = 3;
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002129 private static final float LARGE_TEXT_SCALE = 1.3f;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002130
Joe Onorato46439ce2010-11-19 13:56:21 -08002131 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002132 private Notification mN;
2133 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002134 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002135 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2136 private ArrayList<String> mPersonList = new ArrayList<String>();
2137 private NotificationColorUtil mColorUtil;
2138 private boolean mColorUtilInited = false;
Julia Reynolds74303cf2015-10-16 11:37:55 -04002139 private List<Topic> mTopics = new ArrayList<>();
Christoph Studer7ac80e62014-08-04 16:01:57 +02002140
2141 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +01002142 * The user that built the notification originally.
2143 */
2144 private int mOriginatingUserId;
2145
2146 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002147 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002148 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002149
2150 * <table>
2151 * <tr><th align=right>priority</th>
2152 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2153 * <tr><th align=right>when</th>
2154 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2155 * <tr><th align=right>audio stream</th>
2156 * <td>{@link #STREAM_DEFAULT}</td></tr>
2157 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002158 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002159
2160 * @param context
2161 * A {@link Context} that will be used by the Builder to construct the
2162 * RemoteViews. The Context will not be held past the lifetime of this Builder
2163 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002164 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002165 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002166 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002167 }
2168
Joe Onoratocb109a02011-01-18 17:57:41 -08002169 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002170 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002171 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002172 public Builder(Context context, Notification toAdopt) {
2173 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002174
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002175 if (toAdopt == null) {
2176 mN = new Notification();
2177 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2178 mN.priority = PRIORITY_DEFAULT;
2179 mN.visibility = VISIBILITY_PRIVATE;
2180 } else {
2181 mN = toAdopt;
2182 if (mN.actions != null) {
2183 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002184 }
2185
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002186 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2187 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2188 }
2189
2190 if (mN.getTopics() != null) {
2191 Collections.addAll(mTopics, mN.getTopics());
2192 }
2193
2194 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2195 if (!TextUtils.isEmpty(templateClass)) {
2196 final Class<? extends Style> styleClass
2197 = getNotificationStyleClass(templateClass);
2198 if (styleClass == null) {
2199 Log.d(TAG, "Unknown style class: " + templateClass);
2200 } else {
2201 try {
2202 final Constructor<? extends Style> ctor = styleClass.getConstructor();
2203 ctor.setAccessible(true);
2204 final Style style = ctor.newInstance();
2205 style.restoreFromExtras(mN.extras);
2206
2207 if (style != null) {
2208 setStyle(style);
2209 }
2210 } catch (Throwable t) {
2211 Log.e(TAG, "Could not create Style", t);
2212 }
2213 }
2214 }
2215
2216 }
2217 }
2218
2219 private NotificationColorUtil getColorUtil() {
2220 if (!mColorUtilInited) {
2221 mColorUtilInited = true;
2222 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2223 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002224 }
2225 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002226 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002227 }
2228
2229 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002230 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Daniel Sandler0c890492012-09-12 17:23:10 -07002231 * It will be shown in the notification content view by default; use
Griff Hazen50c11652014-05-16 09:46:31 -07002232 * {@link #setShowWhen(boolean) setShowWhen} to control this.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002233 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002234 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002235 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002236 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002237 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002238 return this;
2239 }
2240
Joe Onoratocb109a02011-01-18 17:57:41 -08002241 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002242 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002243 * in the content view.
2244 */
2245 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002246 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002247 return this;
2248 }
2249
2250 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002251 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002252 *
2253 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002254 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002255 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002256 * Useful when showing an elapsed time (like an ongoing phone call).
2257 *
2258 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002259 * @see Notification#when
2260 */
2261 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002262 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002263 return this;
2264 }
2265
2266 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002267 * Set the small icon resource, which will be used to represent the notification in the
2268 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002269 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002270
2271 * The platform template for the expanded view will draw this icon in the left, unless a
2272 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2273 * icon will be moved to the right-hand side.
2274 *
2275
2276 * @param icon
2277 * A resource ID in the application's package of the drawable to use.
2278 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002279 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002280 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002281 return setSmallIcon(icon != 0
2282 ? Icon.createWithResource(mContext, icon)
2283 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002284 }
2285
Joe Onoratocb109a02011-01-18 17:57:41 -08002286 /**
2287 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2288 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2289 * LevelListDrawable}.
2290 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002291 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002292 * @param level The level to use for the icon.
2293 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002294 * @see Notification#icon
2295 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002296 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002297 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002298 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002299 return setSmallIcon(icon);
2300 }
2301
2302 /**
2303 * Set the small icon, which will be used to represent the notification in the
2304 * status bar and content view (unless overriden there by a
2305 * {@link #setLargeIcon(Bitmap) large icon}).
2306 *
2307 * @param icon An Icon object to use.
2308 * @see Notification#icon
2309 */
2310 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002311 mN.setSmallIcon(icon);
2312 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2313 mN.icon = icon.getResId();
2314 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002315 return this;
2316 }
2317
Joe Onoratocb109a02011-01-18 17:57:41 -08002318 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002319 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002320 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002321 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002322 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002323 return this;
2324 }
2325
Joe Onoratocb109a02011-01-18 17:57:41 -08002326 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002327 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002328 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002329 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002330 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002331 return this;
2332 }
2333
Joe Onoratocb109a02011-01-18 17:57:41 -08002334 /**
Joe Malin8d40d042012-11-05 11:36:40 -08002335 * Set the third line of text in the platform notification template.
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002336 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2337 * same location in the standard template.
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002338 */
2339 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002340 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002341 return this;
2342 }
2343
2344 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002345 * Set the large number at the right-hand side of the notification. This is
2346 * equivalent to setContentInfo, although it might show the number in a different
2347 * font size for readability.
2348 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002349 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002350 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002351 return this;
2352 }
2353
Joe Onoratocb109a02011-01-18 17:57:41 -08002354 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002355 * A small piece of additional information pertaining to this notification.
2356 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002357 * The platform template will draw this on the last line of the notification, at the far
2358 * right (to the right of a smallIcon if it has been placed there).
Joe Onoratocb109a02011-01-18 17:57:41 -08002359 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002360 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002361 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002362 return this;
2363 }
2364
Joe Onoratocb109a02011-01-18 17:57:41 -08002365 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002366 * Set the progress this notification represents.
2367 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002368 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002369 */
2370 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002371 mN.extras.putInt(EXTRA_PROGRESS, progress);
2372 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2373 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002374 return this;
2375 }
2376
2377 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002378 * Supply a custom RemoteViews to use instead of the platform template.
2379 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002380 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002381 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002382 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002383 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002384 return setCustomContentView(views);
2385 }
2386
2387 /**
2388 * Supply custom RemoteViews to use instead of the platform template.
2389 *
2390 * This will override the layout that would otherwise be constructed by this Builder
2391 * object.
2392 */
2393 public Builder setCustomContentView(RemoteViews contentView) {
2394 mN.contentView = contentView;
2395 return this;
2396 }
2397
2398 /**
2399 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2400 *
2401 * This will override the expanded layout that would otherwise be constructed by this
2402 * Builder object.
2403 */
2404 public Builder setCustomBigContentView(RemoteViews contentView) {
2405 mN.bigContentView = contentView;
2406 return this;
2407 }
2408
2409 /**
2410 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2411 *
2412 * This will override the heads-up layout that would otherwise be constructed by this
2413 * Builder object.
2414 */
2415 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2416 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002417 return this;
2418 }
2419
Joe Onoratocb109a02011-01-18 17:57:41 -08002420 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002421 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2422 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002423 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2424 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2425 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002426 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002427 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002428 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002429 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002430 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002431 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002432 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002433 return this;
2434 }
2435
Joe Onoratocb109a02011-01-18 17:57:41 -08002436 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002437 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2438 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002439 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002440 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002441 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002442 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002443 return this;
2444 }
2445
Joe Onoratocb109a02011-01-18 17:57:41 -08002446 /**
2447 * An intent to launch instead of posting the notification to the status bar.
2448 * Only for use with extremely high-priority notifications demanding the user's
2449 * <strong>immediate</strong> attention, such as an incoming phone call or
2450 * alarm clock that the user has explicitly set to a particular time.
2451 * If this facility is used for something else, please give the user an option
2452 * to turn it off and use a normal notification, as this can be extremely
2453 * disruptive.
2454 *
Chris Wren47c20a12014-06-18 17:27:29 -04002455 * <p>
2456 * The system UI may choose to display a heads-up notification, instead of
2457 * launching this intent, while the user is using the device.
2458 * </p>
2459 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002460 * @param intent The pending intent to launch.
2461 * @param highPriority Passing true will cause this notification to be sent
2462 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002463 *
2464 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002465 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002466 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002467 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002468 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2469 return this;
2470 }
2471
Joe Onoratocb109a02011-01-18 17:57:41 -08002472 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002473 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002474 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002475 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002476 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002477 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002478 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002479 return this;
2480 }
2481
Joe Onoratocb109a02011-01-18 17:57:41 -08002482 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002483 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002484 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002485 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002486 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002487 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002488 setTicker(tickerText);
2489 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002490 return this;
2491 }
2492
Joe Onoratocb109a02011-01-18 17:57:41 -08002493 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002494 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002495 *
2496 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002497 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2498 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002499 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002500 public Builder setLargeIcon(Bitmap b) {
2501 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2502 }
2503
2504 /**
2505 * Add a large icon to the notification content view.
2506 *
2507 * In the platform template, this image will be shown on the left of the notification view
2508 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2509 * badge atop the large icon).
2510 */
2511 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002512 mN.mLargeIcon = icon;
2513 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002514 return this;
2515 }
2516
Joe Onoratocb109a02011-01-18 17:57:41 -08002517 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002518 * Set the sound to play.
2519 *
John Spurlockc0650f022014-07-19 13:22:39 -04002520 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2521 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002522 *
Chris Wren47c20a12014-06-18 17:27:29 -04002523 * <p>
2524 * A notification that is noisy is more likely to be presented as a heads-up notification.
2525 * </p>
2526 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002527 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002528 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002529 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002530 mN.sound = sound;
2531 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -08002532 return this;
2533 }
2534
Joe Onoratocb109a02011-01-18 17:57:41 -08002535 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002536 * Set the sound to play, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002537 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002538 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2539 *
Chris Wren47c20a12014-06-18 17:27:29 -04002540 * <p>
2541 * A notification that is noisy is more likely to be presented as a heads-up notification.
2542 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002543 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002544 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002545 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002546 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002547 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002548 mN.sound = sound;
2549 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002550 return this;
2551 }
2552
Joe Onoratocb109a02011-01-18 17:57:41 -08002553 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002554 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2555 * use during playback.
2556 *
2557 * <p>
2558 * A notification that is noisy is more likely to be presented as a heads-up notification.
2559 * </p>
2560 *
2561 * @see Notification#sound
2562 */
2563 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002564 mN.sound = sound;
2565 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002566 return this;
2567 }
2568
2569 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002570 * Set the vibration pattern to use.
2571 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002572 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2573 * <code>pattern</code> parameter.
2574 *
Chris Wren47c20a12014-06-18 17:27:29 -04002575 * <p>
2576 * A notification that vibrates is more likely to be presented as a heads-up notification.
2577 * </p>
2578 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002579 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002580 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002581 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002582 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002583 return this;
2584 }
2585
Joe Onoratocb109a02011-01-18 17:57:41 -08002586 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002587 * Set the desired color for the indicator LED on the device, as well as the
2588 * blink duty cycle (specified in milliseconds).
2589 *
2590
2591 * Not all devices will honor all (or even any) of these values.
2592 *
2593
2594 * @see Notification#ledARGB
2595 * @see Notification#ledOnMS
2596 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002597 */
Tor Norbye80756e32015-03-02 09:39:27 -08002598 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002599 mN.ledARGB = argb;
2600 mN.ledOnMS = onMs;
2601 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002602 if (onMs != 0 || offMs != 0) {
2603 mN.flags |= FLAG_SHOW_LIGHTS;
2604 }
2605 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
2606 mN.flags |= FLAG_SHOW_LIGHTS;
2607 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002608 return this;
2609 }
2610
Joe Onoratocb109a02011-01-18 17:57:41 -08002611 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002612 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002613 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002614
2615 * Ongoing notifications cannot be dismissed by the user, so your application or service
2616 * must take care of canceling them.
2617 *
2618
2619 * They are typically used to indicate a background task that the user is actively engaged
2620 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2621 * (e.g., a file download, sync operation, active network connection).
2622 *
2623
2624 * @see Notification#FLAG_ONGOING_EVENT
2625 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002626 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002627 public Builder setOngoing(boolean ongoing) {
2628 setFlag(FLAG_ONGOING_EVENT, ongoing);
2629 return this;
2630 }
2631
Joe Onoratocb109a02011-01-18 17:57:41 -08002632 /**
2633 * Set this flag if you would only like the sound, vibrate
2634 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002635 *
2636 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002637 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002638 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2639 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2640 return this;
2641 }
2642
Joe Onoratocb109a02011-01-18 17:57:41 -08002643 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002644 * Make this notification automatically dismissed when the user touches it. The
2645 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2646 *
2647 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002648 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002649 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002650 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002651 return this;
2652 }
2653
Joe Onoratocb109a02011-01-18 17:57:41 -08002654 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002655 * Set whether or not this notification should not bridge to other devices.
2656 *
2657 * <p>Some notifications can be bridged to other devices for remote display.
2658 * This hint can be set to recommend this notification not be bridged.
2659 */
2660 public Builder setLocalOnly(boolean localOnly) {
2661 setFlag(FLAG_LOCAL_ONLY, localOnly);
2662 return this;
2663 }
2664
2665 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002666 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002667 * <p>
2668 * The value should be one or more of the following fields combined with
2669 * bitwise-or:
2670 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2671 * <p>
2672 * For all default values, use {@link #DEFAULT_ALL}.
2673 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002674 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002675 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002676 return this;
2677 }
2678
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002679 /**
2680 * Set the priority of this notification.
2681 *
2682 * @see Notification#priority
2683 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002684 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002685 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002686 return this;
2687 }
Joe Malin8d40d042012-11-05 11:36:40 -08002688
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002689 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002690 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002691 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002692 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002693 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002694 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002695 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002696 return this;
2697 }
2698
2699 /**
Chris Wrendde75302014-03-26 17:24:15 -04002700 * Add a person that is relevant to this notification.
2701 *
Chris Wrene6c48932014-09-29 17:19:27 -04002702 * <P>
2703 * Depending on user preferences, this annotation may allow the notification to pass
2704 * through interruption filters, and to appear more prominently in the user interface.
2705 * </P>
2706 *
2707 * <P>
2708 * The person should be specified by the {@code String} representation of a
2709 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2710 * </P>
2711 *
2712 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2713 * URIs. The path part of these URIs must exist in the contacts database, in the
2714 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2715 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2716 * </P>
2717 *
2718 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002719 * @see Notification#EXTRA_PEOPLE
2720 */
Chris Wrene6c48932014-09-29 17:19:27 -04002721 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002722 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002723 return this;
2724 }
2725
2726 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002727 * Set this notification to be part of a group of notifications sharing the same key.
2728 * Grouped notifications may display in a cluster or stack on devices which
2729 * support such rendering.
2730 *
2731 * <p>To make this notification the summary for its group, also call
2732 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2733 * {@link #setSortKey}.
2734 * @param groupKey The group key of the group.
2735 * @return this object for method chaining
2736 */
2737 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002738 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002739 return this;
2740 }
2741
2742 /**
2743 * Set this notification to be the group summary for a group of notifications.
2744 * Grouped notifications may display in a cluster or stack on devices which
2745 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2746 * @param isGroupSummary Whether this notification should be a group summary.
2747 * @return this object for method chaining
2748 */
2749 public Builder setGroupSummary(boolean isGroupSummary) {
2750 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2751 return this;
2752 }
2753
2754 /**
2755 * Set a sort key that orders this notification among other notifications from the
2756 * same package. This can be useful if an external sort was already applied and an app
2757 * would like to preserve this. Notifications will be sorted lexicographically using this
2758 * value, although providing different priorities in addition to providing sort key may
2759 * cause this value to be ignored.
2760 *
2761 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002762 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002763 *
2764 * @see String#compareTo(String)
2765 */
2766 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002767 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002768 return this;
2769 }
2770
2771 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002772 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002773 *
Griff Hazen720042b2014-02-24 15:46:56 -08002774 * <p>Values within the Bundle will replace existing extras values in this Builder.
2775 *
2776 * @see Notification#extras
2777 */
Griff Hazen959591e2014-05-15 22:26:18 -07002778 public Builder addExtras(Bundle extras) {
2779 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002780 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002781 }
2782 return this;
2783 }
2784
2785 /**
2786 * Set metadata for this notification.
2787 *
2788 * <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 -04002789 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002790 * called.
2791 *
Griff Hazen720042b2014-02-24 15:46:56 -08002792 * <p>Replaces any existing extras values with those from the provided Bundle.
2793 * Use {@link #addExtras} to merge in metadata instead.
2794 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002795 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002796 */
Griff Hazen959591e2014-05-15 22:26:18 -07002797 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002798 if (extras != null) {
2799 mUserExtras = extras;
2800 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002801 return this;
2802 }
2803
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002804 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002805 * Get the current metadata Bundle used by this notification Builder.
2806 *
2807 * <p>The returned Bundle is shared with this Builder.
2808 *
2809 * <p>The current contents of this Bundle are copied into the Notification each time
2810 * {@link #build()} is called.
2811 *
2812 * @see Notification#extras
2813 */
2814 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002815 return mUserExtras;
2816 }
2817
2818 private Bundle getAllExtras() {
2819 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2820 saveExtras.putAll(mN.extras);
2821 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002822 }
2823
2824 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002825 * Add an action to this notification. Actions are typically displayed by
2826 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002827 * <p>
2828 * Every action must have an icon (32dp square and matching the
2829 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2830 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2831 * <p>
2832 * A notification in its expanded form can display up to 3 actions, from left to right in
2833 * the order they were added. Actions will not be displayed when the notification is
2834 * collapsed, however, so be sure that any essential functions may be accessed by the user
2835 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002836 *
2837 * @param icon Resource ID of a drawable that represents the action.
2838 * @param title Text describing the action.
2839 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002840 *
2841 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002842 */
Dan Sandler86647982015-05-13 23:41:13 -04002843 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002844 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002845 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002846 return this;
2847 }
2848
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002849 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002850 * Add an action to this notification. Actions are typically displayed by
2851 * the system as a button adjacent to the notification content.
2852 * <p>
2853 * Every action must have an icon (32dp square and matching the
2854 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2855 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2856 * <p>
2857 * A notification in its expanded form can display up to 3 actions, from left to right in
2858 * the order they were added. Actions will not be displayed when the notification is
2859 * collapsed, however, so be sure that any essential functions may be accessed by the user
2860 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2861 *
2862 * @param action The action to add.
2863 */
2864 public Builder addAction(Action action) {
2865 mActions.add(action);
2866 return this;
2867 }
2868
2869 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002870 * Alter the complete list of actions attached to this notification.
2871 * @see #addAction(Action).
2872 *
2873 * @param actions
2874 * @return
2875 */
2876 public Builder setActions(Action... actions) {
2877 mActions.clear();
2878 for (int i = 0; i < actions.length; i++) {
2879 mActions.add(actions[i]);
2880 }
2881 return this;
2882 }
2883
2884 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002885 * Add a rich notification style to be applied at build time.
2886 *
2887 * @param style Object responsible for modifying the notification style.
2888 */
2889 public Builder setStyle(Style style) {
2890 if (mStyle != style) {
2891 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002892 if (mStyle != null) {
2893 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002894 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
2895 } else {
2896 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002897 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002898 }
2899 return this;
2900 }
2901
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002902 /**
2903 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07002904 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002905 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2906 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2907 *
2908 * @return The same Builder.
2909 */
2910 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002911 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002912 return this;
2913 }
2914
2915 /**
2916 * Supply a replacement Notification whose contents should be shown in insecure contexts
2917 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2918 * @param n A replacement notification, presumably with some or all info redacted.
2919 * @return The same Builder.
2920 */
2921 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002922 if (n != null) {
2923 mN.publicVersion = new Notification();
2924 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
2925 } else {
2926 mN.publicVersion = null;
2927 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002928 return this;
2929 }
2930
Griff Hazenb720abe2014-05-20 13:15:30 -07002931 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002932 * Apply an extender to this notification builder. Extenders may be used to add
2933 * metadata or change options on this builder.
2934 */
Griff Hazen61a9e862014-05-22 16:05:19 -07002935 public Builder extend(Extender extender) {
2936 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002937 return this;
2938 }
2939
Dan Sandler4e787062015-06-17 15:09:48 -04002940 /**
2941 * @hide
2942 */
2943 public void setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002944 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002945 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002946 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002947 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002948 }
2949 }
2950
Dan Sandler26e81cf2014-05-06 10:01:27 -04002951 /**
2952 * Sets {@link Notification#color}.
2953 *
2954 * @param argb The accent color to use
2955 *
2956 * @return The same Builder.
2957 */
Tor Norbye80756e32015-03-02 09:39:27 -08002958 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002959 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002960 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04002961 return this;
2962 }
2963
Julia Reynolds74303cf2015-10-16 11:37:55 -04002964 /**
2965 * Add a topic to this notification. Topics are typically displayed in Notification
2966 * settings.
2967 * <p>
2968 * Every topic must have an id and a textual label.
2969 *
2970 * @param topic The topic to add.
2971 */
2972 public Builder addTopic(Topic topic) {
2973 mTopics.add(topic);
2974 return this;
2975 }
2976
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002977 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02002978 // Note: This assumes that the current user can read the profile badge of the
2979 // originating user.
Svetoslavc7d62f02014-09-04 15:39:54 -07002980 return mContext.getPackageManager().getUserBadgeForDensity(
Kenny Guy8942bcd2014-09-08 21:09:47 +01002981 new UserHandle(mOriginatingUserId), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002982 }
2983
2984 private Bitmap getProfileBadge() {
2985 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01002986 if (badge == null) {
2987 return null;
2988 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002989 final int size = mContext.getResources().getDimensionPixelSize(
2990 R.dimen.notification_badge_size);
2991 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002992 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002993 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002994 badge.draw(canvas);
2995 return bitmap;
2996 }
2997
Kenny Guy98193ea2014-07-24 19:54:37 +01002998 private boolean addProfileBadge(RemoteViews contentView, int resId) {
2999 Bitmap profileBadge = getProfileBadge();
3000
3001 contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
3002 contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
3003 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
3004
3005 if (profileBadge != null) {
3006 contentView.setImageViewBitmap(resId, profileBadge);
3007 contentView.setViewVisibility(resId, View.VISIBLE);
3008
3009 // Make sure Line 3 is visible. As badge will be here if there
3010 // is no text to display.
3011 if (resId == R.id.profile_badge_line3) {
3012 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3013 }
3014 return true;
3015 }
3016 return false;
3017 }
3018
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003019 private void shrinkLine3Text(RemoteViews contentView) {
3020 float subTextSize = mContext.getResources().getDimensionPixelSize(
3021 R.dimen.notification_subtext_size);
3022 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
3023 }
3024
Christoph Studerfe718432014-09-01 18:21:18 +02003025 private void unshrinkLine3Text(RemoteViews contentView) {
3026 float regularTextSize = mContext.getResources().getDimensionPixelSize(
3027 com.android.internal.R.dimen.notification_text_size);
3028 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
3029 }
3030
3031 private void resetStandardTemplate(RemoteViews contentView) {
3032 removeLargeIconBackground(contentView);
3033 contentView.setViewPadding(R.id.icon, 0, 0, 0, 0);
3034 contentView.setImageViewResource(R.id.icon, 0);
3035 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3036 contentView.setViewVisibility(R.id.right_icon, View.GONE);
3037 contentView.setInt(R.id.right_icon, "setBackgroundResource", 0);
3038 contentView.setImageViewResource(R.id.right_icon, 0);
3039 contentView.setImageViewResource(R.id.icon, 0);
3040 contentView.setTextViewText(R.id.title, null);
3041 contentView.setTextViewText(R.id.text, null);
3042 unshrinkLine3Text(contentView);
3043 contentView.setTextViewText(R.id.text2, null);
3044 contentView.setViewVisibility(R.id.text2, View.GONE);
3045 contentView.setViewVisibility(R.id.info, View.GONE);
3046 contentView.setViewVisibility(R.id.time, View.GONE);
3047 contentView.setViewVisibility(R.id.line3, View.GONE);
3048 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3049 contentView.setViewVisibility(R.id.progress, View.GONE);
Christoph Studerca1db712014-09-10 17:31:33 +02003050 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003051 }
3052
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003053 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003054 return applyStandardTemplate(resId, true /* hasProgress */);
3055 }
3056
3057 /**
3058 * @param hasProgress whether the progress bar should be shown and set
3059 */
3060 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003061 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003062
Christoph Studerfe718432014-09-01 18:21:18 +02003063 resetStandardTemplate(contentView);
3064
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003065 boolean showLine3 = false;
3066 boolean showLine2 = false;
Kenny Guy98193ea2014-07-24 19:54:37 +01003067 boolean contentTextInLine2 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003068 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003069
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003070 if (mN.mLargeIcon != null) {
3071 contentView.setImageViewIcon(R.id.icon, mN.mLargeIcon);
3072 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3073 contentView.setImageViewIcon(R.id.right_icon, mN.mSmallIcon);
Dan Sandler190d58d2014-05-15 09:33:39 -04003074 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003075 processSmallRightIcon(mN.mSmallIcon, contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04003076 } else { // small icon at left
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003077 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Dan Sandler190d58d2014-05-15 09:33:39 -04003078 contentView.setViewVisibility(R.id.icon, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003079 processSmallIconAsLarge(mN.mSmallIcon, contentView);
Joe Onorato561d3852010-11-20 18:09:34 -08003080 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003081 if (ex.getCharSequence(EXTRA_TITLE) != null) {
3082 contentView.setTextViewText(R.id.title,
3083 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003084 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003085 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3086 contentView.setTextViewText(R.id.text,
3087 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003088 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003089 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003090 if (ex.getCharSequence(EXTRA_INFO_TEXT) != null) {
3091 contentView.setTextViewText(R.id.info,
3092 processLegacyText(ex.getCharSequence(EXTRA_INFO_TEXT)));
Jeff Sharkey1c400132011-08-05 14:50:13 -07003093 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003094 showLine3 = true;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003095 } else if (mN.number > 0) {
Daniel Sandlerebce0112011-06-16 16:44:51 -04003096 final int tooBig = mContext.getResources().getInteger(
3097 R.integer.status_bar_notification_info_maxnum);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003098 if (mN.number > tooBig) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003099 contentView.setTextViewText(R.id.info, processLegacyText(
3100 mContext.getResources().getString(
3101 R.string.status_bar_notification_info_overflow)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003102 } else {
3103 NumberFormat f = NumberFormat.getIntegerInstance();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003104 contentView.setTextViewText(R.id.info, processLegacyText(f.format(mN.number)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003105 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003106 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003107 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003108 } else {
3109 contentView.setViewVisibility(R.id.info, View.GONE);
3110 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003111
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003112 // Need to show three lines?
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003113 if (ex.getCharSequence(EXTRA_SUB_TEXT) != null) {
3114 contentView.setTextViewText(R.id.text,
3115 processLegacyText(ex.getCharSequence(EXTRA_SUB_TEXT)));
3116 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3117 contentView.setTextViewText(R.id.text2,
3118 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003119 contentView.setViewVisibility(R.id.text2, View.VISIBLE);
3120 showLine2 = true;
Kenny Guy98193ea2014-07-24 19:54:37 +01003121 contentTextInLine2 = true;
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003122 } else {
3123 contentView.setViewVisibility(R.id.text2, View.GONE);
3124 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003125 } else {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003126 contentView.setViewVisibility(R.id.text2, View.GONE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003127 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3128 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3129 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3130 if (hasProgress && (max != 0 || ind)) {
Christoph Studeraca4b252014-09-09 15:58:41 +02003131 contentView.setViewVisibility(R.id.progress, View.VISIBLE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003132 contentView.setProgressBar(
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003133 R.id.progress, max, progress, ind);
Jorim Jaggief72a192014-08-26 21:57:46 +02003134 contentView.setProgressBackgroundTintList(
Alan Viverette4a357cd2015-03-18 18:37:18 -07003135 R.id.progress, ColorStateList.valueOf(mContext.getColor(
Jorim Jaggief72a192014-08-26 21:57:46 +02003136 R.color.notification_progress_background_color)));
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003137 if (mN.color != COLOR_DEFAULT) {
3138 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Jorim Jaggief72a192014-08-26 21:57:46 +02003139 contentView.setProgressTintList(R.id.progress, colorStateList);
3140 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
3141 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003142 showLine2 = true;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003143 } else {
3144 contentView.setViewVisibility(R.id.progress, View.GONE);
3145 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003146 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003147 if (showLine2) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003148
3149 // need to shrink all the type to make sure everything fits
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003150 shrinkLine3Text(contentView);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003151 }
3152
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003153 if (showsTimeOrChronometer()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003154 if (ex.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003155 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3156 contentView.setLong(R.id.chronometer, "setBase",
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003157 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003158 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3159 } else {
3160 contentView.setViewVisibility(R.id.time, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003161 contentView.setLong(R.id.time, "setTime", mN.when);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003162 }
Joe Onorato561d3852010-11-20 18:09:34 -08003163 }
Daniel Sandler0c890492012-09-12 17:23:10 -07003164
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003165 // Adjust padding depending on line count and font size.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003166 contentView.setViewPadding(R.id.line1, 0,
3167 calculateTopPadding(mContext, hasThreeLines(),
3168 mContext.getResources().getConfiguration().fontScale),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003169 0, 0);
3170
Kenny Guy98193ea2014-07-24 19:54:37 +01003171 // We want to add badge to first line of text.
3172 boolean addedBadge = addProfileBadge(contentView,
3173 contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
3174 // If we added the badge to line 3 then we should show line 3.
3175 if (addedBadge && !contentTextInLine2) {
3176 showLine3 = true;
3177 }
3178
3179 // Note getStandardView may hide line 3 again.
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003180 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003181 contentView.setViewVisibility(R.id.overflow_divider,
3182 showLine3 ? View.VISIBLE : View.GONE);
Joe Onorato561d3852010-11-20 18:09:34 -08003183 return contentView;
3184 }
3185
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003186 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003187 * @return true if the built notification will show the time or the chronometer; false
3188 * otherwise
3189 */
3190 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003191 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003192 }
3193
3194 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003195 * Logic to find out whether the notification is going to have three lines in the contracted
3196 * layout. This is used to adjust the top padding.
3197 *
3198 * @return true if the notification is going to have three lines; false if the notification
3199 * is going to have one or two lines
3200 */
3201 private boolean hasThreeLines() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003202 final CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3203 final CharSequence text = mN.extras.getCharSequence(EXTRA_TEXT);
3204 boolean contentTextInLine2 = subText != null && text != null;
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003205 boolean hasProgress = mStyle == null || mStyle.hasProgress();
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003206 // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
3207 boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003208 boolean hasLine3 = text != null || mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null
3209 || mN.number > 0 || badgeInLine3;
3210 final Bundle ex = mN.extras;
3211 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3212 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3213 boolean hasLine2 = (subText != null && text != null) ||
3214 (hasProgress && subText == null && (max != 0 || ind));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003215 return hasLine2 && hasLine3;
3216 }
3217
3218 /**
3219 * @hide
3220 */
3221 public static int calculateTopPadding(Context ctx, boolean hasThreeLines,
3222 float fontScale) {
3223 int padding = ctx.getResources().getDimensionPixelSize(hasThreeLines
3224 ? R.dimen.notification_top_pad_narrow
3225 : R.dimen.notification_top_pad);
3226 int largePadding = ctx.getResources().getDimensionPixelSize(hasThreeLines
3227 ? R.dimen.notification_top_pad_large_text_narrow
3228 : R.dimen.notification_top_pad_large_text);
3229 float largeFactor = (MathUtils.constrain(fontScale, 1.0f, LARGE_TEXT_SCALE) - 1f)
3230 / (LARGE_TEXT_SCALE - 1f);
3231
3232 // Linearly interpolate the padding between large and normal with the font scale ranging
3233 // from 1f to LARGE_TEXT_SCALE
3234 return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
3235 }
3236
Christoph Studerfe718432014-09-01 18:21:18 +02003237 private void resetStandardTemplateWithActions(RemoteViews big) {
3238 big.setViewVisibility(R.id.actions, View.GONE);
3239 big.setViewVisibility(R.id.action_divider, View.GONE);
3240 big.removeAllViews(R.id.actions);
3241 }
3242
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003243 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003244 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003245
Christoph Studerfe718432014-09-01 18:21:18 +02003246 resetStandardTemplateWithActions(big);
3247
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003248 int N = mActions.size();
3249 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003250 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003251 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003252 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003253 for (int i=0; i<N; i++) {
3254 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003255 big.addView(R.id.actions, button);
3256 }
3257 }
3258 return big;
3259 }
3260
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003261 /**
3262 * Construct a RemoteViews for the final 1U notification layout. In order:
3263 * 1. Custom contentView from the caller
3264 * 2. Style's proposed content view
3265 * 3. Standard template view
3266 */
3267 public RemoteViews makeContentView() {
3268 if (mN.contentView != null) {
3269 return mN.contentView;
3270 } else if (mStyle != null) {
3271 final RemoteViews styleView = mStyle.makeContentView();
3272 if (styleView != null) {
3273 return styleView;
3274 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003275 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003276 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003277 }
3278
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003279 /**
3280 * Construct a RemoteViews for the final big notification layout.
3281 */
3282 public RemoteViews makeBigContentView() {
3283 if (mStyle != null) {
3284 final RemoteViews styleView = mStyle.makeBigContentView();
3285 if (styleView != null) {
3286 return styleView;
3287 }
3288 } else if (mActions.size() == 0) return null;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003289
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003290 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003291 }
3292
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003293 /**
3294 * Construct a RemoteViews for the final heads-up notification layout.
3295 */
3296 public RemoteViews makeHeadsUpContentView() {
3297 if (mStyle != null) {
3298 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3299 if (styleView != null) {
3300 return styleView;
3301 }
3302 } else if (mActions.size() == 0) return null;
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 Reynolds74303cf2015-10-16 11:37:55 -04003455 if (mTopics.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003456 mN.topics = new Topic[mTopics.size()];
3457 mTopics.toArray(mN.topics);
Julia Reynolds74303cf2015-10-16 11:37:55 -04003458 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003459 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003460 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003461
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003462 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003463 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003464 ApplicationInfo applicationInfo = n.extras.getParcelable(
3465 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003466 Context builderContext;
3467 try {
Kenny Guy77320062014-08-27 21:37:15 +01003468 builderContext = context.createApplicationContext(applicationInfo,
Christoph Studer4600f9b2014-07-22 22:44:43 +02003469 Context.CONTEXT_RESTRICTED);
3470 } catch (NameNotFoundException e) {
Kenny Guy77320062014-08-27 21:37:15 +01003471 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
Christoph Studer4600f9b2014-07-22 22:44:43 +02003472 builderContext = context; // try with our context
3473 }
3474
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003475 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003476 }
3477
3478 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3479 Class<? extends Style>[] classes = new Class[]{
3480 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3481 for (Class<? extends Style> innerClass : classes) {
3482 if (templateClass.equals(innerClass.getName())) {
3483 return innerClass;
3484 }
3485 }
3486 return null;
3487 }
3488
3489 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3490 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003491 }
3492
3493 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3494 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003495 }
3496
3497 private void setBuilderHeadsUpContentView(Notification n,
3498 RemoteViews headsUpContentView) {
3499 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003500 }
3501
3502 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003503 * @deprecated Use {@link #build()} instead.
3504 */
3505 @Deprecated
3506 public Notification getNotification() {
3507 return build();
3508 }
3509
3510 /**
3511 * Combine all of the options that have been set and return a new {@link Notification}
3512 * object.
3513 */
3514 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003515 // first, add any extras from the calling code
3516 if (mUserExtras != null) {
3517 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003518 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003519
3520 // lazy stuff from mContext; see comment in Builder(Context, Notification)
3521 mN.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, mContext.getApplicationInfo());
Kenny Guy8942bcd2014-09-08 21:09:47 +01003522 mOriginatingUserId = mContext.getUserId();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003523 mN.extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
Christoph Studer943aa672014-08-03 20:31:16 +02003524
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003525 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003526
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003527 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003528 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003529 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003530
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003531 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003532 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003533
3534 /**
3535 * Apply this Builder to an existing {@link Notification} object.
3536 *
3537 * @hide
3538 */
3539 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003540 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003541 return n;
3542 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003543
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003544 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003545 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003546 }
3547
3548 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003549 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003550 }
3551
3552 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003553 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003554 }
3555
3556 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003557 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003558 }
3559
3560 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003561 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003562 }
3563
3564 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003565 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003566 }
3567
3568 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003569 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003570 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003571 }
3572
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003573 /**
3574 * An object that can apply a rich notification style to a {@link Notification.Builder}
3575 * object.
3576 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003577 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003578 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003579
3580 /**
3581 * @hide
3582 */
3583 protected CharSequence mSummaryText = null;
3584
3585 /**
3586 * @hide
3587 */
3588 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003589
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003590 protected Builder mBuilder;
3591
Chris Wrend6297db2012-05-03 16:20:13 -04003592 /**
3593 * Overrides ContentTitle in the big form of the template.
3594 * This defaults to the value passed to setContentTitle().
3595 */
3596 protected void internalSetBigContentTitle(CharSequence title) {
3597 mBigContentTitle = title;
3598 }
3599
3600 /**
3601 * Set the first line of text after the detail section in the big form of the template.
3602 */
3603 protected void internalSetSummaryText(CharSequence cs) {
3604 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003605 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003606 }
3607
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003608 public void setBuilder(Builder builder) {
3609 if (mBuilder != builder) {
3610 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003611 if (mBuilder != null) {
3612 mBuilder.setStyle(this);
3613 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003614 }
3615 }
3616
Chris Wrend6297db2012-05-03 16:20:13 -04003617 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003618 if (mBuilder == null) {
3619 throw new IllegalArgumentException("Style requires a valid Builder object");
3620 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003621 }
Chris Wrend6297db2012-05-03 16:20:13 -04003622
3623 protected RemoteViews getStandardView(int layoutId) {
3624 checkBuilder();
3625
Christoph Studer4600f9b2014-07-22 22:44:43 +02003626 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003627 CharSequence oldBuilderContentTitle =
3628 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003629 if (mBigContentTitle != null) {
3630 mBuilder.setContentTitle(mBigContentTitle);
3631 }
3632
Chris Wrend6297db2012-05-03 16:20:13 -04003633 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3634
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003635 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003636
Chris Wrend6297db2012-05-03 16:20:13 -04003637 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3638 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003639 } else {
3640 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003641 }
3642
Daniel Sandler619738c2012-06-07 16:33:08 -04003643 // The last line defaults to the subtext, but can be replaced by mSummaryText
3644 final CharSequence overflowText =
3645 mSummaryTextSet ? mSummaryText
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003646 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT);
Daniel Sandler619738c2012-06-07 16:33:08 -04003647 if (overflowText != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003648 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
Daniel Sandler619738c2012-06-07 16:33:08 -04003649 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003650 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
Daniel Sandler916ad912012-06-13 12:17:07 -04003651 } else {
Kenny Guy98193ea2014-07-24 19:54:37 +01003652 // Clear text in case we use the line to show the profile badge.
3653 contentView.setTextViewText(R.id.text, "");
Daniel Sandler916ad912012-06-13 12:17:07 -04003654 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3655 contentView.setViewVisibility(R.id.line3, View.GONE);
Chris Wrend6297db2012-05-03 16:20:13 -04003656 }
3657
3658 return contentView;
3659 }
3660
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003661 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003662 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3663 * The default implementation has nothing additional to add.
3664 * @hide
3665 */
3666 public RemoteViews makeContentView() {
3667 return null;
3668 }
3669
3670 /**
3671 * Construct a Style-specific RemoteViews for the final big notification layout.
3672 * @hide
3673 */
3674 public RemoteViews makeBigContentView() {
3675 return null;
3676 }
3677
3678 /**
3679 * Construct a Style-specific RemoteViews for the final HUN layout.
3680 * @hide
3681 */
3682 public RemoteViews makeHeadsUpContentView() {
3683 return null;
3684 }
3685
3686 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003687 * Changes the padding of the first line such that the big and small content view have the
3688 * same top padding.
3689 *
3690 * @hide
3691 */
3692 protected void applyTopPadding(RemoteViews contentView) {
3693 int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003694 mBuilder.hasThreeLines(),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003695 mBuilder.mContext.getResources().getConfiguration().fontScale);
3696 contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
3697 }
3698
3699 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003700 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003701 * @hide
3702 */
3703 public void addExtras(Bundle extras) {
3704 if (mSummaryTextSet) {
3705 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3706 }
3707 if (mBigContentTitle != null) {
3708 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3709 }
Chris Wren91ad5632013-06-05 15:05:57 -04003710 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003711 }
3712
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003713 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003714 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003715 * @hide
3716 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003717 protected void restoreFromExtras(Bundle extras) {
3718 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3719 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3720 mSummaryTextSet = true;
3721 }
3722 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3723 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3724 }
3725 }
3726
3727
3728 /**
3729 * @hide
3730 */
3731 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003732 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003733 return wip;
3734 }
3735
Daniel Sandler0ec46202015-06-24 01:27:05 -04003736 /**
3737 * @hide
3738 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003739 public void purgeResources() {}
3740
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003741 /**
3742 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3743 * attached to.
3744 *
3745 * @return the fully constructed Notification.
3746 */
3747 public Notification build() {
3748 checkBuilder();
3749 return mBuilder.build();
3750 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003751
3752 /**
3753 * @hide
3754 * @return true if the style positions the progress bar on the second line; false if the
3755 * style hides the progress bar
3756 */
3757 protected boolean hasProgress() {
3758 return true;
3759 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003760 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003761
3762 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003763 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003764 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003765 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003766 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003767 * Notification notif = new Notification.Builder(mContext)
3768 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3769 * .setContentText(subject)
3770 * .setSmallIcon(R.drawable.new_post)
3771 * .setLargeIcon(aBitmap)
3772 * .setStyle(new Notification.BigPictureStyle()
3773 * .bigPicture(aBigBitmap))
3774 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003775 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003776 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003777 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003778 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003779 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003780 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003781 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003782 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003783
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003784 public BigPictureStyle() {
3785 }
3786
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003787 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003788 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003789 }
3790
Chris Wrend6297db2012-05-03 16:20:13 -04003791 /**
3792 * Overrides ContentTitle in the big form of the template.
3793 * This defaults to the value passed to setContentTitle().
3794 */
3795 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003796 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003797 return this;
3798 }
3799
3800 /**
3801 * Set the first line of text after the detail section in the big form of the template.
3802 */
3803 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003804 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003805 return this;
3806 }
3807
Chris Wren0bd664d2012-08-01 13:56:56 -04003808 /**
3809 * Provide the bitmap to be used as the payload for the BigPicture notification.
3810 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003811 public BigPictureStyle bigPicture(Bitmap b) {
3812 mPicture = b;
3813 return this;
3814 }
3815
Chris Wren3745a3d2012-05-22 15:11:52 -04003816 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003817 * Override the large icon when the big notification is shown.
3818 */
3819 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003820 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3821 }
3822
3823 /**
3824 * Override the large icon when the big notification is shown.
3825 */
3826 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003827 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003828 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003829 return this;
3830 }
3831
Riley Andrews0394a0c2015-11-03 23:36:52 -08003832 /** @hide */
3833 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
3834
Daniel Sandler0ec46202015-06-24 01:27:05 -04003835 /**
3836 * @hide
3837 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003838 @Override
3839 public void purgeResources() {
3840 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08003841 if (mPicture != null &&
3842 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08003843 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003844 mPicture = mPicture.createAshmemBitmap();
3845 }
3846 if (mBigLargeIcon != null) {
3847 mBigLargeIcon.convertToAshmem();
3848 }
3849 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003850
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003851 /**
3852 * @hide
3853 */
3854 public RemoteViews makeBigContentView() {
3855 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003856 // This covers the following cases:
3857 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003858 // mN.mLargeIcon
3859 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003860 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003861 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003862 oldLargeIcon = mBuilder.mN.mLargeIcon;
3863 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003864 }
3865
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003866 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003867
Christoph Studer5c510ee2014-12-15 16:32:27 +01003868 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003869 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003870 }
3871
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003872 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3873
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003874 applyTopPadding(contentView);
3875
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003876 boolean twoTextLines = mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT) != null
3877 && mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT) != null;
Kenny Guy98193ea2014-07-24 19:54:37 +01003878 mBuilder.addProfileBadge(contentView,
3879 twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003880 return contentView;
3881 }
3882
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003883 /**
3884 * @hide
3885 */
3886 public void addExtras(Bundle extras) {
3887 super.addExtras(extras);
3888
3889 if (mBigLargeIconSet) {
3890 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3891 }
3892 extras.putParcelable(EXTRA_PICTURE, mPicture);
3893 }
3894
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003895 /**
3896 * @hide
3897 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003898 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003899 protected void restoreFromExtras(Bundle extras) {
3900 super.restoreFromExtras(extras);
3901
3902 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003903 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003904 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003905 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003906 mPicture = extras.getParcelable(EXTRA_PICTURE);
3907 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003908 }
3909
3910 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003911 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003912 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003913 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003914 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003915 * Notification notif = new Notification.Builder(mContext)
3916 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3917 * .setContentText(subject)
3918 * .setSmallIcon(R.drawable.new_mail)
3919 * .setLargeIcon(aBitmap)
3920 * .setStyle(new Notification.BigTextStyle()
3921 * .bigText(aVeryLongString))
3922 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003923 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003924 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003925 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003926 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003927 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003928
3929 private static final int MAX_LINES = 13;
3930 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3931 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3932
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003933 private CharSequence mBigText;
3934
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003935 public BigTextStyle() {
3936 }
3937
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003938 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003939 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003940 }
3941
Chris Wrend6297db2012-05-03 16:20:13 -04003942 /**
3943 * Overrides ContentTitle in the big form of the template.
3944 * This defaults to the value passed to setContentTitle().
3945 */
3946 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003947 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003948 return this;
3949 }
3950
3951 /**
3952 * Set the first line of text after the detail section in the big form of the template.
3953 */
3954 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003955 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003956 return this;
3957 }
3958
Chris Wren0bd664d2012-08-01 13:56:56 -04003959 /**
3960 * Provide the longer text to be displayed in the big form of the
3961 * template in place of the content text.
3962 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003963 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003964 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003965 return this;
3966 }
3967
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003968 /**
3969 * @hide
3970 */
3971 public void addExtras(Bundle extras) {
3972 super.addExtras(extras);
3973
Christoph Studer4600f9b2014-07-22 22:44:43 +02003974 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3975 }
3976
3977 /**
3978 * @hide
3979 */
3980 @Override
3981 protected void restoreFromExtras(Bundle extras) {
3982 super.restoreFromExtras(extras);
3983
3984 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003985 }
3986
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003987 /**
3988 * @hide
3989 */
3990 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003991
3992 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003993 CharSequence oldBuilderContentText =
3994 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3995 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003996
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003997 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003998
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003999 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004000
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004001 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004002 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004003 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Chris Wren3c5f92432012-05-04 16:31:17 -04004004 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004005
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004006 applyTopPadding(contentView);
4007
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004008 mBuilder.shrinkLine3Text(contentView);
4009
Kenny Guy98193ea2014-07-24 19:54:37 +01004010 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4011
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004012 return contentView;
4013 }
4014
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004015 private int calculateMaxLines() {
4016 int lineCount = MAX_LINES;
4017 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004018 boolean hasSummary = (mSummaryTextSet ? mSummaryText
4019 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004020 if (hasActions) {
4021 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4022 }
4023 if (hasSummary) {
4024 lineCount -= LINES_CONSUMED_BY_SUMMARY;
4025 }
4026
4027 // If we have less top padding at the top, we can fit less lines.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004028 if (!mBuilder.hasThreeLines()) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004029 lineCount--;
4030 }
4031 return lineCount;
4032 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004033 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004034
4035 /**
4036 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004037 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004038 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004039 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004040 * Notification notif = new Notification.Builder(mContext)
4041 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4042 * .setContentText(subject)
4043 * .setSmallIcon(R.drawable.new_mail)
4044 * .setLargeIcon(aBitmap)
4045 * .setStyle(new Notification.InboxStyle()
4046 * .addLine(str1)
4047 * .addLine(str2)
4048 * .setContentTitle(&quot;&quot;)
4049 * .setSummaryText(&quot;+3 more&quot;))
4050 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004051 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004052 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004053 * @see Notification#bigContentView
4054 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004055 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004056 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4057
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004058 public InboxStyle() {
4059 }
4060
Daniel Sandler879c5e02012-04-17 16:46:51 -04004061 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004062 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004063 }
4064
Chris Wrend6297db2012-05-03 16:20:13 -04004065 /**
4066 * Overrides ContentTitle in the big form of the template.
4067 * This defaults to the value passed to setContentTitle().
4068 */
4069 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004070 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004071 return this;
4072 }
4073
4074 /**
4075 * Set the first line of text after the detail section in the big form of the template.
4076 */
4077 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004078 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004079 return this;
4080 }
4081
Chris Wren0bd664d2012-08-01 13:56:56 -04004082 /**
4083 * Append a line to the digest section of the Inbox notification.
4084 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004085 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004086 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004087 return this;
4088 }
4089
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004090 /**
4091 * @hide
4092 */
4093 public void addExtras(Bundle extras) {
4094 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004095
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004096 CharSequence[] a = new CharSequence[mTexts.size()];
4097 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4098 }
4099
Christoph Studer4600f9b2014-07-22 22:44:43 +02004100 /**
4101 * @hide
4102 */
4103 @Override
4104 protected void restoreFromExtras(Bundle extras) {
4105 super.restoreFromExtras(extras);
4106
4107 mTexts.clear();
4108 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4109 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4110 }
4111 }
4112
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004113 /**
4114 * @hide
4115 */
4116 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004117 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004118 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004119 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4120 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004121
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004122 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004123
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004124 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004125
Chris Wrend6297db2012-05-03 16:20:13 -04004126 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004127
Chris Wrend6297db2012-05-03 16:20:13 -04004128 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 -04004129 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004130
Chris Wren4ed80d52012-05-17 09:30:03 -04004131 // Make sure all rows are gone in case we reuse a view.
4132 for (int rowId : rowIds) {
4133 contentView.setViewVisibility(rowId, View.GONE);
4134 }
4135
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004136 final boolean largeText =
4137 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4138 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4139 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004140 int i=0;
4141 while (i < mTexts.size() && i < rowIds.length) {
4142 CharSequence str = mTexts.get(i);
4143 if (str != null && !str.equals("")) {
4144 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004145 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004146 if (largeText) {
4147 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4148 subTextSize);
4149 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004150 }
4151 i++;
4152 }
4153
Chris Wren683ab002012-09-20 10:35:54 -04004154 contentView.setViewVisibility(R.id.inbox_end_pad,
4155 mTexts.size() > 0 ? View.VISIBLE : View.GONE);
4156
4157 contentView.setViewVisibility(R.id.inbox_more,
4158 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
Chris Wren29bb6d92012-05-17 18:09:42 -04004159
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004160 applyTopPadding(contentView);
4161
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004162 mBuilder.shrinkLine3Text(contentView);
4163
Kenny Guy98193ea2014-07-24 19:54:37 +01004164 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4165
Daniel Sandler879c5e02012-04-17 16:46:51 -04004166 return contentView;
4167 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004168 }
Dan Sandler842dd772014-05-15 09:36:47 -04004169
4170 /**
4171 * Notification style for media playback notifications.
4172 *
4173 * In the expanded form, {@link Notification#bigContentView}, up to 5
4174 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004175 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004176 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4177 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4178 * treated as album artwork.
4179 *
4180 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4181 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004182 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004183 * in the standard view alongside the usual content.
4184 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004185 * Notifications created with MediaStyle will have their category set to
4186 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4187 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4188 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004189 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4190 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004191 * the System UI can identify this as a notification representing an active media session
4192 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4193 *
4194 * To use this style with your Notification, feed it to
4195 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4196 * <pre class="prettyprint">
4197 * Notification noti = new Notification.Builder()
4198 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004199 * .setContentTitle(&quot;Track title&quot;)
4200 * .setContentText(&quot;Artist - Album&quot;)
4201 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004202 * .setStyle(<b>new Notification.MediaStyle()</b>
4203 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004204 * .build();
4205 * </pre>
4206 *
4207 * @see Notification#bigContentView
4208 */
4209 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004210 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004211 static final int MAX_MEDIA_BUTTONS = 5;
4212
4213 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004214 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004215
4216 public MediaStyle() {
4217 }
4218
4219 public MediaStyle(Builder builder) {
4220 setBuilder(builder);
4221 }
4222
4223 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004224 * 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 -04004225 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004226 *
4227 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004228 */
4229 public MediaStyle setShowActionsInCompactView(int...actions) {
4230 mActionsToShowInCompact = actions;
4231 return this;
4232 }
4233
4234 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004235 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4236 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004237 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004238 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004239 mToken = token;
4240 return this;
4241 }
4242
Christoph Studer4600f9b2014-07-22 22:44:43 +02004243 /**
4244 * @hide
4245 */
Dan Sandler842dd772014-05-15 09:36:47 -04004246 @Override
4247 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004248 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004249 if (wip.category == null) {
4250 wip.category = Notification.CATEGORY_TRANSPORT;
4251 }
Dan Sandler842dd772014-05-15 09:36:47 -04004252 return wip;
4253 }
4254
Christoph Studer4600f9b2014-07-22 22:44:43 +02004255 /**
4256 * @hide
4257 */
4258 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004259 public RemoteViews makeContentView() {
4260 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004261 }
4262
4263 /**
4264 * @hide
4265 */
4266 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004267 public RemoteViews makeBigContentView() {
4268 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004269 }
4270
Dan Sandler842dd772014-05-15 09:36:47 -04004271 /** @hide */
4272 @Override
4273 public void addExtras(Bundle extras) {
4274 super.addExtras(extras);
4275
4276 if (mToken != null) {
4277 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4278 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004279 if (mActionsToShowInCompact != null) {
4280 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4281 }
Dan Sandler842dd772014-05-15 09:36:47 -04004282 }
4283
Christoph Studer4600f9b2014-07-22 22:44:43 +02004284 /**
4285 * @hide
4286 */
4287 @Override
4288 protected void restoreFromExtras(Bundle extras) {
4289 super.restoreFromExtras(extras);
4290
4291 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4292 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4293 }
4294 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4295 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4296 }
4297 }
4298
Dan Sandler842dd772014-05-15 09:36:47 -04004299 private RemoteViews generateMediaActionButton(Action action) {
4300 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004301 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004302 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004303 button.setImageViewIcon(R.id.action0, action.getIcon());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004304 button.setDrawableParameters(R.id.action0, false, -1,
4305 0xFFFFFFFF,
4306 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004307 if (!tombstone) {
4308 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4309 }
4310 button.setContentDescription(R.id.action0, action.title);
4311 return button;
4312 }
4313
4314 private RemoteViews makeMediaContentView() {
4315 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004316 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004317
4318 final int numActions = mBuilder.mActions.size();
4319 final int N = mActionsToShowInCompact == null
4320 ? 0
4321 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4322 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004323 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004324 for (int i = 0; i < N; i++) {
4325 if (i >= numActions) {
4326 throw new IllegalArgumentException(String.format(
4327 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4328 i, numActions - 1));
4329 }
4330
4331 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
4332 final RemoteViews button = generateMediaActionButton(action);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004333 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004334 }
4335 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004336 styleText(view);
4337 hideRightIcon(view);
Dan Sandler842dd772014-05-15 09:36:47 -04004338 return view;
4339 }
4340
4341 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004342 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
4343 RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount),
4344 false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004345
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004346 if (actionCount > 0) {
4347 big.removeAllViews(com.android.internal.R.id.media_actions);
4348 for (int i = 0; i < actionCount; i++) {
Dan Sandler842dd772014-05-15 09:36:47 -04004349 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004350 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004351 }
4352 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004353 styleText(big);
4354 hideRightIcon(big);
4355 applyTopPadding(big);
4356 big.setViewVisibility(android.R.id.progress, View.GONE);
Dan Sandler842dd772014-05-15 09:36:47 -04004357 return big;
4358 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004359
4360 private int getBigLayoutResource(int actionCount) {
4361 if (actionCount <= 3) {
4362 return R.layout.notification_template_material_big_media_narrow;
4363 } else {
4364 return R.layout.notification_template_material_big_media;
4365 }
4366 }
4367
4368 private void hideRightIcon(RemoteViews contentView) {
4369 contentView.setViewVisibility(R.id.right_icon, View.GONE);
4370 }
4371
4372 /**
4373 * Applies the special text colors for media notifications to all text views.
4374 */
4375 private void styleText(RemoteViews contentView) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07004376 int primaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004377 R.color.notification_media_primary_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -07004378 int secondaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004379 R.color.notification_media_secondary_color);
4380 contentView.setTextColor(R.id.title, primaryColor);
4381 if (mBuilder.showsTimeOrChronometer()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004382 if (mBuilder.getAllExtras().getBoolean(EXTRA_SHOW_CHRONOMETER)) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004383 contentView.setTextColor(R.id.chronometer, secondaryColor);
4384 } else {
4385 contentView.setTextColor(R.id.time, secondaryColor);
4386 }
4387 }
4388 contentView.setTextColor(R.id.text2, secondaryColor);
4389 contentView.setTextColor(R.id.text, secondaryColor);
4390 contentView.setTextColor(R.id.info, secondaryColor);
4391 }
4392
4393 /**
4394 * @hide
4395 */
4396 @Override
4397 protected boolean hasProgress() {
4398 return false;
4399 }
Dan Sandler842dd772014-05-15 09:36:47 -04004400 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004401
Christoph Studer4600f9b2014-07-22 22:44:43 +02004402 // When adding a new Style subclass here, don't forget to update
4403 // Builder.getNotificationStyleClass.
4404
Griff Hazen61a9e862014-05-22 16:05:19 -07004405 /**
4406 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4407 * metadata or change options on a notification builder.
4408 */
4409 public interface Extender {
4410 /**
4411 * Apply this extender to a notification builder.
4412 * @param builder the builder to be modified.
4413 * @return the build object for chaining.
4414 */
4415 public Builder extend(Builder builder);
4416 }
4417
4418 /**
4419 * Helper class to add wearable extensions to notifications.
4420 * <p class="note"> See
4421 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4422 * for Android Wear</a> for more information on how to use this class.
4423 * <p>
4424 * To create a notification with wearable extensions:
4425 * <ol>
4426 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4427 * properties.
4428 * <li>Create a {@link android.app.Notification.WearableExtender}.
4429 * <li>Set wearable-specific properties using the
4430 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4431 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4432 * notification.
4433 * <li>Post the notification to the notification system with the
4434 * {@code NotificationManager.notify(...)} methods.
4435 * </ol>
4436 *
4437 * <pre class="prettyprint">
4438 * Notification notif = new Notification.Builder(mContext)
4439 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4440 * .setContentText(subject)
4441 * .setSmallIcon(R.drawable.new_mail)
4442 * .extend(new Notification.WearableExtender()
4443 * .setContentIcon(R.drawable.new_mail))
4444 * .build();
4445 * NotificationManager notificationManger =
4446 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4447 * notificationManger.notify(0, notif);</pre>
4448 *
4449 * <p>Wearable extensions can be accessed on an existing notification by using the
4450 * {@code WearableExtender(Notification)} constructor,
4451 * and then using the {@code get} methods to access values.
4452 *
4453 * <pre class="prettyprint">
4454 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4455 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004456 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004457 */
4458 public static final class WearableExtender implements Extender {
4459 /**
4460 * Sentinel value for an action index that is unset.
4461 */
4462 public static final int UNSET_ACTION_INDEX = -1;
4463
4464 /**
4465 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4466 * default sizing.
4467 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004468 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004469 * on their content.
4470 */
4471 public static final int SIZE_DEFAULT = 0;
4472
4473 /**
4474 * Size value for use with {@link #setCustomSizePreset} to show this notification
4475 * with an extra small size.
4476 * <p>This value is only applicable for custom display notifications created using
4477 * {@link #setDisplayIntent}.
4478 */
4479 public static final int SIZE_XSMALL = 1;
4480
4481 /**
4482 * Size value for use with {@link #setCustomSizePreset} to show this notification
4483 * with a small size.
4484 * <p>This value is only applicable for custom display notifications created using
4485 * {@link #setDisplayIntent}.
4486 */
4487 public static final int SIZE_SMALL = 2;
4488
4489 /**
4490 * Size value for use with {@link #setCustomSizePreset} to show this notification
4491 * with a medium size.
4492 * <p>This value is only applicable for custom display notifications created using
4493 * {@link #setDisplayIntent}.
4494 */
4495 public static final int SIZE_MEDIUM = 3;
4496
4497 /**
4498 * Size value for use with {@link #setCustomSizePreset} to show this notification
4499 * with a large size.
4500 * <p>This value is only applicable for custom display notifications created using
4501 * {@link #setDisplayIntent}.
4502 */
4503 public static final int SIZE_LARGE = 4;
4504
Griff Hazend5f11f92014-05-27 15:40:09 -07004505 /**
4506 * Size value for use with {@link #setCustomSizePreset} to show this notification
4507 * full screen.
4508 * <p>This value is only applicable for custom display notifications created using
4509 * {@link #setDisplayIntent}.
4510 */
4511 public static final int SIZE_FULL_SCREEN = 5;
4512
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004513 /**
4514 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4515 * short amount of time when this notification is displayed on the screen. This
4516 * is the default value.
4517 */
4518 public static final int SCREEN_TIMEOUT_SHORT = 0;
4519
4520 /**
4521 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4522 * for a longer amount of time when this notification is displayed on the screen.
4523 */
4524 public static final int SCREEN_TIMEOUT_LONG = -1;
4525
Griff Hazen61a9e862014-05-22 16:05:19 -07004526 /** Notification extra which contains wearable extensions */
4527 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4528
Pete Gastaf6781d2014-10-07 15:17:05 -04004529 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004530 private static final String KEY_ACTIONS = "actions";
4531 private static final String KEY_FLAGS = "flags";
4532 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4533 private static final String KEY_PAGES = "pages";
4534 private static final String KEY_BACKGROUND = "background";
4535 private static final String KEY_CONTENT_ICON = "contentIcon";
4536 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4537 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4538 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4539 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4540 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004541 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004542
4543 // Flags bitwise-ored to mFlags
4544 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4545 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4546 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4547 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004548 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004549
4550 // Default value for flags integer
4551 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4552
4553 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4554 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4555
4556 private ArrayList<Action> mActions = new ArrayList<Action>();
4557 private int mFlags = DEFAULT_FLAGS;
4558 private PendingIntent mDisplayIntent;
4559 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4560 private Bitmap mBackground;
4561 private int mContentIcon;
4562 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4563 private int mContentActionIndex = UNSET_ACTION_INDEX;
4564 private int mCustomSizePreset = SIZE_DEFAULT;
4565 private int mCustomContentHeight;
4566 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004567 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004568
4569 /**
4570 * Create a {@link android.app.Notification.WearableExtender} with default
4571 * options.
4572 */
4573 public WearableExtender() {
4574 }
4575
4576 public WearableExtender(Notification notif) {
4577 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4578 if (wearableBundle != null) {
4579 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4580 if (actions != null) {
4581 mActions.addAll(actions);
4582 }
4583
4584 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4585 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4586
4587 Notification[] pages = getNotificationArrayFromBundle(
4588 wearableBundle, KEY_PAGES);
4589 if (pages != null) {
4590 Collections.addAll(mPages, pages);
4591 }
4592
4593 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4594 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4595 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4596 DEFAULT_CONTENT_ICON_GRAVITY);
4597 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4598 UNSET_ACTION_INDEX);
4599 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4600 SIZE_DEFAULT);
4601 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4602 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004603 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004604 }
4605 }
4606
4607 /**
4608 * Apply wearable extensions to a notification that is being built. This is typically
4609 * called by the {@link android.app.Notification.Builder#extend} method of
4610 * {@link android.app.Notification.Builder}.
4611 */
4612 @Override
4613 public Notification.Builder extend(Notification.Builder builder) {
4614 Bundle wearableBundle = new Bundle();
4615
4616 if (!mActions.isEmpty()) {
4617 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4618 }
4619 if (mFlags != DEFAULT_FLAGS) {
4620 wearableBundle.putInt(KEY_FLAGS, mFlags);
4621 }
4622 if (mDisplayIntent != null) {
4623 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4624 }
4625 if (!mPages.isEmpty()) {
4626 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4627 new Notification[mPages.size()]));
4628 }
4629 if (mBackground != null) {
4630 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4631 }
4632 if (mContentIcon != 0) {
4633 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4634 }
4635 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4636 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4637 }
4638 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4639 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4640 mContentActionIndex);
4641 }
4642 if (mCustomSizePreset != SIZE_DEFAULT) {
4643 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4644 }
4645 if (mCustomContentHeight != 0) {
4646 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4647 }
4648 if (mGravity != DEFAULT_GRAVITY) {
4649 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4650 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004651 if (mHintScreenTimeout != 0) {
4652 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4653 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004654
4655 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4656 return builder;
4657 }
4658
4659 @Override
4660 public WearableExtender clone() {
4661 WearableExtender that = new WearableExtender();
4662 that.mActions = new ArrayList<Action>(this.mActions);
4663 that.mFlags = this.mFlags;
4664 that.mDisplayIntent = this.mDisplayIntent;
4665 that.mPages = new ArrayList<Notification>(this.mPages);
4666 that.mBackground = this.mBackground;
4667 that.mContentIcon = this.mContentIcon;
4668 that.mContentIconGravity = this.mContentIconGravity;
4669 that.mContentActionIndex = this.mContentActionIndex;
4670 that.mCustomSizePreset = this.mCustomSizePreset;
4671 that.mCustomContentHeight = this.mCustomContentHeight;
4672 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004673 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004674 return that;
4675 }
4676
4677 /**
4678 * Add a wearable action to this notification.
4679 *
4680 * <p>When wearable actions are added using this method, the set of actions that
4681 * show on a wearable device splits from devices that only show actions added
4682 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4683 * of which actions display on different devices.
4684 *
4685 * @param action the action to add to this notification
4686 * @return this object for method chaining
4687 * @see android.app.Notification.Action
4688 */
4689 public WearableExtender addAction(Action action) {
4690 mActions.add(action);
4691 return this;
4692 }
4693
4694 /**
4695 * Adds wearable actions to this notification.
4696 *
4697 * <p>When wearable actions are added using this method, the set of actions that
4698 * show on a wearable device splits from devices that only show actions added
4699 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4700 * of which actions display on different devices.
4701 *
4702 * @param actions the actions to add to this notification
4703 * @return this object for method chaining
4704 * @see android.app.Notification.Action
4705 */
4706 public WearableExtender addActions(List<Action> actions) {
4707 mActions.addAll(actions);
4708 return this;
4709 }
4710
4711 /**
4712 * Clear all wearable actions present on this builder.
4713 * @return this object for method chaining.
4714 * @see #addAction
4715 */
4716 public WearableExtender clearActions() {
4717 mActions.clear();
4718 return this;
4719 }
4720
4721 /**
4722 * Get the wearable actions present on this notification.
4723 */
4724 public List<Action> getActions() {
4725 return mActions;
4726 }
4727
4728 /**
4729 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004730 * this notification. The {@link PendingIntent} provided should be for an activity.
4731 *
4732 * <pre class="prettyprint">
4733 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4734 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4735 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4736 * Notification notif = new Notification.Builder(context)
4737 * .extend(new Notification.WearableExtender()
4738 * .setDisplayIntent(displayPendingIntent)
4739 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4740 * .build();</pre>
4741 *
4742 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004743 * should have an empty task affinity. It is also recommended to use the device
4744 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004745 *
4746 * <p>Example AndroidManifest.xml entry:
4747 * <pre class="prettyprint">
4748 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4749 * android:exported=&quot;true&quot;
4750 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004751 * android:taskAffinity=&quot;&quot;
4752 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004753 *
4754 * @param intent the {@link PendingIntent} for an activity
4755 * @return this object for method chaining
4756 * @see android.app.Notification.WearableExtender#getDisplayIntent
4757 */
4758 public WearableExtender setDisplayIntent(PendingIntent intent) {
4759 mDisplayIntent = intent;
4760 return this;
4761 }
4762
4763 /**
4764 * Get the intent to launch inside of an activity view when displaying this
4765 * notification. This {@code PendingIntent} should be for an activity.
4766 */
4767 public PendingIntent getDisplayIntent() {
4768 return mDisplayIntent;
4769 }
4770
4771 /**
4772 * Add an additional page of content to display with this notification. The current
4773 * notification forms the first page, and pages added using this function form
4774 * subsequent pages. This field can be used to separate a notification into multiple
4775 * sections.
4776 *
4777 * @param page the notification to add as another page
4778 * @return this object for method chaining
4779 * @see android.app.Notification.WearableExtender#getPages
4780 */
4781 public WearableExtender addPage(Notification page) {
4782 mPages.add(page);
4783 return this;
4784 }
4785
4786 /**
4787 * Add additional pages of content to display with this notification. The current
4788 * notification forms the first page, and pages added using this function form
4789 * subsequent pages. This field can be used to separate a notification into multiple
4790 * sections.
4791 *
4792 * @param pages a list of notifications
4793 * @return this object for method chaining
4794 * @see android.app.Notification.WearableExtender#getPages
4795 */
4796 public WearableExtender addPages(List<Notification> pages) {
4797 mPages.addAll(pages);
4798 return this;
4799 }
4800
4801 /**
4802 * Clear all additional pages present on this builder.
4803 * @return this object for method chaining.
4804 * @see #addPage
4805 */
4806 public WearableExtender clearPages() {
4807 mPages.clear();
4808 return this;
4809 }
4810
4811 /**
4812 * Get the array of additional pages of content for displaying this notification. The
4813 * current notification forms the first page, and elements within this array form
4814 * subsequent pages. This field can be used to separate a notification into multiple
4815 * sections.
4816 * @return the pages for this notification
4817 */
4818 public List<Notification> getPages() {
4819 return mPages;
4820 }
4821
4822 /**
4823 * Set a background image to be displayed behind the notification content.
4824 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4825 * will work with any notification style.
4826 *
4827 * @param background the background bitmap
4828 * @return this object for method chaining
4829 * @see android.app.Notification.WearableExtender#getBackground
4830 */
4831 public WearableExtender setBackground(Bitmap background) {
4832 mBackground = background;
4833 return this;
4834 }
4835
4836 /**
4837 * Get a background image to be displayed behind the notification content.
4838 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4839 * will work with any notification style.
4840 *
4841 * @return the background image
4842 * @see android.app.Notification.WearableExtender#setBackground
4843 */
4844 public Bitmap getBackground() {
4845 return mBackground;
4846 }
4847
4848 /**
4849 * Set an icon that goes with the content of this notification.
4850 */
4851 public WearableExtender setContentIcon(int icon) {
4852 mContentIcon = icon;
4853 return this;
4854 }
4855
4856 /**
4857 * Get an icon that goes with the content of this notification.
4858 */
4859 public int getContentIcon() {
4860 return mContentIcon;
4861 }
4862
4863 /**
4864 * Set the gravity that the content icon should have within the notification display.
4865 * Supported values include {@link android.view.Gravity#START} and
4866 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4867 * @see #setContentIcon
4868 */
4869 public WearableExtender setContentIconGravity(int contentIconGravity) {
4870 mContentIconGravity = contentIconGravity;
4871 return this;
4872 }
4873
4874 /**
4875 * Get the gravity that the content icon should have within the notification display.
4876 * Supported values include {@link android.view.Gravity#START} and
4877 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4878 * @see #getContentIcon
4879 */
4880 public int getContentIconGravity() {
4881 return mContentIconGravity;
4882 }
4883
4884 /**
4885 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004886 * this notification. This action will no longer display separately from the
4887 * notification's content.
4888 *
Griff Hazenca48d352014-05-28 22:37:13 -07004889 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004890 * set, although the list of available actions comes from the main notification and not
4891 * from the child page's notification.
4892 *
4893 * @param actionIndex The index of the action to hoist onto the current notification page.
4894 * If wearable actions were added to the main notification, this index
4895 * will apply to that list, otherwise it will apply to the regular
4896 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004897 */
4898 public WearableExtender setContentAction(int actionIndex) {
4899 mContentActionIndex = actionIndex;
4900 return this;
4901 }
4902
4903 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004904 * Get the index of the notification action, if any, that was specified as being clickable
4905 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004906 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004907 *
Griff Hazenca48d352014-05-28 22:37:13 -07004908 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004909 * set, although the list of available actions comes from the main notification and not
4910 * from the child page's notification.
4911 *
4912 * <p>If wearable specific actions were added to the main notification, this index will
4913 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004914 *
4915 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004916 */
4917 public int getContentAction() {
4918 return mContentActionIndex;
4919 }
4920
4921 /**
4922 * Set the gravity that this notification should have within the available viewport space.
4923 * Supported values include {@link android.view.Gravity#TOP},
4924 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4925 * The default value is {@link android.view.Gravity#BOTTOM}.
4926 */
4927 public WearableExtender setGravity(int gravity) {
4928 mGravity = gravity;
4929 return this;
4930 }
4931
4932 /**
4933 * Get the gravity that this notification should have within the available viewport space.
4934 * Supported values include {@link android.view.Gravity#TOP},
4935 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4936 * The default value is {@link android.view.Gravity#BOTTOM}.
4937 */
4938 public int getGravity() {
4939 return mGravity;
4940 }
4941
4942 /**
4943 * Set the custom size preset for the display of this notification out of the available
4944 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4945 * {@link #SIZE_LARGE}.
4946 * <p>Some custom size presets are only applicable for custom display notifications created
4947 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4948 * documentation for the preset in question. See also
4949 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4950 */
4951 public WearableExtender setCustomSizePreset(int sizePreset) {
4952 mCustomSizePreset = sizePreset;
4953 return this;
4954 }
4955
4956 /**
4957 * Get the custom size preset for the display of this notification out of the available
4958 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4959 * {@link #SIZE_LARGE}.
4960 * <p>Some custom size presets are only applicable for custom display notifications created
4961 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4962 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4963 */
4964 public int getCustomSizePreset() {
4965 return mCustomSizePreset;
4966 }
4967
4968 /**
4969 * Set the custom height in pixels for the display of this notification's content.
4970 * <p>This option is only available for custom display notifications created
4971 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4972 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4973 * {@link #getCustomContentHeight}.
4974 */
4975 public WearableExtender setCustomContentHeight(int height) {
4976 mCustomContentHeight = height;
4977 return this;
4978 }
4979
4980 /**
4981 * Get the custom height in pixels for the display of this notification's content.
4982 * <p>This option is only available for custom display notifications created
4983 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4984 * {@link #setCustomContentHeight}.
4985 */
4986 public int getCustomContentHeight() {
4987 return mCustomContentHeight;
4988 }
4989
4990 /**
4991 * Set whether the scrolling position for the contents of this notification should start
4992 * at the bottom of the contents instead of the top when the contents are too long to
4993 * display within the screen. Default is false (start scroll at the top).
4994 */
4995 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4996 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4997 return this;
4998 }
4999
5000 /**
5001 * Get whether the scrolling position for the contents of this notification should start
5002 * at the bottom of the contents instead of the top when the contents are too long to
5003 * display within the screen. Default is false (start scroll at the top).
5004 */
5005 public boolean getStartScrollBottom() {
5006 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
5007 }
5008
5009 /**
5010 * Set whether the content intent is available when the wearable device is not connected
5011 * to a companion device. The user can still trigger this intent when the wearable device
5012 * is offline, but a visual hint will indicate that the content intent may not be available.
5013 * Defaults to true.
5014 */
5015 public WearableExtender setContentIntentAvailableOffline(
5016 boolean contentIntentAvailableOffline) {
5017 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
5018 return this;
5019 }
5020
5021 /**
5022 * Get whether the content intent is available when the wearable device is not connected
5023 * to a companion device. The user can still trigger this intent when the wearable device
5024 * is offline, but a visual hint will indicate that the content intent may not be available.
5025 * Defaults to true.
5026 */
5027 public boolean getContentIntentAvailableOffline() {
5028 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
5029 }
5030
5031 /**
5032 * Set a hint that this notification's icon should not be displayed.
5033 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
5034 * @return this object for method chaining
5035 */
5036 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
5037 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
5038 return this;
5039 }
5040
5041 /**
5042 * Get a hint that this notification's icon should not be displayed.
5043 * @return {@code true} if this icon should not be displayed, false otherwise.
5044 * The default value is {@code false} if this was never set.
5045 */
5046 public boolean getHintHideIcon() {
5047 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
5048 }
5049
5050 /**
5051 * Set a visual hint that only the background image of this notification should be
5052 * displayed, and other semantic content should be hidden. This hint is only applicable
5053 * to sub-pages added using {@link #addPage}.
5054 */
5055 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
5056 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
5057 return this;
5058 }
5059
5060 /**
5061 * Get a visual hint that only the background image of this notification should be
5062 * displayed, and other semantic content should be hidden. This hint is only applicable
5063 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
5064 */
5065 public boolean getHintShowBackgroundOnly() {
5066 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
5067 }
5068
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005069 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005070 * Set a hint that this notification's background should not be clipped if possible,
5071 * and should instead be resized to fully display on the screen, retaining the aspect
5072 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005073 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
5074 * @return this object for method chaining
5075 */
5076 public WearableExtender setHintAvoidBackgroundClipping(
5077 boolean hintAvoidBackgroundClipping) {
5078 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
5079 return this;
5080 }
5081
5082 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005083 * Get a hint that this notification's background should not be clipped if possible,
5084 * and should instead be resized to fully display on the screen, retaining the aspect
5085 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005086 * @return {@code true} if it's ok if the background is clipped on the screen, false
5087 * otherwise. The default value is {@code false} if this was never set.
5088 */
5089 public boolean getHintAvoidBackgroundClipping() {
5090 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5091 }
5092
5093 /**
5094 * Set a hint that the screen should remain on for at least this duration when
5095 * this notification is displayed on the screen.
5096 * @param timeout The requested screen timeout in milliseconds. Can also be either
5097 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5098 * @return this object for method chaining
5099 */
5100 public WearableExtender setHintScreenTimeout(int timeout) {
5101 mHintScreenTimeout = timeout;
5102 return this;
5103 }
5104
5105 /**
5106 * Get the duration, in milliseconds, that the screen should remain on for
5107 * when this notification is displayed.
5108 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5109 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5110 */
5111 public int getHintScreenTimeout() {
5112 return mHintScreenTimeout;
5113 }
5114
Griff Hazen61a9e862014-05-22 16:05:19 -07005115 private void setFlag(int mask, boolean value) {
5116 if (value) {
5117 mFlags |= mask;
5118 } else {
5119 mFlags &= ~mask;
5120 }
5121 }
5122 }
5123
5124 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005125 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5126 * with car extensions:
5127 *
5128 * <ol>
5129 * <li>Create an {@link Notification.Builder}, setting any desired
5130 * properties.
5131 * <li>Create a {@link CarExtender}.
5132 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5133 * {@link CarExtender}.
5134 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5135 * to apply the extensions to a notification.
5136 * </ol>
5137 *
5138 * <pre class="prettyprint">
5139 * Notification notification = new Notification.Builder(context)
5140 * ...
5141 * .extend(new CarExtender()
5142 * .set*(...))
5143 * .build();
5144 * </pre>
5145 *
5146 * <p>Car extensions can be accessed on an existing notification by using the
5147 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5148 * to access values.
5149 */
5150 public static final class CarExtender implements Extender {
5151 private static final String TAG = "CarExtender";
5152
5153 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5154 private static final String EXTRA_LARGE_ICON = "large_icon";
5155 private static final String EXTRA_CONVERSATION = "car_conversation";
5156 private static final String EXTRA_COLOR = "app_color";
5157
5158 private Bitmap mLargeIcon;
5159 private UnreadConversation mUnreadConversation;
5160 private int mColor = Notification.COLOR_DEFAULT;
5161
5162 /**
5163 * Create a {@link CarExtender} with default options.
5164 */
5165 public CarExtender() {
5166 }
5167
5168 /**
5169 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5170 *
5171 * @param notif The notification from which to copy options.
5172 */
5173 public CarExtender(Notification notif) {
5174 Bundle carBundle = notif.extras == null ?
5175 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5176 if (carBundle != null) {
5177 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5178 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5179
5180 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5181 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5182 }
5183 }
5184
5185 /**
5186 * Apply car extensions to a notification that is being built. This is typically called by
5187 * the {@link Notification.Builder#extend(Notification.Extender)}
5188 * method of {@link Notification.Builder}.
5189 */
5190 @Override
5191 public Notification.Builder extend(Notification.Builder builder) {
5192 Bundle carExtensions = new Bundle();
5193
5194 if (mLargeIcon != null) {
5195 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5196 }
5197 if (mColor != Notification.COLOR_DEFAULT) {
5198 carExtensions.putInt(EXTRA_COLOR, mColor);
5199 }
5200
5201 if (mUnreadConversation != null) {
5202 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5203 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5204 }
5205
5206 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5207 return builder;
5208 }
5209
5210 /**
5211 * Sets the accent color to use when Android Auto presents the notification.
5212 *
5213 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5214 * to accent the displayed notification. However, not all colors are acceptable in an
5215 * automotive setting. This method can be used to override the color provided in the
5216 * notification in such a situation.
5217 */
Tor Norbye80756e32015-03-02 09:39:27 -08005218 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005219 mColor = color;
5220 return this;
5221 }
5222
5223 /**
5224 * Gets the accent color.
5225 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005226 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005227 */
Tor Norbye80756e32015-03-02 09:39:27 -08005228 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005229 public int getColor() {
5230 return mColor;
5231 }
5232
5233 /**
5234 * Sets the large icon of the car notification.
5235 *
5236 * If no large icon is set in the extender, Android Auto will display the icon
5237 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5238 *
5239 * @param largeIcon The large icon to use in the car notification.
5240 * @return This object for method chaining.
5241 */
5242 public CarExtender setLargeIcon(Bitmap largeIcon) {
5243 mLargeIcon = largeIcon;
5244 return this;
5245 }
5246
5247 /**
5248 * Gets the large icon used in this car notification, or null if no icon has been set.
5249 *
5250 * @return The large icon for the car notification.
5251 * @see CarExtender#setLargeIcon
5252 */
5253 public Bitmap getLargeIcon() {
5254 return mLargeIcon;
5255 }
5256
5257 /**
5258 * Sets the unread conversation in a message notification.
5259 *
5260 * @param unreadConversation The unread part of the conversation this notification conveys.
5261 * @return This object for method chaining.
5262 */
5263 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5264 mUnreadConversation = unreadConversation;
5265 return this;
5266 }
5267
5268 /**
5269 * Returns the unread conversation conveyed by this notification.
5270 * @see #setUnreadConversation(UnreadConversation)
5271 */
5272 public UnreadConversation getUnreadConversation() {
5273 return mUnreadConversation;
5274 }
5275
5276 /**
5277 * A class which holds the unread messages from a conversation.
5278 */
5279 public static class UnreadConversation {
5280 private static final String KEY_AUTHOR = "author";
5281 private static final String KEY_TEXT = "text";
5282 private static final String KEY_MESSAGES = "messages";
5283 private static final String KEY_REMOTE_INPUT = "remote_input";
5284 private static final String KEY_ON_REPLY = "on_reply";
5285 private static final String KEY_ON_READ = "on_read";
5286 private static final String KEY_PARTICIPANTS = "participants";
5287 private static final String KEY_TIMESTAMP = "timestamp";
5288
5289 private final String[] mMessages;
5290 private final RemoteInput mRemoteInput;
5291 private final PendingIntent mReplyPendingIntent;
5292 private final PendingIntent mReadPendingIntent;
5293 private final String[] mParticipants;
5294 private final long mLatestTimestamp;
5295
5296 UnreadConversation(String[] messages, RemoteInput remoteInput,
5297 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5298 String[] participants, long latestTimestamp) {
5299 mMessages = messages;
5300 mRemoteInput = remoteInput;
5301 mReadPendingIntent = readPendingIntent;
5302 mReplyPendingIntent = replyPendingIntent;
5303 mParticipants = participants;
5304 mLatestTimestamp = latestTimestamp;
5305 }
5306
5307 /**
5308 * Gets the list of messages conveyed by this notification.
5309 */
5310 public String[] getMessages() {
5311 return mMessages;
5312 }
5313
5314 /**
5315 * Gets the remote input that will be used to convey the response to a message list, or
5316 * null if no such remote input exists.
5317 */
5318 public RemoteInput getRemoteInput() {
5319 return mRemoteInput;
5320 }
5321
5322 /**
5323 * Gets the pending intent that will be triggered when the user replies to this
5324 * notification.
5325 */
5326 public PendingIntent getReplyPendingIntent() {
5327 return mReplyPendingIntent;
5328 }
5329
5330 /**
5331 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5332 * in this object's message list.
5333 */
5334 public PendingIntent getReadPendingIntent() {
5335 return mReadPendingIntent;
5336 }
5337
5338 /**
5339 * Gets the participants in the conversation.
5340 */
5341 public String[] getParticipants() {
5342 return mParticipants;
5343 }
5344
5345 /**
5346 * Gets the firs participant in the conversation.
5347 */
5348 public String getParticipant() {
5349 return mParticipants.length > 0 ? mParticipants[0] : null;
5350 }
5351
5352 /**
5353 * Gets the timestamp of the conversation.
5354 */
5355 public long getLatestTimestamp() {
5356 return mLatestTimestamp;
5357 }
5358
5359 Bundle getBundleForUnreadConversation() {
5360 Bundle b = new Bundle();
5361 String author = null;
5362 if (mParticipants != null && mParticipants.length > 1) {
5363 author = mParticipants[0];
5364 }
5365 Parcelable[] messages = new Parcelable[mMessages.length];
5366 for (int i = 0; i < messages.length; i++) {
5367 Bundle m = new Bundle();
5368 m.putString(KEY_TEXT, mMessages[i]);
5369 m.putString(KEY_AUTHOR, author);
5370 messages[i] = m;
5371 }
5372 b.putParcelableArray(KEY_MESSAGES, messages);
5373 if (mRemoteInput != null) {
5374 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5375 }
5376 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5377 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5378 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5379 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5380 return b;
5381 }
5382
5383 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5384 if (b == null) {
5385 return null;
5386 }
5387 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5388 String[] messages = null;
5389 if (parcelableMessages != null) {
5390 String[] tmp = new String[parcelableMessages.length];
5391 boolean success = true;
5392 for (int i = 0; i < tmp.length; i++) {
5393 if (!(parcelableMessages[i] instanceof Bundle)) {
5394 success = false;
5395 break;
5396 }
5397 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5398 if (tmp[i] == null) {
5399 success = false;
5400 break;
5401 }
5402 }
5403 if (success) {
5404 messages = tmp;
5405 } else {
5406 return null;
5407 }
5408 }
5409
5410 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5411 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5412
5413 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5414
5415 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5416 if (participants == null || participants.length != 1) {
5417 return null;
5418 }
5419
5420 return new UnreadConversation(messages,
5421 remoteInput,
5422 onReply,
5423 onRead,
5424 participants, b.getLong(KEY_TIMESTAMP));
5425 }
5426 };
5427
5428 /**
5429 * Builder class for {@link CarExtender.UnreadConversation} objects.
5430 */
5431 public static class Builder {
5432 private final List<String> mMessages = new ArrayList<String>();
5433 private final String mParticipant;
5434 private RemoteInput mRemoteInput;
5435 private PendingIntent mReadPendingIntent;
5436 private PendingIntent mReplyPendingIntent;
5437 private long mLatestTimestamp;
5438
5439 /**
5440 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5441 *
5442 * @param name The name of the other participant in the conversation.
5443 */
5444 public Builder(String name) {
5445 mParticipant = name;
5446 }
5447
5448 /**
5449 * Appends a new unread message to the list of messages for this conversation.
5450 *
5451 * The messages should be added from oldest to newest.
5452 *
5453 * @param message The text of the new unread message.
5454 * @return This object for method chaining.
5455 */
5456 public Builder addMessage(String message) {
5457 mMessages.add(message);
5458 return this;
5459 }
5460
5461 /**
5462 * Sets the pending intent and remote input which will convey the reply to this
5463 * notification.
5464 *
5465 * @param pendingIntent The pending intent which will be triggered on a reply.
5466 * @param remoteInput The remote input parcelable which will carry the reply.
5467 * @return This object for method chaining.
5468 *
5469 * @see CarExtender.UnreadConversation#getRemoteInput
5470 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5471 */
5472 public Builder setReplyAction(
5473 PendingIntent pendingIntent, RemoteInput remoteInput) {
5474 mRemoteInput = remoteInput;
5475 mReplyPendingIntent = pendingIntent;
5476
5477 return this;
5478 }
5479
5480 /**
5481 * Sets the pending intent that will be sent once the messages in this notification
5482 * are read.
5483 *
5484 * @param pendingIntent The pending intent to use.
5485 * @return This object for method chaining.
5486 */
5487 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5488 mReadPendingIntent = pendingIntent;
5489 return this;
5490 }
5491
5492 /**
5493 * Sets the timestamp of the most recent message in an unread conversation.
5494 *
5495 * If a messaging notification has been posted by your application and has not
5496 * yet been cancelled, posting a later notification with the same id and tag
5497 * but without a newer timestamp may result in Android Auto not displaying a
5498 * heads up notification for the later notification.
5499 *
5500 * @param timestamp The timestamp of the most recent message in the conversation.
5501 * @return This object for method chaining.
5502 */
5503 public Builder setLatestTimestamp(long timestamp) {
5504 mLatestTimestamp = timestamp;
5505 return this;
5506 }
5507
5508 /**
5509 * Builds a new unread conversation object.
5510 *
5511 * @return The new unread conversation object.
5512 */
5513 public UnreadConversation build() {
5514 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5515 String[] participants = { mParticipant };
5516 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5517 mReadPendingIntent, participants, mLatestTimestamp);
5518 }
5519 }
5520 }
5521
5522 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005523 * Get an array of Notification objects from a parcelable array bundle field.
5524 * Update the bundle to have a typed array so fetches in the future don't need
5525 * to do an array copy.
5526 */
5527 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5528 Parcelable[] array = bundle.getParcelableArray(key);
5529 if (array instanceof Notification[] || array == null) {
5530 return (Notification[]) array;
5531 }
5532 Notification[] typedArray = Arrays.copyOf(array, array.length,
5533 Notification[].class);
5534 bundle.putParcelableArray(key, typedArray);
5535 return typedArray;
5536 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005537
5538 private static class BuilderRemoteViews extends RemoteViews {
5539 public BuilderRemoteViews(Parcel parcel) {
5540 super(parcel);
5541 }
5542
Kenny Guy77320062014-08-27 21:37:15 +01005543 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5544 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005545 }
5546
5547 @Override
5548 public BuilderRemoteViews clone() {
5549 Parcel p = Parcel.obtain();
5550 writeToParcel(p, 0);
5551 p.setDataPosition(0);
5552 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5553 p.recycle();
5554 return brv;
5555 }
5556 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005557}