blob: 49edff411342827358e2c46db82e043e8dac1cd7 [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;
Joe Onorato46439ce2010-11-19 13:56:21 -08002602 return this;
2603 }
2604
Joe Onoratocb109a02011-01-18 17:57:41 -08002605 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002606 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002607 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002608
2609 * Ongoing notifications cannot be dismissed by the user, so your application or service
2610 * must take care of canceling them.
2611 *
2612
2613 * They are typically used to indicate a background task that the user is actively engaged
2614 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2615 * (e.g., a file download, sync operation, active network connection).
2616 *
2617
2618 * @see Notification#FLAG_ONGOING_EVENT
2619 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002620 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002621 public Builder setOngoing(boolean ongoing) {
2622 setFlag(FLAG_ONGOING_EVENT, ongoing);
2623 return this;
2624 }
2625
Joe Onoratocb109a02011-01-18 17:57:41 -08002626 /**
2627 * Set this flag if you would only like the sound, vibrate
2628 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002629 *
2630 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002631 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002632 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2633 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2634 return this;
2635 }
2636
Joe Onoratocb109a02011-01-18 17:57:41 -08002637 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002638 * Make this notification automatically dismissed when the user touches it. The
2639 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2640 *
2641 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002642 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002643 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002644 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002645 return this;
2646 }
2647
Joe Onoratocb109a02011-01-18 17:57:41 -08002648 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002649 * Set whether or not this notification should not bridge to other devices.
2650 *
2651 * <p>Some notifications can be bridged to other devices for remote display.
2652 * This hint can be set to recommend this notification not be bridged.
2653 */
2654 public Builder setLocalOnly(boolean localOnly) {
2655 setFlag(FLAG_LOCAL_ONLY, localOnly);
2656 return this;
2657 }
2658
2659 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002660 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002661 * <p>
2662 * The value should be one or more of the following fields combined with
2663 * bitwise-or:
2664 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2665 * <p>
2666 * For all default values, use {@link #DEFAULT_ALL}.
2667 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002668 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002669 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002670 return this;
2671 }
2672
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002673 /**
2674 * Set the priority of this notification.
2675 *
2676 * @see Notification#priority
2677 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002678 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002679 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002680 return this;
2681 }
Joe Malin8d40d042012-11-05 11:36:40 -08002682
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002683 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002684 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002685 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002686 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002687 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002688 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002689 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002690 return this;
2691 }
2692
2693 /**
Chris Wrendde75302014-03-26 17:24:15 -04002694 * Add a person that is relevant to this notification.
2695 *
Chris Wrene6c48932014-09-29 17:19:27 -04002696 * <P>
2697 * Depending on user preferences, this annotation may allow the notification to pass
2698 * through interruption filters, and to appear more prominently in the user interface.
2699 * </P>
2700 *
2701 * <P>
2702 * The person should be specified by the {@code String} representation of a
2703 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2704 * </P>
2705 *
2706 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2707 * URIs. The path part of these URIs must exist in the contacts database, in the
2708 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2709 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2710 * </P>
2711 *
2712 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002713 * @see Notification#EXTRA_PEOPLE
2714 */
Chris Wrene6c48932014-09-29 17:19:27 -04002715 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002716 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002717 return this;
2718 }
2719
2720 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002721 * Set this notification to be part of a group of notifications sharing the same key.
2722 * Grouped notifications may display in a cluster or stack on devices which
2723 * support such rendering.
2724 *
2725 * <p>To make this notification the summary for its group, also call
2726 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2727 * {@link #setSortKey}.
2728 * @param groupKey The group key of the group.
2729 * @return this object for method chaining
2730 */
2731 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002732 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002733 return this;
2734 }
2735
2736 /**
2737 * Set this notification to be the group summary for a group of notifications.
2738 * Grouped notifications may display in a cluster or stack on devices which
2739 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2740 * @param isGroupSummary Whether this notification should be a group summary.
2741 * @return this object for method chaining
2742 */
2743 public Builder setGroupSummary(boolean isGroupSummary) {
2744 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2745 return this;
2746 }
2747
2748 /**
2749 * Set a sort key that orders this notification among other notifications from the
2750 * same package. This can be useful if an external sort was already applied and an app
2751 * would like to preserve this. Notifications will be sorted lexicographically using this
2752 * value, although providing different priorities in addition to providing sort key may
2753 * cause this value to be ignored.
2754 *
2755 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002756 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002757 *
2758 * @see String#compareTo(String)
2759 */
2760 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002761 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002762 return this;
2763 }
2764
2765 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002766 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002767 *
Griff Hazen720042b2014-02-24 15:46:56 -08002768 * <p>Values within the Bundle will replace existing extras values in this Builder.
2769 *
2770 * @see Notification#extras
2771 */
Griff Hazen959591e2014-05-15 22:26:18 -07002772 public Builder addExtras(Bundle extras) {
2773 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002774 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002775 }
2776 return this;
2777 }
2778
2779 /**
2780 * Set metadata for this notification.
2781 *
2782 * <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 -04002783 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002784 * called.
2785 *
Griff Hazen720042b2014-02-24 15:46:56 -08002786 * <p>Replaces any existing extras values with those from the provided Bundle.
2787 * Use {@link #addExtras} to merge in metadata instead.
2788 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002789 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002790 */
Griff Hazen959591e2014-05-15 22:26:18 -07002791 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002792 if (extras != null) {
2793 mUserExtras = extras;
2794 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002795 return this;
2796 }
2797
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002798 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002799 * Get the current metadata Bundle used by this notification Builder.
2800 *
2801 * <p>The returned Bundle is shared with this Builder.
2802 *
2803 * <p>The current contents of this Bundle are copied into the Notification each time
2804 * {@link #build()} is called.
2805 *
2806 * @see Notification#extras
2807 */
2808 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002809 return mUserExtras;
2810 }
2811
2812 private Bundle getAllExtras() {
2813 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2814 saveExtras.putAll(mN.extras);
2815 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002816 }
2817
2818 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002819 * Add an action to this notification. Actions are typically displayed by
2820 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002821 * <p>
2822 * Every action must have an icon (32dp square and matching the
2823 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2824 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2825 * <p>
2826 * A notification in its expanded form can display up to 3 actions, from left to right in
2827 * the order they were added. Actions will not be displayed when the notification is
2828 * collapsed, however, so be sure that any essential functions may be accessed by the user
2829 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002830 *
2831 * @param icon Resource ID of a drawable that represents the action.
2832 * @param title Text describing the action.
2833 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002834 *
2835 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002836 */
Dan Sandler86647982015-05-13 23:41:13 -04002837 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002838 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002839 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002840 return this;
2841 }
2842
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002843 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002844 * Add an action to this notification. Actions are typically displayed by
2845 * the system as a button adjacent to the notification content.
2846 * <p>
2847 * Every action must have an icon (32dp square and matching the
2848 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2849 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2850 * <p>
2851 * A notification in its expanded form can display up to 3 actions, from left to right in
2852 * the order they were added. Actions will not be displayed when the notification is
2853 * collapsed, however, so be sure that any essential functions may be accessed by the user
2854 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2855 *
2856 * @param action The action to add.
2857 */
2858 public Builder addAction(Action action) {
2859 mActions.add(action);
2860 return this;
2861 }
2862
2863 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002864 * Alter the complete list of actions attached to this notification.
2865 * @see #addAction(Action).
2866 *
2867 * @param actions
2868 * @return
2869 */
2870 public Builder setActions(Action... actions) {
2871 mActions.clear();
2872 for (int i = 0; i < actions.length; i++) {
2873 mActions.add(actions[i]);
2874 }
2875 return this;
2876 }
2877
2878 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002879 * Add a rich notification style to be applied at build time.
2880 *
2881 * @param style Object responsible for modifying the notification style.
2882 */
2883 public Builder setStyle(Style style) {
2884 if (mStyle != style) {
2885 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002886 if (mStyle != null) {
2887 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002888 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
2889 } else {
2890 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002891 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002892 }
2893 return this;
2894 }
2895
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002896 /**
2897 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07002898 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002899 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2900 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2901 *
2902 * @return The same Builder.
2903 */
2904 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002905 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002906 return this;
2907 }
2908
2909 /**
2910 * Supply a replacement Notification whose contents should be shown in insecure contexts
2911 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2912 * @param n A replacement notification, presumably with some or all info redacted.
2913 * @return The same Builder.
2914 */
2915 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002916 if (n != null) {
2917 mN.publicVersion = new Notification();
2918 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
2919 } else {
2920 mN.publicVersion = null;
2921 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002922 return this;
2923 }
2924
Griff Hazenb720abe2014-05-20 13:15:30 -07002925 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002926 * Apply an extender to this notification builder. Extenders may be used to add
2927 * metadata or change options on this builder.
2928 */
Griff Hazen61a9e862014-05-22 16:05:19 -07002929 public Builder extend(Extender extender) {
2930 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002931 return this;
2932 }
2933
Dan Sandler4e787062015-06-17 15:09:48 -04002934 /**
2935 * @hide
2936 */
2937 public void setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002938 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002939 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002940 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002941 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002942 }
2943 }
2944
Dan Sandler26e81cf2014-05-06 10:01:27 -04002945 /**
2946 * Sets {@link Notification#color}.
2947 *
2948 * @param argb The accent color to use
2949 *
2950 * @return The same Builder.
2951 */
Tor Norbye80756e32015-03-02 09:39:27 -08002952 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002953 mN.color = argb;
Dan Sandler26e81cf2014-05-06 10:01:27 -04002954 return this;
2955 }
2956
Julia Reynolds74303cf2015-10-16 11:37:55 -04002957 /**
2958 * Add a topic to this notification. Topics are typically displayed in Notification
2959 * settings.
2960 * <p>
2961 * Every topic must have an id and a textual label.
2962 *
2963 * @param topic The topic to add.
2964 */
2965 public Builder addTopic(Topic topic) {
2966 mTopics.add(topic);
2967 return this;
2968 }
2969
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002970 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02002971 // Note: This assumes that the current user can read the profile badge of the
2972 // originating user.
Svetoslavc7d62f02014-09-04 15:39:54 -07002973 return mContext.getPackageManager().getUserBadgeForDensity(
Kenny Guy8942bcd2014-09-08 21:09:47 +01002974 new UserHandle(mOriginatingUserId), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002975 }
2976
2977 private Bitmap getProfileBadge() {
2978 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01002979 if (badge == null) {
2980 return null;
2981 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002982 final int size = mContext.getResources().getDimensionPixelSize(
2983 R.dimen.notification_badge_size);
2984 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002985 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002986 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002987 badge.draw(canvas);
2988 return bitmap;
2989 }
2990
Kenny Guy98193ea2014-07-24 19:54:37 +01002991 private boolean addProfileBadge(RemoteViews contentView, int resId) {
2992 Bitmap profileBadge = getProfileBadge();
2993
2994 contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
2995 contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
2996 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2997
2998 if (profileBadge != null) {
2999 contentView.setImageViewBitmap(resId, profileBadge);
3000 contentView.setViewVisibility(resId, View.VISIBLE);
3001
3002 // Make sure Line 3 is visible. As badge will be here if there
3003 // is no text to display.
3004 if (resId == R.id.profile_badge_line3) {
3005 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3006 }
3007 return true;
3008 }
3009 return false;
3010 }
3011
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003012 private void shrinkLine3Text(RemoteViews contentView) {
3013 float subTextSize = mContext.getResources().getDimensionPixelSize(
3014 R.dimen.notification_subtext_size);
3015 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
3016 }
3017
Christoph Studerfe718432014-09-01 18:21:18 +02003018 private void unshrinkLine3Text(RemoteViews contentView) {
3019 float regularTextSize = mContext.getResources().getDimensionPixelSize(
3020 com.android.internal.R.dimen.notification_text_size);
3021 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
3022 }
3023
3024 private void resetStandardTemplate(RemoteViews contentView) {
3025 removeLargeIconBackground(contentView);
3026 contentView.setViewPadding(R.id.icon, 0, 0, 0, 0);
3027 contentView.setImageViewResource(R.id.icon, 0);
3028 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3029 contentView.setViewVisibility(R.id.right_icon, View.GONE);
3030 contentView.setInt(R.id.right_icon, "setBackgroundResource", 0);
3031 contentView.setImageViewResource(R.id.right_icon, 0);
3032 contentView.setImageViewResource(R.id.icon, 0);
3033 contentView.setTextViewText(R.id.title, null);
3034 contentView.setTextViewText(R.id.text, null);
3035 unshrinkLine3Text(contentView);
3036 contentView.setTextViewText(R.id.text2, null);
3037 contentView.setViewVisibility(R.id.text2, View.GONE);
3038 contentView.setViewVisibility(R.id.info, View.GONE);
3039 contentView.setViewVisibility(R.id.time, View.GONE);
3040 contentView.setViewVisibility(R.id.line3, View.GONE);
3041 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3042 contentView.setViewVisibility(R.id.progress, View.GONE);
Christoph Studerca1db712014-09-10 17:31:33 +02003043 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003044 }
3045
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003046 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003047 return applyStandardTemplate(resId, true /* hasProgress */);
3048 }
3049
3050 /**
3051 * @param hasProgress whether the progress bar should be shown and set
3052 */
3053 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003054 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003055
Christoph Studerfe718432014-09-01 18:21:18 +02003056 resetStandardTemplate(contentView);
3057
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003058 boolean showLine3 = false;
3059 boolean showLine2 = false;
Kenny Guy98193ea2014-07-24 19:54:37 +01003060 boolean contentTextInLine2 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003061 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003062
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003063 if (mN.mLargeIcon != null) {
3064 contentView.setImageViewIcon(R.id.icon, mN.mLargeIcon);
3065 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3066 contentView.setImageViewIcon(R.id.right_icon, mN.mSmallIcon);
Dan Sandler190d58d2014-05-15 09:33:39 -04003067 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003068 processSmallRightIcon(mN.mSmallIcon, contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04003069 } else { // small icon at left
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003070 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
Dan Sandler190d58d2014-05-15 09:33:39 -04003071 contentView.setViewVisibility(R.id.icon, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003072 processSmallIconAsLarge(mN.mSmallIcon, contentView);
Joe Onorato561d3852010-11-20 18:09:34 -08003073 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003074 if (ex.getCharSequence(EXTRA_TITLE) != null) {
3075 contentView.setTextViewText(R.id.title,
3076 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003077 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003078 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3079 contentView.setTextViewText(R.id.text,
3080 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003081 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003082 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003083 if (ex.getCharSequence(EXTRA_INFO_TEXT) != null) {
3084 contentView.setTextViewText(R.id.info,
3085 processLegacyText(ex.getCharSequence(EXTRA_INFO_TEXT)));
Jeff Sharkey1c400132011-08-05 14:50:13 -07003086 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003087 showLine3 = true;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003088 } else if (mN.number > 0) {
Daniel Sandlerebce0112011-06-16 16:44:51 -04003089 final int tooBig = mContext.getResources().getInteger(
3090 R.integer.status_bar_notification_info_maxnum);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003091 if (mN.number > tooBig) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003092 contentView.setTextViewText(R.id.info, processLegacyText(
3093 mContext.getResources().getString(
3094 R.string.status_bar_notification_info_overflow)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003095 } else {
3096 NumberFormat f = NumberFormat.getIntegerInstance();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003097 contentView.setTextViewText(R.id.info, processLegacyText(f.format(mN.number)));
Joe Onorato059a2f82011-01-04 10:27:01 -08003098 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003099 contentView.setViewVisibility(R.id.info, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003100 showLine3 = true;
Joe Onorato561d3852010-11-20 18:09:34 -08003101 } else {
3102 contentView.setViewVisibility(R.id.info, View.GONE);
3103 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003104
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003105 // Need to show three lines?
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003106 if (ex.getCharSequence(EXTRA_SUB_TEXT) != null) {
3107 contentView.setTextViewText(R.id.text,
3108 processLegacyText(ex.getCharSequence(EXTRA_SUB_TEXT)));
3109 if (ex.getCharSequence(EXTRA_TEXT) != null) {
3110 contentView.setTextViewText(R.id.text2,
3111 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003112 contentView.setViewVisibility(R.id.text2, View.VISIBLE);
3113 showLine2 = true;
Kenny Guy98193ea2014-07-24 19:54:37 +01003114 contentTextInLine2 = true;
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003115 } else {
3116 contentView.setViewVisibility(R.id.text2, View.GONE);
3117 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003118 } else {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003119 contentView.setViewVisibility(R.id.text2, View.GONE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003120 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3121 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3122 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3123 if (hasProgress && (max != 0 || ind)) {
Christoph Studeraca4b252014-09-09 15:58:41 +02003124 contentView.setViewVisibility(R.id.progress, View.VISIBLE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003125 contentView.setProgressBar(
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003126 R.id.progress, max, progress, ind);
Jorim Jaggief72a192014-08-26 21:57:46 +02003127 contentView.setProgressBackgroundTintList(
Alan Viverette4a357cd2015-03-18 18:37:18 -07003128 R.id.progress, ColorStateList.valueOf(mContext.getColor(
Jorim Jaggief72a192014-08-26 21:57:46 +02003129 R.color.notification_progress_background_color)));
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003130 if (mN.color != COLOR_DEFAULT) {
3131 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Jorim Jaggief72a192014-08-26 21:57:46 +02003132 contentView.setProgressTintList(R.id.progress, colorStateList);
3133 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
3134 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003135 showLine2 = true;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003136 } else {
3137 contentView.setViewVisibility(R.id.progress, View.GONE);
3138 }
Jeff Sharkey1c400132011-08-05 14:50:13 -07003139 }
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003140 if (showLine2) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003141
3142 // need to shrink all the type to make sure everything fits
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003143 shrinkLine3Text(contentView);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003144 }
3145
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003146 if (showsTimeOrChronometer()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003147 if (ex.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003148 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3149 contentView.setLong(R.id.chronometer, "setBase",
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003150 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003151 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3152 } else {
3153 contentView.setViewVisibility(R.id.time, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003154 contentView.setLong(R.id.time, "setTime", mN.when);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04003155 }
Joe Onorato561d3852010-11-20 18:09:34 -08003156 }
Daniel Sandler0c890492012-09-12 17:23:10 -07003157
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003158 // Adjust padding depending on line count and font size.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003159 contentView.setViewPadding(R.id.line1, 0,
3160 calculateTopPadding(mContext, hasThreeLines(),
3161 mContext.getResources().getConfiguration().fontScale),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003162 0, 0);
3163
Kenny Guy98193ea2014-07-24 19:54:37 +01003164 // We want to add badge to first line of text.
3165 boolean addedBadge = addProfileBadge(contentView,
3166 contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
3167 // If we added the badge to line 3 then we should show line 3.
3168 if (addedBadge && !contentTextInLine2) {
3169 showLine3 = true;
3170 }
3171
3172 // Note getStandardView may hide line 3 again.
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003173 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003174 contentView.setViewVisibility(R.id.overflow_divider,
3175 showLine3 ? View.VISIBLE : View.GONE);
Joe Onorato561d3852010-11-20 18:09:34 -08003176 return contentView;
3177 }
3178
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003179 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003180 * @return true if the built notification will show the time or the chronometer; false
3181 * otherwise
3182 */
3183 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003184 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003185 }
3186
3187 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003188 * Logic to find out whether the notification is going to have three lines in the contracted
3189 * layout. This is used to adjust the top padding.
3190 *
3191 * @return true if the notification is going to have three lines; false if the notification
3192 * is going to have one or two lines
3193 */
3194 private boolean hasThreeLines() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003195 final CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3196 final CharSequence text = mN.extras.getCharSequence(EXTRA_TEXT);
3197 boolean contentTextInLine2 = subText != null && text != null;
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003198 boolean hasProgress = mStyle == null || mStyle.hasProgress();
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003199 // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
3200 boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003201 boolean hasLine3 = text != null || mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null
3202 || mN.number > 0 || badgeInLine3;
3203 final Bundle ex = mN.extras;
3204 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3205 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3206 boolean hasLine2 = (subText != null && text != null) ||
3207 (hasProgress && subText == null && (max != 0 || ind));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003208 return hasLine2 && hasLine3;
3209 }
3210
3211 /**
3212 * @hide
3213 */
3214 public static int calculateTopPadding(Context ctx, boolean hasThreeLines,
3215 float fontScale) {
3216 int padding = ctx.getResources().getDimensionPixelSize(hasThreeLines
3217 ? R.dimen.notification_top_pad_narrow
3218 : R.dimen.notification_top_pad);
3219 int largePadding = ctx.getResources().getDimensionPixelSize(hasThreeLines
3220 ? R.dimen.notification_top_pad_large_text_narrow
3221 : R.dimen.notification_top_pad_large_text);
3222 float largeFactor = (MathUtils.constrain(fontScale, 1.0f, LARGE_TEXT_SCALE) - 1f)
3223 / (LARGE_TEXT_SCALE - 1f);
3224
3225 // Linearly interpolate the padding between large and normal with the font scale ranging
3226 // from 1f to LARGE_TEXT_SCALE
3227 return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
3228 }
3229
Christoph Studerfe718432014-09-01 18:21:18 +02003230 private void resetStandardTemplateWithActions(RemoteViews big) {
3231 big.setViewVisibility(R.id.actions, View.GONE);
3232 big.setViewVisibility(R.id.action_divider, View.GONE);
3233 big.removeAllViews(R.id.actions);
3234 }
3235
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003236 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003237 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003238
Christoph Studerfe718432014-09-01 18:21:18 +02003239 resetStandardTemplateWithActions(big);
3240
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003241 int N = mActions.size();
3242 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003243 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel Sandler6387d2f2012-05-22 13:44:09 -04003244 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
Daniel Sandler8680bf82012-05-15 16:52:52 -04003245 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003246 for (int i=0; i<N; i++) {
3247 final RemoteViews button = generateActionButton(mActions.get(i));
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003248 big.addView(R.id.actions, button);
3249 }
3250 }
3251 return big;
3252 }
3253
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003254 /**
3255 * Construct a RemoteViews for the final 1U notification layout. In order:
3256 * 1. Custom contentView from the caller
3257 * 2. Style's proposed content view
3258 * 3. Standard template view
3259 */
3260 public RemoteViews makeContentView() {
3261 if (mN.contentView != null) {
3262 return mN.contentView;
3263 } else if (mStyle != null) {
3264 final RemoteViews styleView = mStyle.makeContentView();
3265 if (styleView != null) {
3266 return styleView;
3267 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003268 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003269 return applyStandardTemplate(getBaseLayoutResource());
Joe Onorato46439ce2010-11-19 13:56:21 -08003270 }
3271
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003272 /**
3273 * Construct a RemoteViews for the final big notification layout.
3274 */
3275 public RemoteViews makeBigContentView() {
3276 if (mStyle != null) {
3277 final RemoteViews styleView = mStyle.makeBigContentView();
3278 if (styleView != null) {
3279 return styleView;
3280 }
3281 } else if (mActions.size() == 0) return null;
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003282
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003283 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003284 }
3285
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003286 /**
3287 * Construct a RemoteViews for the final heads-up notification layout.
3288 */
3289 public RemoteViews makeHeadsUpContentView() {
3290 if (mStyle != null) {
3291 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3292 if (styleView != null) {
3293 return styleView;
3294 }
3295 } else if (mActions.size() == 0) return null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05003296
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003297 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003298 }
3299
3300
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003301 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003302 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003303 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003304 tombstone ? getActionTombstoneLayoutResource()
3305 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003306 final Icon ai = action.getIcon();
Dan Sandler912282e2015-07-28 22:49:30 -04003307 button.setTextViewCompoundDrawablesRelative(R.id.action0, ai, null, null, null);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003308 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003309 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003310 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003311 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003312 button.setContentDescription(R.id.action0, action.title);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003313 processLegacyAction(action, button);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003314 return button;
3315 }
3316
Joe Onoratocb109a02011-01-18 17:57:41 -08003317 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003318 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003319 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003320 */
3321 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003322 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003323 }
3324
3325 private void processLegacyAction(Action action, RemoteViews button) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003326 if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
Christoph Studer239f8352014-08-25 15:13:18 +02003327 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
Alan Viverette4a357cd2015-03-18 18:37:18 -07003328 mContext.getColor(R.color.notification_action_color_filter),
Christoph Studer239f8352014-08-25 15:13:18 +02003329 PorterDuff.Mode.MULTIPLY);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003330 }
3331 }
3332
3333 private CharSequence processLegacyText(CharSequence charSequence) {
3334 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003335 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003336 } else {
3337 return charSequence;
3338 }
3339 }
3340
Dan Sandler26e81cf2014-05-06 10:01:27 -04003341 /**
3342 * Apply any necessary background to smallIcons being used in the largeIcon spot.
3343 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003344 private void processSmallIconAsLarge(Icon largeIcon, RemoteViews contentView) {
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003345 if (!isLegacy()) {
3346 contentView.setDrawableParameters(R.id.icon, false, -1,
3347 0xFFFFFFFF,
3348 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003349 applyLargeIconBackground(contentView);
Dan Sandlerd63f9322015-05-06 15:18:49 -04003350 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003351 if (getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003352 applyLargeIconBackground(contentView);
3353 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003354 }
3355 }
3356
Dan Sandler26e81cf2014-05-06 10:01:27 -04003357 /**
3358 * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
3359 * if it's grayscale).
3360 */
3361 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003362 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3363 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003364 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Dan Sandler26e81cf2014-05-06 10:01:27 -04003365 applyLargeIconBackground(contentView);
Dan Sandler190d58d2014-05-15 09:33:39 -04003366 } else {
3367 removeLargeIconBackground(contentView);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003368 }
3369 }
3370
Dan Sandler26e81cf2014-05-06 10:01:27 -04003371 /**
3372 * Add a colored circle behind the largeIcon slot.
3373 */
3374 private void applyLargeIconBackground(RemoteViews contentView) {
3375 contentView.setInt(R.id.icon, "setBackgroundResource",
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003376 R.drawable.notification_icon_legacy_bg);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003377
3378 contentView.setDrawableParameters(
3379 R.id.icon,
3380 true,
3381 -1,
Jorim Jaggi74419312014-06-10 20:57:21 +02003382 resolveColor(),
Dan Sandler26e81cf2014-05-06 10:01:27 -04003383 PorterDuff.Mode.SRC_ATOP,
3384 -1);
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003385
3386 int padding = mContext.getResources().getDimensionPixelSize(
3387 R.dimen.notification_large_icon_circle_padding);
3388 contentView.setViewPadding(R.id.icon, padding, padding, padding, padding);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003389 }
3390
Dan Sandler190d58d2014-05-15 09:33:39 -04003391 private void removeLargeIconBackground(RemoteViews contentView) {
3392 contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3393 }
3394
Dan Sandler26e81cf2014-05-06 10:01:27 -04003395 /**
3396 * Recolor small icons when used in the R.id.right_icon slot.
3397 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04003398 private void processSmallRightIcon(Icon smallIcon, RemoteViews contentView) {
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003399 if (!isLegacy()) {
Dan Sandler190d58d2014-05-15 09:33:39 -04003400 contentView.setDrawableParameters(R.id.right_icon, false, -1,
3401 0xFFFFFFFF,
3402 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003403 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04003404 final boolean gray = isLegacy()
3405 && smallIcon.getType() == Icon.TYPE_RESOURCE
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003406 && getColorUtil().isGrayscaleIcon(mContext, smallIcon.getResId());
Dan Sandlerd63f9322015-05-06 15:18:49 -04003407 if (!isLegacy() || gray) {
Dan Sandler190d58d2014-05-15 09:33:39 -04003408 contentView.setInt(R.id.right_icon,
3409 "setBackgroundResource",
3410 R.drawable.notification_icon_legacy_bg);
3411
3412 contentView.setDrawableParameters(
3413 R.id.right_icon,
3414 true,
3415 -1,
Jorim Jaggi74419312014-06-10 20:57:21 +02003416 resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003417 PorterDuff.Mode.SRC_ATOP,
3418 -1);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003419 }
3420 }
3421
Jorim Jaggi74419312014-06-10 20:57:21 +02003422 private int sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003423 if (mN.color != COLOR_DEFAULT) {
3424 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003425 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003426 return mN.color;
Jorim Jaggi74419312014-06-10 20:57:21 +02003427 }
3428
Dan Sandler26e81cf2014-05-06 10:01:27 -04003429 private int resolveColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003430 if (mN.color == COLOR_DEFAULT) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07003431 return mContext.getColor(R.color.notification_icon_bg_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003432 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003433 return mN.color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003434 }
3435
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003436 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003437 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003438 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003439 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003440 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003441 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003442 mN.actions = new Action[mActions.size()];
3443 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003444 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003445 if (!mPersonList.isEmpty()) {
3446 mN.extras.putStringArray(EXTRA_PEOPLE,
3447 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003448 }
Julia Reynolds74303cf2015-10-16 11:37:55 -04003449 if (mTopics.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003450 mN.topics = new Topic[mTopics.size()];
3451 mTopics.toArray(mN.topics);
Julia Reynolds74303cf2015-10-16 11:37:55 -04003452 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003453 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003454 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003455
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003456 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003457 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003458 ApplicationInfo applicationInfo = n.extras.getParcelable(
3459 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003460 Context builderContext;
3461 try {
Kenny Guy77320062014-08-27 21:37:15 +01003462 builderContext = context.createApplicationContext(applicationInfo,
Christoph Studer4600f9b2014-07-22 22:44:43 +02003463 Context.CONTEXT_RESTRICTED);
3464 } catch (NameNotFoundException e) {
Kenny Guy77320062014-08-27 21:37:15 +01003465 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
Christoph Studer4600f9b2014-07-22 22:44:43 +02003466 builderContext = context; // try with our context
3467 }
3468
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003469 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003470 }
3471
3472 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3473 Class<? extends Style>[] classes = new Class[]{
3474 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3475 for (Class<? extends Style> innerClass : classes) {
3476 if (templateClass.equals(innerClass.getName())) {
3477 return innerClass;
3478 }
3479 }
3480 return null;
3481 }
3482
3483 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3484 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003485 }
3486
3487 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3488 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003489 }
3490
3491 private void setBuilderHeadsUpContentView(Notification n,
3492 RemoteViews headsUpContentView) {
3493 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003494 }
3495
3496 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003497 * @deprecated Use {@link #build()} instead.
3498 */
3499 @Deprecated
3500 public Notification getNotification() {
3501 return build();
3502 }
3503
3504 /**
3505 * Combine all of the options that have been set and return a new {@link Notification}
3506 * object.
3507 */
3508 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003509 // first, add any extras from the calling code
3510 if (mUserExtras != null) {
3511 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003512 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003513
3514 // lazy stuff from mContext; see comment in Builder(Context, Notification)
3515 mN.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, mContext.getApplicationInfo());
Kenny Guy8942bcd2014-09-08 21:09:47 +01003516 mOriginatingUserId = mContext.getUserId();
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003517 mN.extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
Christoph Studer943aa672014-08-03 20:31:16 +02003518
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003519 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003520
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003521 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003522 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003523 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003524
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003525 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003526 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003527
3528 /**
3529 * Apply this Builder to an existing {@link Notification} object.
3530 *
3531 * @hide
3532 */
3533 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003534 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003535 return n;
3536 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003537
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003538 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003539 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003540 }
3541
3542 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003543 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003544 }
3545
3546 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003547 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003548 }
3549
3550 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003551 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003552 }
3553
3554 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003555 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003556 }
3557
3558 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003559 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003560 }
3561
3562 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003563 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003564 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003565 }
3566
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003567 /**
3568 * An object that can apply a rich notification style to a {@link Notification.Builder}
3569 * object.
3570 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003571 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003572 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003573
3574 /**
3575 * @hide
3576 */
3577 protected CharSequence mSummaryText = null;
3578
3579 /**
3580 * @hide
3581 */
3582 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003583
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003584 protected Builder mBuilder;
3585
Chris Wrend6297db2012-05-03 16:20:13 -04003586 /**
3587 * Overrides ContentTitle in the big form of the template.
3588 * This defaults to the value passed to setContentTitle().
3589 */
3590 protected void internalSetBigContentTitle(CharSequence title) {
3591 mBigContentTitle = title;
3592 }
3593
3594 /**
3595 * Set the first line of text after the detail section in the big form of the template.
3596 */
3597 protected void internalSetSummaryText(CharSequence cs) {
3598 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003599 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003600 }
3601
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003602 public void setBuilder(Builder builder) {
3603 if (mBuilder != builder) {
3604 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003605 if (mBuilder != null) {
3606 mBuilder.setStyle(this);
3607 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003608 }
3609 }
3610
Chris Wrend6297db2012-05-03 16:20:13 -04003611 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003612 if (mBuilder == null) {
3613 throw new IllegalArgumentException("Style requires a valid Builder object");
3614 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003615 }
Chris Wrend6297db2012-05-03 16:20:13 -04003616
3617 protected RemoteViews getStandardView(int layoutId) {
3618 checkBuilder();
3619
Christoph Studer4600f9b2014-07-22 22:44:43 +02003620 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003621 CharSequence oldBuilderContentTitle =
3622 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003623 if (mBigContentTitle != null) {
3624 mBuilder.setContentTitle(mBigContentTitle);
3625 }
3626
Chris Wrend6297db2012-05-03 16:20:13 -04003627 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3628
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003629 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003630
Chris Wrend6297db2012-05-03 16:20:13 -04003631 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3632 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003633 } else {
3634 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003635 }
3636
Daniel Sandler619738c2012-06-07 16:33:08 -04003637 // The last line defaults to the subtext, but can be replaced by mSummaryText
3638 final CharSequence overflowText =
3639 mSummaryTextSet ? mSummaryText
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003640 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT);
Daniel Sandler619738c2012-06-07 16:33:08 -04003641 if (overflowText != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003642 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
Daniel Sandler619738c2012-06-07 16:33:08 -04003643 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003644 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
Daniel Sandler916ad912012-06-13 12:17:07 -04003645 } else {
Kenny Guy98193ea2014-07-24 19:54:37 +01003646 // Clear text in case we use the line to show the profile badge.
3647 contentView.setTextViewText(R.id.text, "");
Daniel Sandler916ad912012-06-13 12:17:07 -04003648 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3649 contentView.setViewVisibility(R.id.line3, View.GONE);
Chris Wrend6297db2012-05-03 16:20:13 -04003650 }
3651
3652 return contentView;
3653 }
3654
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003655 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003656 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3657 * The default implementation has nothing additional to add.
3658 * @hide
3659 */
3660 public RemoteViews makeContentView() {
3661 return null;
3662 }
3663
3664 /**
3665 * Construct a Style-specific RemoteViews for the final big notification layout.
3666 * @hide
3667 */
3668 public RemoteViews makeBigContentView() {
3669 return null;
3670 }
3671
3672 /**
3673 * Construct a Style-specific RemoteViews for the final HUN layout.
3674 * @hide
3675 */
3676 public RemoteViews makeHeadsUpContentView() {
3677 return null;
3678 }
3679
3680 /**
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003681 * Changes the padding of the first line such that the big and small content view have the
3682 * same top padding.
3683 *
3684 * @hide
3685 */
3686 protected void applyTopPadding(RemoteViews contentView) {
3687 int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003688 mBuilder.hasThreeLines(),
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003689 mBuilder.mContext.getResources().getConfiguration().fontScale);
3690 contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
3691 }
3692
3693 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003694 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003695 * @hide
3696 */
3697 public void addExtras(Bundle extras) {
3698 if (mSummaryTextSet) {
3699 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3700 }
3701 if (mBigContentTitle != null) {
3702 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3703 }
Chris Wren91ad5632013-06-05 15:05:57 -04003704 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003705 }
3706
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003707 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003708 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003709 * @hide
3710 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003711 protected void restoreFromExtras(Bundle extras) {
3712 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3713 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3714 mSummaryTextSet = true;
3715 }
3716 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3717 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3718 }
3719 }
3720
3721
3722 /**
3723 * @hide
3724 */
3725 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003726 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003727 return wip;
3728 }
3729
Daniel Sandler0ec46202015-06-24 01:27:05 -04003730 /**
3731 * @hide
3732 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003733 public void purgeResources() {}
3734
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003735 /**
3736 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3737 * attached to.
3738 *
3739 * @return the fully constructed Notification.
3740 */
3741 public Notification build() {
3742 checkBuilder();
3743 return mBuilder.build();
3744 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003745
3746 /**
3747 * @hide
3748 * @return true if the style positions the progress bar on the second line; false if the
3749 * style hides the progress bar
3750 */
3751 protected boolean hasProgress() {
3752 return true;
3753 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003754 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003755
3756 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003757 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003758 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003759 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003760 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003761 * Notification notif = new Notification.Builder(mContext)
3762 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3763 * .setContentText(subject)
3764 * .setSmallIcon(R.drawable.new_post)
3765 * .setLargeIcon(aBitmap)
3766 * .setStyle(new Notification.BigPictureStyle()
3767 * .bigPicture(aBigBitmap))
3768 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003769 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003770 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003771 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003772 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003773 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003774 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003775 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003776 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003777
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003778 public BigPictureStyle() {
3779 }
3780
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003781 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003782 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003783 }
3784
Chris Wrend6297db2012-05-03 16:20:13 -04003785 /**
3786 * Overrides ContentTitle in the big form of the template.
3787 * This defaults to the value passed to setContentTitle().
3788 */
3789 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003790 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003791 return this;
3792 }
3793
3794 /**
3795 * Set the first line of text after the detail section in the big form of the template.
3796 */
3797 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003798 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003799 return this;
3800 }
3801
Chris Wren0bd664d2012-08-01 13:56:56 -04003802 /**
3803 * Provide the bitmap to be used as the payload for the BigPicture notification.
3804 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003805 public BigPictureStyle bigPicture(Bitmap b) {
3806 mPicture = b;
3807 return this;
3808 }
3809
Chris Wren3745a3d2012-05-22 15:11:52 -04003810 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003811 * Override the large icon when the big notification is shown.
3812 */
3813 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003814 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3815 }
3816
3817 /**
3818 * Override the large icon when the big notification is shown.
3819 */
3820 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003821 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003822 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003823 return this;
3824 }
3825
Daniel Sandler0ec46202015-06-24 01:27:05 -04003826 /**
3827 * @hide
3828 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003829 @Override
3830 public void purgeResources() {
3831 super.purgeResources();
3832 if (mPicture != null && mPicture.isMutable()) {
3833 mPicture = mPicture.createAshmemBitmap();
3834 }
3835 if (mBigLargeIcon != null) {
3836 mBigLargeIcon.convertToAshmem();
3837 }
3838 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003839
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003840 /**
3841 * @hide
3842 */
3843 public RemoteViews makeBigContentView() {
3844 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003845 // This covers the following cases:
3846 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003847 // mN.mLargeIcon
3848 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003849 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003850 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003851 oldLargeIcon = mBuilder.mN.mLargeIcon;
3852 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003853 }
3854
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003855 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003856
Christoph Studer5c510ee2014-12-15 16:32:27 +01003857 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003858 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003859 }
3860
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003861 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3862
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003863 applyTopPadding(contentView);
3864
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003865 boolean twoTextLines = mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT) != null
3866 && mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT) != null;
Kenny Guy98193ea2014-07-24 19:54:37 +01003867 mBuilder.addProfileBadge(contentView,
3868 twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003869 return contentView;
3870 }
3871
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003872 /**
3873 * @hide
3874 */
3875 public void addExtras(Bundle extras) {
3876 super.addExtras(extras);
3877
3878 if (mBigLargeIconSet) {
3879 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3880 }
3881 extras.putParcelable(EXTRA_PICTURE, mPicture);
3882 }
3883
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003884 /**
3885 * @hide
3886 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003887 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003888 protected void restoreFromExtras(Bundle extras) {
3889 super.restoreFromExtras(extras);
3890
3891 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003892 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003893 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003894 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003895 mPicture = extras.getParcelable(EXTRA_PICTURE);
3896 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003897 }
3898
3899 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003900 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003901 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003902 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003903 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003904 * Notification notif = new Notification.Builder(mContext)
3905 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3906 * .setContentText(subject)
3907 * .setSmallIcon(R.drawable.new_mail)
3908 * .setLargeIcon(aBitmap)
3909 * .setStyle(new Notification.BigTextStyle()
3910 * .bigText(aVeryLongString))
3911 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003912 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003913 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003914 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003915 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003916 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003917
3918 private static final int MAX_LINES = 13;
3919 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3920 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3921
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003922 private CharSequence mBigText;
3923
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003924 public BigTextStyle() {
3925 }
3926
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003927 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003928 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003929 }
3930
Chris Wrend6297db2012-05-03 16:20:13 -04003931 /**
3932 * Overrides ContentTitle in the big form of the template.
3933 * This defaults to the value passed to setContentTitle().
3934 */
3935 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003936 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003937 return this;
3938 }
3939
3940 /**
3941 * Set the first line of text after the detail section in the big form of the template.
3942 */
3943 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003944 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003945 return this;
3946 }
3947
Chris Wren0bd664d2012-08-01 13:56:56 -04003948 /**
3949 * Provide the longer text to be displayed in the big form of the
3950 * template in place of the content text.
3951 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003952 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003953 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003954 return this;
3955 }
3956
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003957 /**
3958 * @hide
3959 */
3960 public void addExtras(Bundle extras) {
3961 super.addExtras(extras);
3962
Christoph Studer4600f9b2014-07-22 22:44:43 +02003963 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3964 }
3965
3966 /**
3967 * @hide
3968 */
3969 @Override
3970 protected void restoreFromExtras(Bundle extras) {
3971 super.restoreFromExtras(extras);
3972
3973 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003974 }
3975
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003976 /**
3977 * @hide
3978 */
3979 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003980
3981 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003982 CharSequence oldBuilderContentText =
3983 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3984 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003985
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003986 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003987
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003988 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003989
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003990 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003991 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003992 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Chris Wren3c5f92432012-05-04 16:31:17 -04003993 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003994
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003995 applyTopPadding(contentView);
3996
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003997 mBuilder.shrinkLine3Text(contentView);
3998
Kenny Guy98193ea2014-07-24 19:54:37 +01003999 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4000
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004001 return contentView;
4002 }
4003
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004004 private int calculateMaxLines() {
4005 int lineCount = MAX_LINES;
4006 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004007 boolean hasSummary = (mSummaryTextSet ? mSummaryText
4008 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004009 if (hasActions) {
4010 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4011 }
4012 if (hasSummary) {
4013 lineCount -= LINES_CONSUMED_BY_SUMMARY;
4014 }
4015
4016 // If we have less top padding at the top, we can fit less lines.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004017 if (!mBuilder.hasThreeLines()) {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004018 lineCount--;
4019 }
4020 return lineCount;
4021 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004022 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004023
4024 /**
4025 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004026 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004027 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004028 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004029 * Notification notif = new Notification.Builder(mContext)
4030 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4031 * .setContentText(subject)
4032 * .setSmallIcon(R.drawable.new_mail)
4033 * .setLargeIcon(aBitmap)
4034 * .setStyle(new Notification.InboxStyle()
4035 * .addLine(str1)
4036 * .addLine(str2)
4037 * .setContentTitle(&quot;&quot;)
4038 * .setSummaryText(&quot;+3 more&quot;))
4039 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004040 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004041 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004042 * @see Notification#bigContentView
4043 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004044 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004045 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4046
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004047 public InboxStyle() {
4048 }
4049
Daniel Sandler879c5e02012-04-17 16:46:51 -04004050 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004051 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004052 }
4053
Chris Wrend6297db2012-05-03 16:20:13 -04004054 /**
4055 * Overrides ContentTitle in the big form of the template.
4056 * This defaults to the value passed to setContentTitle().
4057 */
4058 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004059 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004060 return this;
4061 }
4062
4063 /**
4064 * Set the first line of text after the detail section in the big form of the template.
4065 */
4066 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004067 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004068 return this;
4069 }
4070
Chris Wren0bd664d2012-08-01 13:56:56 -04004071 /**
4072 * Append a line to the digest section of the Inbox notification.
4073 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004074 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004075 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004076 return this;
4077 }
4078
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004079 /**
4080 * @hide
4081 */
4082 public void addExtras(Bundle extras) {
4083 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004084
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004085 CharSequence[] a = new CharSequence[mTexts.size()];
4086 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4087 }
4088
Christoph Studer4600f9b2014-07-22 22:44:43 +02004089 /**
4090 * @hide
4091 */
4092 @Override
4093 protected void restoreFromExtras(Bundle extras) {
4094 super.restoreFromExtras(extras);
4095
4096 mTexts.clear();
4097 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4098 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4099 }
4100 }
4101
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004102 /**
4103 * @hide
4104 */
4105 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004106 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004107 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004108 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4109 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004110
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004111 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004112
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004113 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004114
Chris Wrend6297db2012-05-03 16:20:13 -04004115 contentView.setViewVisibility(R.id.text2, View.GONE);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004116
Chris Wrend6297db2012-05-03 16:20:13 -04004117 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 -04004118 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004119
Chris Wren4ed80d52012-05-17 09:30:03 -04004120 // Make sure all rows are gone in case we reuse a view.
4121 for (int rowId : rowIds) {
4122 contentView.setViewVisibility(rowId, View.GONE);
4123 }
4124
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004125 final boolean largeText =
4126 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4127 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4128 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004129 int i=0;
4130 while (i < mTexts.size() && i < rowIds.length) {
4131 CharSequence str = mTexts.get(i);
4132 if (str != null && !str.equals("")) {
4133 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004134 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004135 if (largeText) {
4136 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4137 subTextSize);
4138 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004139 }
4140 i++;
4141 }
4142
Chris Wren683ab002012-09-20 10:35:54 -04004143 contentView.setViewVisibility(R.id.inbox_end_pad,
4144 mTexts.size() > 0 ? View.VISIBLE : View.GONE);
4145
4146 contentView.setViewVisibility(R.id.inbox_more,
4147 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
Chris Wren29bb6d92012-05-17 18:09:42 -04004148
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004149 applyTopPadding(contentView);
4150
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02004151 mBuilder.shrinkLine3Text(contentView);
4152
Kenny Guy98193ea2014-07-24 19:54:37 +01004153 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4154
Daniel Sandler879c5e02012-04-17 16:46:51 -04004155 return contentView;
4156 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004157 }
Dan Sandler842dd772014-05-15 09:36:47 -04004158
4159 /**
4160 * Notification style for media playback notifications.
4161 *
4162 * In the expanded form, {@link Notification#bigContentView}, up to 5
4163 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004164 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004165 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4166 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4167 * treated as album artwork.
4168 *
4169 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4170 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004171 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004172 * in the standard view alongside the usual content.
4173 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004174 * Notifications created with MediaStyle will have their category set to
4175 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4176 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4177 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004178 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4179 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004180 * the System UI can identify this as a notification representing an active media session
4181 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4182 *
4183 * To use this style with your Notification, feed it to
4184 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4185 * <pre class="prettyprint">
4186 * Notification noti = new Notification.Builder()
4187 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004188 * .setContentTitle(&quot;Track title&quot;)
4189 * .setContentText(&quot;Artist - Album&quot;)
4190 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004191 * .setStyle(<b>new Notification.MediaStyle()</b>
4192 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004193 * .build();
4194 * </pre>
4195 *
4196 * @see Notification#bigContentView
4197 */
4198 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004199 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004200 static final int MAX_MEDIA_BUTTONS = 5;
4201
4202 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004203 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004204
4205 public MediaStyle() {
4206 }
4207
4208 public MediaStyle(Builder builder) {
4209 setBuilder(builder);
4210 }
4211
4212 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004213 * 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 -04004214 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004215 *
4216 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004217 */
4218 public MediaStyle setShowActionsInCompactView(int...actions) {
4219 mActionsToShowInCompact = actions;
4220 return this;
4221 }
4222
4223 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004224 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4225 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004226 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004227 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004228 mToken = token;
4229 return this;
4230 }
4231
Christoph Studer4600f9b2014-07-22 22:44:43 +02004232 /**
4233 * @hide
4234 */
Dan Sandler842dd772014-05-15 09:36:47 -04004235 @Override
4236 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004237 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004238 if (wip.category == null) {
4239 wip.category = Notification.CATEGORY_TRANSPORT;
4240 }
Dan Sandler842dd772014-05-15 09:36:47 -04004241 return wip;
4242 }
4243
Christoph Studer4600f9b2014-07-22 22:44:43 +02004244 /**
4245 * @hide
4246 */
4247 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004248 public RemoteViews makeContentView() {
4249 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004250 }
4251
4252 /**
4253 * @hide
4254 */
4255 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004256 public RemoteViews makeBigContentView() {
4257 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004258 }
4259
Dan Sandler842dd772014-05-15 09:36:47 -04004260 /** @hide */
4261 @Override
4262 public void addExtras(Bundle extras) {
4263 super.addExtras(extras);
4264
4265 if (mToken != null) {
4266 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4267 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004268 if (mActionsToShowInCompact != null) {
4269 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4270 }
Dan Sandler842dd772014-05-15 09:36:47 -04004271 }
4272
Christoph Studer4600f9b2014-07-22 22:44:43 +02004273 /**
4274 * @hide
4275 */
4276 @Override
4277 protected void restoreFromExtras(Bundle extras) {
4278 super.restoreFromExtras(extras);
4279
4280 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4281 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4282 }
4283 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4284 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4285 }
4286 }
4287
Dan Sandler842dd772014-05-15 09:36:47 -04004288 private RemoteViews generateMediaActionButton(Action action) {
4289 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004290 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004291 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004292 button.setImageViewIcon(R.id.action0, action.getIcon());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004293 button.setDrawableParameters(R.id.action0, false, -1,
4294 0xFFFFFFFF,
4295 PorterDuff.Mode.SRC_ATOP, -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004296 if (!tombstone) {
4297 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4298 }
4299 button.setContentDescription(R.id.action0, action.title);
4300 return button;
4301 }
4302
4303 private RemoteViews makeMediaContentView() {
4304 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004305 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004306
4307 final int numActions = mBuilder.mActions.size();
4308 final int N = mActionsToShowInCompact == null
4309 ? 0
4310 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4311 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004312 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004313 for (int i = 0; i < N; i++) {
4314 if (i >= numActions) {
4315 throw new IllegalArgumentException(String.format(
4316 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4317 i, numActions - 1));
4318 }
4319
4320 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
4321 final RemoteViews button = generateMediaActionButton(action);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004322 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004323 }
4324 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004325 styleText(view);
4326 hideRightIcon(view);
Dan Sandler842dd772014-05-15 09:36:47 -04004327 return view;
4328 }
4329
4330 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004331 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
4332 RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount),
4333 false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004334
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004335 if (actionCount > 0) {
4336 big.removeAllViews(com.android.internal.R.id.media_actions);
4337 for (int i = 0; i < actionCount; i++) {
Dan Sandler842dd772014-05-15 09:36:47 -04004338 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004339 big.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004340 }
4341 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004342 styleText(big);
4343 hideRightIcon(big);
4344 applyTopPadding(big);
4345 big.setViewVisibility(android.R.id.progress, View.GONE);
Dan Sandler842dd772014-05-15 09:36:47 -04004346 return big;
4347 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004348
4349 private int getBigLayoutResource(int actionCount) {
4350 if (actionCount <= 3) {
4351 return R.layout.notification_template_material_big_media_narrow;
4352 } else {
4353 return R.layout.notification_template_material_big_media;
4354 }
4355 }
4356
4357 private void hideRightIcon(RemoteViews contentView) {
4358 contentView.setViewVisibility(R.id.right_icon, View.GONE);
4359 }
4360
4361 /**
4362 * Applies the special text colors for media notifications to all text views.
4363 */
4364 private void styleText(RemoteViews contentView) {
Alan Viverette4a357cd2015-03-18 18:37:18 -07004365 int primaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004366 R.color.notification_media_primary_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -07004367 int secondaryColor = mBuilder.mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004368 R.color.notification_media_secondary_color);
4369 contentView.setTextColor(R.id.title, primaryColor);
4370 if (mBuilder.showsTimeOrChronometer()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004371 if (mBuilder.getAllExtras().getBoolean(EXTRA_SHOW_CHRONOMETER)) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004372 contentView.setTextColor(R.id.chronometer, secondaryColor);
4373 } else {
4374 contentView.setTextColor(R.id.time, secondaryColor);
4375 }
4376 }
4377 contentView.setTextColor(R.id.text2, secondaryColor);
4378 contentView.setTextColor(R.id.text, secondaryColor);
4379 contentView.setTextColor(R.id.info, secondaryColor);
4380 }
4381
4382 /**
4383 * @hide
4384 */
4385 @Override
4386 protected boolean hasProgress() {
4387 return false;
4388 }
Dan Sandler842dd772014-05-15 09:36:47 -04004389 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004390
Christoph Studer4600f9b2014-07-22 22:44:43 +02004391 // When adding a new Style subclass here, don't forget to update
4392 // Builder.getNotificationStyleClass.
4393
Griff Hazen61a9e862014-05-22 16:05:19 -07004394 /**
4395 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4396 * metadata or change options on a notification builder.
4397 */
4398 public interface Extender {
4399 /**
4400 * Apply this extender to a notification builder.
4401 * @param builder the builder to be modified.
4402 * @return the build object for chaining.
4403 */
4404 public Builder extend(Builder builder);
4405 }
4406
4407 /**
4408 * Helper class to add wearable extensions to notifications.
4409 * <p class="note"> See
4410 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4411 * for Android Wear</a> for more information on how to use this class.
4412 * <p>
4413 * To create a notification with wearable extensions:
4414 * <ol>
4415 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4416 * properties.
4417 * <li>Create a {@link android.app.Notification.WearableExtender}.
4418 * <li>Set wearable-specific properties using the
4419 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4420 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4421 * notification.
4422 * <li>Post the notification to the notification system with the
4423 * {@code NotificationManager.notify(...)} methods.
4424 * </ol>
4425 *
4426 * <pre class="prettyprint">
4427 * Notification notif = new Notification.Builder(mContext)
4428 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4429 * .setContentText(subject)
4430 * .setSmallIcon(R.drawable.new_mail)
4431 * .extend(new Notification.WearableExtender()
4432 * .setContentIcon(R.drawable.new_mail))
4433 * .build();
4434 * NotificationManager notificationManger =
4435 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4436 * notificationManger.notify(0, notif);</pre>
4437 *
4438 * <p>Wearable extensions can be accessed on an existing notification by using the
4439 * {@code WearableExtender(Notification)} constructor,
4440 * and then using the {@code get} methods to access values.
4441 *
4442 * <pre class="prettyprint">
4443 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4444 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004445 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004446 */
4447 public static final class WearableExtender implements Extender {
4448 /**
4449 * Sentinel value for an action index that is unset.
4450 */
4451 public static final int UNSET_ACTION_INDEX = -1;
4452
4453 /**
4454 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4455 * default sizing.
4456 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004457 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004458 * on their content.
4459 */
4460 public static final int SIZE_DEFAULT = 0;
4461
4462 /**
4463 * Size value for use with {@link #setCustomSizePreset} to show this notification
4464 * with an extra small size.
4465 * <p>This value is only applicable for custom display notifications created using
4466 * {@link #setDisplayIntent}.
4467 */
4468 public static final int SIZE_XSMALL = 1;
4469
4470 /**
4471 * Size value for use with {@link #setCustomSizePreset} to show this notification
4472 * with a small size.
4473 * <p>This value is only applicable for custom display notifications created using
4474 * {@link #setDisplayIntent}.
4475 */
4476 public static final int SIZE_SMALL = 2;
4477
4478 /**
4479 * Size value for use with {@link #setCustomSizePreset} to show this notification
4480 * with a medium size.
4481 * <p>This value is only applicable for custom display notifications created using
4482 * {@link #setDisplayIntent}.
4483 */
4484 public static final int SIZE_MEDIUM = 3;
4485
4486 /**
4487 * Size value for use with {@link #setCustomSizePreset} to show this notification
4488 * with a large size.
4489 * <p>This value is only applicable for custom display notifications created using
4490 * {@link #setDisplayIntent}.
4491 */
4492 public static final int SIZE_LARGE = 4;
4493
Griff Hazend5f11f92014-05-27 15:40:09 -07004494 /**
4495 * Size value for use with {@link #setCustomSizePreset} to show this notification
4496 * full screen.
4497 * <p>This value is only applicable for custom display notifications created using
4498 * {@link #setDisplayIntent}.
4499 */
4500 public static final int SIZE_FULL_SCREEN = 5;
4501
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004502 /**
4503 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4504 * short amount of time when this notification is displayed on the screen. This
4505 * is the default value.
4506 */
4507 public static final int SCREEN_TIMEOUT_SHORT = 0;
4508
4509 /**
4510 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4511 * for a longer amount of time when this notification is displayed on the screen.
4512 */
4513 public static final int SCREEN_TIMEOUT_LONG = -1;
4514
Griff Hazen61a9e862014-05-22 16:05:19 -07004515 /** Notification extra which contains wearable extensions */
4516 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4517
Pete Gastaf6781d2014-10-07 15:17:05 -04004518 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004519 private static final String KEY_ACTIONS = "actions";
4520 private static final String KEY_FLAGS = "flags";
4521 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4522 private static final String KEY_PAGES = "pages";
4523 private static final String KEY_BACKGROUND = "background";
4524 private static final String KEY_CONTENT_ICON = "contentIcon";
4525 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4526 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4527 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4528 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4529 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004530 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004531
4532 // Flags bitwise-ored to mFlags
4533 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4534 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4535 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4536 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004537 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004538
4539 // Default value for flags integer
4540 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4541
4542 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4543 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4544
4545 private ArrayList<Action> mActions = new ArrayList<Action>();
4546 private int mFlags = DEFAULT_FLAGS;
4547 private PendingIntent mDisplayIntent;
4548 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4549 private Bitmap mBackground;
4550 private int mContentIcon;
4551 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4552 private int mContentActionIndex = UNSET_ACTION_INDEX;
4553 private int mCustomSizePreset = SIZE_DEFAULT;
4554 private int mCustomContentHeight;
4555 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004556 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004557
4558 /**
4559 * Create a {@link android.app.Notification.WearableExtender} with default
4560 * options.
4561 */
4562 public WearableExtender() {
4563 }
4564
4565 public WearableExtender(Notification notif) {
4566 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4567 if (wearableBundle != null) {
4568 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4569 if (actions != null) {
4570 mActions.addAll(actions);
4571 }
4572
4573 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4574 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4575
4576 Notification[] pages = getNotificationArrayFromBundle(
4577 wearableBundle, KEY_PAGES);
4578 if (pages != null) {
4579 Collections.addAll(mPages, pages);
4580 }
4581
4582 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4583 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4584 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4585 DEFAULT_CONTENT_ICON_GRAVITY);
4586 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4587 UNSET_ACTION_INDEX);
4588 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4589 SIZE_DEFAULT);
4590 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4591 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004592 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004593 }
4594 }
4595
4596 /**
4597 * Apply wearable extensions to a notification that is being built. This is typically
4598 * called by the {@link android.app.Notification.Builder#extend} method of
4599 * {@link android.app.Notification.Builder}.
4600 */
4601 @Override
4602 public Notification.Builder extend(Notification.Builder builder) {
4603 Bundle wearableBundle = new Bundle();
4604
4605 if (!mActions.isEmpty()) {
4606 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4607 }
4608 if (mFlags != DEFAULT_FLAGS) {
4609 wearableBundle.putInt(KEY_FLAGS, mFlags);
4610 }
4611 if (mDisplayIntent != null) {
4612 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4613 }
4614 if (!mPages.isEmpty()) {
4615 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4616 new Notification[mPages.size()]));
4617 }
4618 if (mBackground != null) {
4619 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4620 }
4621 if (mContentIcon != 0) {
4622 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4623 }
4624 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4625 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4626 }
4627 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4628 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4629 mContentActionIndex);
4630 }
4631 if (mCustomSizePreset != SIZE_DEFAULT) {
4632 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4633 }
4634 if (mCustomContentHeight != 0) {
4635 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4636 }
4637 if (mGravity != DEFAULT_GRAVITY) {
4638 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4639 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004640 if (mHintScreenTimeout != 0) {
4641 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4642 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004643
4644 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4645 return builder;
4646 }
4647
4648 @Override
4649 public WearableExtender clone() {
4650 WearableExtender that = new WearableExtender();
4651 that.mActions = new ArrayList<Action>(this.mActions);
4652 that.mFlags = this.mFlags;
4653 that.mDisplayIntent = this.mDisplayIntent;
4654 that.mPages = new ArrayList<Notification>(this.mPages);
4655 that.mBackground = this.mBackground;
4656 that.mContentIcon = this.mContentIcon;
4657 that.mContentIconGravity = this.mContentIconGravity;
4658 that.mContentActionIndex = this.mContentActionIndex;
4659 that.mCustomSizePreset = this.mCustomSizePreset;
4660 that.mCustomContentHeight = this.mCustomContentHeight;
4661 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004662 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004663 return that;
4664 }
4665
4666 /**
4667 * Add a wearable action to this notification.
4668 *
4669 * <p>When wearable actions are added using this method, the set of actions that
4670 * show on a wearable device splits from devices that only show actions added
4671 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4672 * of which actions display on different devices.
4673 *
4674 * @param action the action to add to this notification
4675 * @return this object for method chaining
4676 * @see android.app.Notification.Action
4677 */
4678 public WearableExtender addAction(Action action) {
4679 mActions.add(action);
4680 return this;
4681 }
4682
4683 /**
4684 * Adds wearable actions to this notification.
4685 *
4686 * <p>When wearable actions are added using this method, the set of actions that
4687 * show on a wearable device splits from devices that only show actions added
4688 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4689 * of which actions display on different devices.
4690 *
4691 * @param actions the actions to add to this notification
4692 * @return this object for method chaining
4693 * @see android.app.Notification.Action
4694 */
4695 public WearableExtender addActions(List<Action> actions) {
4696 mActions.addAll(actions);
4697 return this;
4698 }
4699
4700 /**
4701 * Clear all wearable actions present on this builder.
4702 * @return this object for method chaining.
4703 * @see #addAction
4704 */
4705 public WearableExtender clearActions() {
4706 mActions.clear();
4707 return this;
4708 }
4709
4710 /**
4711 * Get the wearable actions present on this notification.
4712 */
4713 public List<Action> getActions() {
4714 return mActions;
4715 }
4716
4717 /**
4718 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004719 * this notification. The {@link PendingIntent} provided should be for an activity.
4720 *
4721 * <pre class="prettyprint">
4722 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4723 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4724 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4725 * Notification notif = new Notification.Builder(context)
4726 * .extend(new Notification.WearableExtender()
4727 * .setDisplayIntent(displayPendingIntent)
4728 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4729 * .build();</pre>
4730 *
4731 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004732 * should have an empty task affinity. It is also recommended to use the device
4733 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004734 *
4735 * <p>Example AndroidManifest.xml entry:
4736 * <pre class="prettyprint">
4737 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4738 * android:exported=&quot;true&quot;
4739 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004740 * android:taskAffinity=&quot;&quot;
4741 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004742 *
4743 * @param intent the {@link PendingIntent} for an activity
4744 * @return this object for method chaining
4745 * @see android.app.Notification.WearableExtender#getDisplayIntent
4746 */
4747 public WearableExtender setDisplayIntent(PendingIntent intent) {
4748 mDisplayIntent = intent;
4749 return this;
4750 }
4751
4752 /**
4753 * Get the intent to launch inside of an activity view when displaying this
4754 * notification. This {@code PendingIntent} should be for an activity.
4755 */
4756 public PendingIntent getDisplayIntent() {
4757 return mDisplayIntent;
4758 }
4759
4760 /**
4761 * Add an additional page of content to display with this notification. The current
4762 * notification forms the first page, and pages added using this function form
4763 * subsequent pages. This field can be used to separate a notification into multiple
4764 * sections.
4765 *
4766 * @param page the notification to add as another page
4767 * @return this object for method chaining
4768 * @see android.app.Notification.WearableExtender#getPages
4769 */
4770 public WearableExtender addPage(Notification page) {
4771 mPages.add(page);
4772 return this;
4773 }
4774
4775 /**
4776 * Add additional pages of content to display with this notification. The current
4777 * notification forms the first page, and pages added using this function form
4778 * subsequent pages. This field can be used to separate a notification into multiple
4779 * sections.
4780 *
4781 * @param pages a list of notifications
4782 * @return this object for method chaining
4783 * @see android.app.Notification.WearableExtender#getPages
4784 */
4785 public WearableExtender addPages(List<Notification> pages) {
4786 mPages.addAll(pages);
4787 return this;
4788 }
4789
4790 /**
4791 * Clear all additional pages present on this builder.
4792 * @return this object for method chaining.
4793 * @see #addPage
4794 */
4795 public WearableExtender clearPages() {
4796 mPages.clear();
4797 return this;
4798 }
4799
4800 /**
4801 * Get the array of additional pages of content for displaying this notification. The
4802 * current notification forms the first page, and elements within this array form
4803 * subsequent pages. This field can be used to separate a notification into multiple
4804 * sections.
4805 * @return the pages for this notification
4806 */
4807 public List<Notification> getPages() {
4808 return mPages;
4809 }
4810
4811 /**
4812 * Set a background image to be displayed behind the notification content.
4813 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4814 * will work with any notification style.
4815 *
4816 * @param background the background bitmap
4817 * @return this object for method chaining
4818 * @see android.app.Notification.WearableExtender#getBackground
4819 */
4820 public WearableExtender setBackground(Bitmap background) {
4821 mBackground = background;
4822 return this;
4823 }
4824
4825 /**
4826 * Get a background image to be displayed behind the notification content.
4827 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4828 * will work with any notification style.
4829 *
4830 * @return the background image
4831 * @see android.app.Notification.WearableExtender#setBackground
4832 */
4833 public Bitmap getBackground() {
4834 return mBackground;
4835 }
4836
4837 /**
4838 * Set an icon that goes with the content of this notification.
4839 */
4840 public WearableExtender setContentIcon(int icon) {
4841 mContentIcon = icon;
4842 return this;
4843 }
4844
4845 /**
4846 * Get an icon that goes with the content of this notification.
4847 */
4848 public int getContentIcon() {
4849 return mContentIcon;
4850 }
4851
4852 /**
4853 * Set the gravity that the content icon should have within the notification display.
4854 * Supported values include {@link android.view.Gravity#START} and
4855 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4856 * @see #setContentIcon
4857 */
4858 public WearableExtender setContentIconGravity(int contentIconGravity) {
4859 mContentIconGravity = contentIconGravity;
4860 return this;
4861 }
4862
4863 /**
4864 * Get 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 #getContentIcon
4868 */
4869 public int getContentIconGravity() {
4870 return mContentIconGravity;
4871 }
4872
4873 /**
4874 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004875 * this notification. This action will no longer display separately from the
4876 * notification's content.
4877 *
Griff Hazenca48d352014-05-28 22:37:13 -07004878 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004879 * set, although the list of available actions comes from the main notification and not
4880 * from the child page's notification.
4881 *
4882 * @param actionIndex The index of the action to hoist onto the current notification page.
4883 * If wearable actions were added to the main notification, this index
4884 * will apply to that list, otherwise it will apply to the regular
4885 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004886 */
4887 public WearableExtender setContentAction(int actionIndex) {
4888 mContentActionIndex = actionIndex;
4889 return this;
4890 }
4891
4892 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004893 * Get the index of the notification action, if any, that was specified as being clickable
4894 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004895 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004896 *
Griff Hazenca48d352014-05-28 22:37:13 -07004897 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004898 * set, although the list of available actions comes from the main notification and not
4899 * from the child page's notification.
4900 *
4901 * <p>If wearable specific actions were added to the main notification, this index will
4902 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004903 *
4904 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004905 */
4906 public int getContentAction() {
4907 return mContentActionIndex;
4908 }
4909
4910 /**
4911 * Set the gravity that this notification should have within the available viewport space.
4912 * Supported values include {@link android.view.Gravity#TOP},
4913 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4914 * The default value is {@link android.view.Gravity#BOTTOM}.
4915 */
4916 public WearableExtender setGravity(int gravity) {
4917 mGravity = gravity;
4918 return this;
4919 }
4920
4921 /**
4922 * Get 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 int getGravity() {
4928 return mGravity;
4929 }
4930
4931 /**
4932 * Set the custom size preset for the display of this notification out of the available
4933 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4934 * {@link #SIZE_LARGE}.
4935 * <p>Some custom size presets are only applicable for custom display notifications created
4936 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4937 * documentation for the preset in question. See also
4938 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4939 */
4940 public WearableExtender setCustomSizePreset(int sizePreset) {
4941 mCustomSizePreset = sizePreset;
4942 return this;
4943 }
4944
4945 /**
4946 * Get the custom size preset for the display of this notification out of the available
4947 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4948 * {@link #SIZE_LARGE}.
4949 * <p>Some custom size presets are only applicable for custom display notifications created
4950 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4951 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4952 */
4953 public int getCustomSizePreset() {
4954 return mCustomSizePreset;
4955 }
4956
4957 /**
4958 * Set the custom height in pixels for the display of this notification's content.
4959 * <p>This option is only available for custom display notifications created
4960 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4961 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4962 * {@link #getCustomContentHeight}.
4963 */
4964 public WearableExtender setCustomContentHeight(int height) {
4965 mCustomContentHeight = height;
4966 return this;
4967 }
4968
4969 /**
4970 * Get the custom height in pixels for the display of this notification's content.
4971 * <p>This option is only available for custom display notifications created
4972 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4973 * {@link #setCustomContentHeight}.
4974 */
4975 public int getCustomContentHeight() {
4976 return mCustomContentHeight;
4977 }
4978
4979 /**
4980 * Set whether the scrolling position for the contents of this notification should start
4981 * at the bottom of the contents instead of the top when the contents are too long to
4982 * display within the screen. Default is false (start scroll at the top).
4983 */
4984 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4985 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4986 return this;
4987 }
4988
4989 /**
4990 * Get whether the scrolling position for the contents of this notification should start
4991 * at the bottom of the contents instead of the top when the contents are too long to
4992 * display within the screen. Default is false (start scroll at the top).
4993 */
4994 public boolean getStartScrollBottom() {
4995 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4996 }
4997
4998 /**
4999 * Set whether the content intent is available when the wearable device is not connected
5000 * to a companion device. The user can still trigger this intent when the wearable device
5001 * is offline, but a visual hint will indicate that the content intent may not be available.
5002 * Defaults to true.
5003 */
5004 public WearableExtender setContentIntentAvailableOffline(
5005 boolean contentIntentAvailableOffline) {
5006 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
5007 return this;
5008 }
5009
5010 /**
5011 * Get whether the content intent is available when the wearable device is not connected
5012 * to a companion device. The user can still trigger this intent when the wearable device
5013 * is offline, but a visual hint will indicate that the content intent may not be available.
5014 * Defaults to true.
5015 */
5016 public boolean getContentIntentAvailableOffline() {
5017 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
5018 }
5019
5020 /**
5021 * Set a hint that this notification's icon should not be displayed.
5022 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
5023 * @return this object for method chaining
5024 */
5025 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
5026 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
5027 return this;
5028 }
5029
5030 /**
5031 * Get a hint that this notification's icon should not be displayed.
5032 * @return {@code true} if this icon should not be displayed, false otherwise.
5033 * The default value is {@code false} if this was never set.
5034 */
5035 public boolean getHintHideIcon() {
5036 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
5037 }
5038
5039 /**
5040 * Set a visual hint that only the background image of this notification should be
5041 * displayed, and other semantic content should be hidden. This hint is only applicable
5042 * to sub-pages added using {@link #addPage}.
5043 */
5044 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
5045 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
5046 return this;
5047 }
5048
5049 /**
5050 * Get a visual hint that only the background image of this notification should be
5051 * displayed, and other semantic content should be hidden. This hint is only applicable
5052 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
5053 */
5054 public boolean getHintShowBackgroundOnly() {
5055 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
5056 }
5057
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005058 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005059 * Set a hint that this notification's background should not be clipped if possible,
5060 * and should instead be resized to fully display on the screen, retaining the aspect
5061 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005062 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
5063 * @return this object for method chaining
5064 */
5065 public WearableExtender setHintAvoidBackgroundClipping(
5066 boolean hintAvoidBackgroundClipping) {
5067 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
5068 return this;
5069 }
5070
5071 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005072 * Get a hint that this notification's background should not be clipped if possible,
5073 * and should instead be resized to fully display on the screen, retaining the aspect
5074 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005075 * @return {@code true} if it's ok if the background is clipped on the screen, false
5076 * otherwise. The default value is {@code false} if this was never set.
5077 */
5078 public boolean getHintAvoidBackgroundClipping() {
5079 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5080 }
5081
5082 /**
5083 * Set a hint that the screen should remain on for at least this duration when
5084 * this notification is displayed on the screen.
5085 * @param timeout The requested screen timeout in milliseconds. Can also be either
5086 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5087 * @return this object for method chaining
5088 */
5089 public WearableExtender setHintScreenTimeout(int timeout) {
5090 mHintScreenTimeout = timeout;
5091 return this;
5092 }
5093
5094 /**
5095 * Get the duration, in milliseconds, that the screen should remain on for
5096 * when this notification is displayed.
5097 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5098 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5099 */
5100 public int getHintScreenTimeout() {
5101 return mHintScreenTimeout;
5102 }
5103
Griff Hazen61a9e862014-05-22 16:05:19 -07005104 private void setFlag(int mask, boolean value) {
5105 if (value) {
5106 mFlags |= mask;
5107 } else {
5108 mFlags &= ~mask;
5109 }
5110 }
5111 }
5112
5113 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005114 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5115 * with car extensions:
5116 *
5117 * <ol>
5118 * <li>Create an {@link Notification.Builder}, setting any desired
5119 * properties.
5120 * <li>Create a {@link CarExtender}.
5121 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5122 * {@link CarExtender}.
5123 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5124 * to apply the extensions to a notification.
5125 * </ol>
5126 *
5127 * <pre class="prettyprint">
5128 * Notification notification = new Notification.Builder(context)
5129 * ...
5130 * .extend(new CarExtender()
5131 * .set*(...))
5132 * .build();
5133 * </pre>
5134 *
5135 * <p>Car extensions can be accessed on an existing notification by using the
5136 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5137 * to access values.
5138 */
5139 public static final class CarExtender implements Extender {
5140 private static final String TAG = "CarExtender";
5141
5142 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5143 private static final String EXTRA_LARGE_ICON = "large_icon";
5144 private static final String EXTRA_CONVERSATION = "car_conversation";
5145 private static final String EXTRA_COLOR = "app_color";
5146
5147 private Bitmap mLargeIcon;
5148 private UnreadConversation mUnreadConversation;
5149 private int mColor = Notification.COLOR_DEFAULT;
5150
5151 /**
5152 * Create a {@link CarExtender} with default options.
5153 */
5154 public CarExtender() {
5155 }
5156
5157 /**
5158 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5159 *
5160 * @param notif The notification from which to copy options.
5161 */
5162 public CarExtender(Notification notif) {
5163 Bundle carBundle = notif.extras == null ?
5164 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5165 if (carBundle != null) {
5166 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5167 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5168
5169 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5170 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5171 }
5172 }
5173
5174 /**
5175 * Apply car extensions to a notification that is being built. This is typically called by
5176 * the {@link Notification.Builder#extend(Notification.Extender)}
5177 * method of {@link Notification.Builder}.
5178 */
5179 @Override
5180 public Notification.Builder extend(Notification.Builder builder) {
5181 Bundle carExtensions = new Bundle();
5182
5183 if (mLargeIcon != null) {
5184 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5185 }
5186 if (mColor != Notification.COLOR_DEFAULT) {
5187 carExtensions.putInt(EXTRA_COLOR, mColor);
5188 }
5189
5190 if (mUnreadConversation != null) {
5191 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5192 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5193 }
5194
5195 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5196 return builder;
5197 }
5198
5199 /**
5200 * Sets the accent color to use when Android Auto presents the notification.
5201 *
5202 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5203 * to accent the displayed notification. However, not all colors are acceptable in an
5204 * automotive setting. This method can be used to override the color provided in the
5205 * notification in such a situation.
5206 */
Tor Norbye80756e32015-03-02 09:39:27 -08005207 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005208 mColor = color;
5209 return this;
5210 }
5211
5212 /**
5213 * Gets the accent color.
5214 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005215 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005216 */
Tor Norbye80756e32015-03-02 09:39:27 -08005217 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005218 public int getColor() {
5219 return mColor;
5220 }
5221
5222 /**
5223 * Sets the large icon of the car notification.
5224 *
5225 * If no large icon is set in the extender, Android Auto will display the icon
5226 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5227 *
5228 * @param largeIcon The large icon to use in the car notification.
5229 * @return This object for method chaining.
5230 */
5231 public CarExtender setLargeIcon(Bitmap largeIcon) {
5232 mLargeIcon = largeIcon;
5233 return this;
5234 }
5235
5236 /**
5237 * Gets the large icon used in this car notification, or null if no icon has been set.
5238 *
5239 * @return The large icon for the car notification.
5240 * @see CarExtender#setLargeIcon
5241 */
5242 public Bitmap getLargeIcon() {
5243 return mLargeIcon;
5244 }
5245
5246 /**
5247 * Sets the unread conversation in a message notification.
5248 *
5249 * @param unreadConversation The unread part of the conversation this notification conveys.
5250 * @return This object for method chaining.
5251 */
5252 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5253 mUnreadConversation = unreadConversation;
5254 return this;
5255 }
5256
5257 /**
5258 * Returns the unread conversation conveyed by this notification.
5259 * @see #setUnreadConversation(UnreadConversation)
5260 */
5261 public UnreadConversation getUnreadConversation() {
5262 return mUnreadConversation;
5263 }
5264
5265 /**
5266 * A class which holds the unread messages from a conversation.
5267 */
5268 public static class UnreadConversation {
5269 private static final String KEY_AUTHOR = "author";
5270 private static final String KEY_TEXT = "text";
5271 private static final String KEY_MESSAGES = "messages";
5272 private static final String KEY_REMOTE_INPUT = "remote_input";
5273 private static final String KEY_ON_REPLY = "on_reply";
5274 private static final String KEY_ON_READ = "on_read";
5275 private static final String KEY_PARTICIPANTS = "participants";
5276 private static final String KEY_TIMESTAMP = "timestamp";
5277
5278 private final String[] mMessages;
5279 private final RemoteInput mRemoteInput;
5280 private final PendingIntent mReplyPendingIntent;
5281 private final PendingIntent mReadPendingIntent;
5282 private final String[] mParticipants;
5283 private final long mLatestTimestamp;
5284
5285 UnreadConversation(String[] messages, RemoteInput remoteInput,
5286 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5287 String[] participants, long latestTimestamp) {
5288 mMessages = messages;
5289 mRemoteInput = remoteInput;
5290 mReadPendingIntent = readPendingIntent;
5291 mReplyPendingIntent = replyPendingIntent;
5292 mParticipants = participants;
5293 mLatestTimestamp = latestTimestamp;
5294 }
5295
5296 /**
5297 * Gets the list of messages conveyed by this notification.
5298 */
5299 public String[] getMessages() {
5300 return mMessages;
5301 }
5302
5303 /**
5304 * Gets the remote input that will be used to convey the response to a message list, or
5305 * null if no such remote input exists.
5306 */
5307 public RemoteInput getRemoteInput() {
5308 return mRemoteInput;
5309 }
5310
5311 /**
5312 * Gets the pending intent that will be triggered when the user replies to this
5313 * notification.
5314 */
5315 public PendingIntent getReplyPendingIntent() {
5316 return mReplyPendingIntent;
5317 }
5318
5319 /**
5320 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5321 * in this object's message list.
5322 */
5323 public PendingIntent getReadPendingIntent() {
5324 return mReadPendingIntent;
5325 }
5326
5327 /**
5328 * Gets the participants in the conversation.
5329 */
5330 public String[] getParticipants() {
5331 return mParticipants;
5332 }
5333
5334 /**
5335 * Gets the firs participant in the conversation.
5336 */
5337 public String getParticipant() {
5338 return mParticipants.length > 0 ? mParticipants[0] : null;
5339 }
5340
5341 /**
5342 * Gets the timestamp of the conversation.
5343 */
5344 public long getLatestTimestamp() {
5345 return mLatestTimestamp;
5346 }
5347
5348 Bundle getBundleForUnreadConversation() {
5349 Bundle b = new Bundle();
5350 String author = null;
5351 if (mParticipants != null && mParticipants.length > 1) {
5352 author = mParticipants[0];
5353 }
5354 Parcelable[] messages = new Parcelable[mMessages.length];
5355 for (int i = 0; i < messages.length; i++) {
5356 Bundle m = new Bundle();
5357 m.putString(KEY_TEXT, mMessages[i]);
5358 m.putString(KEY_AUTHOR, author);
5359 messages[i] = m;
5360 }
5361 b.putParcelableArray(KEY_MESSAGES, messages);
5362 if (mRemoteInput != null) {
5363 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5364 }
5365 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5366 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5367 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5368 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5369 return b;
5370 }
5371
5372 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5373 if (b == null) {
5374 return null;
5375 }
5376 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5377 String[] messages = null;
5378 if (parcelableMessages != null) {
5379 String[] tmp = new String[parcelableMessages.length];
5380 boolean success = true;
5381 for (int i = 0; i < tmp.length; i++) {
5382 if (!(parcelableMessages[i] instanceof Bundle)) {
5383 success = false;
5384 break;
5385 }
5386 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5387 if (tmp[i] == null) {
5388 success = false;
5389 break;
5390 }
5391 }
5392 if (success) {
5393 messages = tmp;
5394 } else {
5395 return null;
5396 }
5397 }
5398
5399 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5400 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5401
5402 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5403
5404 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5405 if (participants == null || participants.length != 1) {
5406 return null;
5407 }
5408
5409 return new UnreadConversation(messages,
5410 remoteInput,
5411 onReply,
5412 onRead,
5413 participants, b.getLong(KEY_TIMESTAMP));
5414 }
5415 };
5416
5417 /**
5418 * Builder class for {@link CarExtender.UnreadConversation} objects.
5419 */
5420 public static class Builder {
5421 private final List<String> mMessages = new ArrayList<String>();
5422 private final String mParticipant;
5423 private RemoteInput mRemoteInput;
5424 private PendingIntent mReadPendingIntent;
5425 private PendingIntent mReplyPendingIntent;
5426 private long mLatestTimestamp;
5427
5428 /**
5429 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5430 *
5431 * @param name The name of the other participant in the conversation.
5432 */
5433 public Builder(String name) {
5434 mParticipant = name;
5435 }
5436
5437 /**
5438 * Appends a new unread message to the list of messages for this conversation.
5439 *
5440 * The messages should be added from oldest to newest.
5441 *
5442 * @param message The text of the new unread message.
5443 * @return This object for method chaining.
5444 */
5445 public Builder addMessage(String message) {
5446 mMessages.add(message);
5447 return this;
5448 }
5449
5450 /**
5451 * Sets the pending intent and remote input which will convey the reply to this
5452 * notification.
5453 *
5454 * @param pendingIntent The pending intent which will be triggered on a reply.
5455 * @param remoteInput The remote input parcelable which will carry the reply.
5456 * @return This object for method chaining.
5457 *
5458 * @see CarExtender.UnreadConversation#getRemoteInput
5459 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5460 */
5461 public Builder setReplyAction(
5462 PendingIntent pendingIntent, RemoteInput remoteInput) {
5463 mRemoteInput = remoteInput;
5464 mReplyPendingIntent = pendingIntent;
5465
5466 return this;
5467 }
5468
5469 /**
5470 * Sets the pending intent that will be sent once the messages in this notification
5471 * are read.
5472 *
5473 * @param pendingIntent The pending intent to use.
5474 * @return This object for method chaining.
5475 */
5476 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5477 mReadPendingIntent = pendingIntent;
5478 return this;
5479 }
5480
5481 /**
5482 * Sets the timestamp of the most recent message in an unread conversation.
5483 *
5484 * If a messaging notification has been posted by your application and has not
5485 * yet been cancelled, posting a later notification with the same id and tag
5486 * but without a newer timestamp may result in Android Auto not displaying a
5487 * heads up notification for the later notification.
5488 *
5489 * @param timestamp The timestamp of the most recent message in the conversation.
5490 * @return This object for method chaining.
5491 */
5492 public Builder setLatestTimestamp(long timestamp) {
5493 mLatestTimestamp = timestamp;
5494 return this;
5495 }
5496
5497 /**
5498 * Builds a new unread conversation object.
5499 *
5500 * @return The new unread conversation object.
5501 */
5502 public UnreadConversation build() {
5503 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5504 String[] participants = { mParticipant };
5505 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5506 mReadPendingIntent, participants, mLatestTimestamp);
5507 }
5508 }
5509 }
5510
5511 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005512 * Get an array of Notification objects from a parcelable array bundle field.
5513 * Update the bundle to have a typed array so fetches in the future don't need
5514 * to do an array copy.
5515 */
5516 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5517 Parcelable[] array = bundle.getParcelableArray(key);
5518 if (array instanceof Notification[] || array == null) {
5519 return (Notification[]) array;
5520 }
5521 Notification[] typedArray = Arrays.copyOf(array, array.length,
5522 Notification[].class);
5523 bundle.putParcelableArray(key, typedArray);
5524 return typedArray;
5525 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005526
5527 private static class BuilderRemoteViews extends RemoteViews {
5528 public BuilderRemoteViews(Parcel parcel) {
5529 super(parcel);
5530 }
5531
Kenny Guy77320062014-08-27 21:37:15 +01005532 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5533 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005534 }
5535
5536 @Override
5537 public BuilderRemoteViews clone() {
5538 Parcel p = Parcel.obtain();
5539 writeToParcel(p, 0);
5540 p.setDataPosition(0);
5541 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5542 p.recycle();
5543 return brv;
5544 }
5545 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005546}