blob: c008c1f8b84b92cb8125f9ef0982e64588c5136a [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Tor Norbye7b9c9122013-05-30 16:48:33 -070020import android.annotation.DrawableRes;
Tor Norbyed9273d62013-05-30 15:59:53 -070021import android.annotation.IntDef;
Daniel Sandler01df1c62014-06-09 10:54:01 -040022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
Julia Reynolds233a5f92015-10-19 13:51:23 -040024import android.annotation.SystemApi;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
26import android.content.Intent;
Kenny Guy77320062014-08-27 21:37:15 +010027import android.content.pm.ApplicationInfo;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070028import android.content.pm.PackageManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020029import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020030import android.content.res.ColorStateList;
Joe Onoratoef1e7762010-09-17 18:38:38 -040031import android.graphics.Bitmap;
Kenny Guy8a0101b2014-05-08 23:34:12 +010032import android.graphics.Canvas;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010033import android.graphics.PorterDuff;
Kenny Guy8a0101b2014-05-08 23:34:12 +010034import android.graphics.drawable.Drawable;
Dan Sandlerd63f9322015-05-06 15:18:49 -040035import android.graphics.drawable.Icon;
John Spurlockc0650f022014-07-19 13:22:39 -040036import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070037import android.media.AudioManager;
Jeff Browndba34ba2014-06-24 20:46:03 -070038import android.media.session.MediaSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.net.Uri;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040040import android.os.BadParcelableException;
Christoph Studer239f8352014-08-25 15:13:18 +020041import android.os.Build;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050042import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.Parcel;
44import android.os.Parcelable;
Daniel Sandlera2985ed2012-04-03 16:42:00 -040045import android.os.SystemClock;
Jeff Sharkey6d515712012-09-20 16:06:08 -070046import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.text.TextUtils;
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040048import android.util.Log;
Dan Sandler50128532015-12-08 15:42:41 -050049import android.util.SparseArray;
Daniel Sandler9f7936a2012-05-21 16:14:28 -040050import android.util.TypedValue;
Griff Hazen61a9e862014-05-22 16:05:19 -070051import android.view.Gravity;
Selim Cinekea4bef72015-12-02 15:51:10 -080052import android.view.NotificationHeaderView;
Joe Onorato8595a3d2010-11-19 18:12:07 -080053import android.view.View;
Jeff Sharkey1c400132011-08-05 14:50:13 -070054import android.widget.ProgressBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.widget.RemoteViews;
56
Griff Hazen959591e2014-05-15 22:26:18 -070057import com.android.internal.R;
Griff Hazenc091ba82014-05-16 10:13:26 -070058import com.android.internal.util.NotificationColorUtil;
Griff Hazen959591e2014-05-15 22:26:18 -070059
Tor Norbyed9273d62013-05-30 15:59:53 -070060import java.lang.annotation.Retention;
61import java.lang.annotation.RetentionPolicy;
Christoph Studer4600f9b2014-07-22 22:44:43 +020062import java.lang.reflect.Constructor;
Daniel Sandler2561b0b2012-02-13 21:04:12 -050063import java.util.ArrayList;
Griff Hazen61a9e862014-05-22 16:05:19 -070064import java.util.Arrays;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070065import java.util.Collections;
Griff Hazen61a9e862014-05-22 16:05:19 -070066import java.util.List;
Julia Reynolds74303cf2015-10-16 11:37:55 -040067import java.util.Objects;
Dan Sandler50128532015-12-08 15:42:41 -050068import java.util.Set;
Joe Onorato561d3852010-11-20 18:09:34 -080069
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070/**
71 * A class that represents how a persistent notification is to be presented to
72 * the user using the {@link android.app.NotificationManager}.
73 *
Joe Onoratocb109a02011-01-18 17:57:41 -080074 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
75 * easier to construct Notifications.</p>
76 *
Joe Fernandez558459f2011-10-13 16:47:36 -070077 * <div class="special reference">
78 * <h3>Developer Guides</h3>
79 * <p>For a guide to creating notifications, read the
80 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
81 * developer guide.</p>
82 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 */
84public class Notification implements Parcelable
85{
Daniel Sandlerdcbaf662013-04-26 16:23:09 -040086 private static final String TAG = "Notification";
87
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 /**
Daniel Sandler01df1c62014-06-09 10:54:01 -040089 * An activity that provides a user interface for adjusting notification preferences for its
90 * containing application. Optional but recommended for apps that post
91 * {@link android.app.Notification Notifications}.
92 */
93 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
94 public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
95 = "android.intent.category.NOTIFICATION_PREFERENCES";
96
97 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 * Use all default values (where applicable).
99 */
100 public static final int DEFAULT_ALL = ~0;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 /**
103 * Use the default notification sound. This will ignore any given
104 * {@link #sound}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500105 *
Chris Wren47c20a12014-06-18 17:27:29 -0400106 * <p>
107 * A notification that is noisy is more likely to be presented as a heads-up notification.
108 * </p>
109 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500111 */
112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 public static final int DEFAULT_SOUND = 1;
114
115 /**
116 * Use the default notification vibrate. This will ignore any given
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500117 * {@link #vibrate}. Using phone vibration requires the
Scott Mainb8b36452009-04-26 15:50:49 -0700118 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500119 *
Chris Wren47c20a12014-06-18 17:27:29 -0400120 * <p>
121 * A notification that vibrates is more likely to be presented as a heads-up notification.
122 * </p>
123 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500125 */
126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 public static final int DEFAULT_VIBRATE = 2;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 /**
130 * Use the default notification lights. This will ignore the
131 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
132 * {@link #ledOnMS}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500133 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 * @see #defaults
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500135 */
136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 public static final int DEFAULT_LIGHTS = 4;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 /**
Christoph Studer535ec612014-09-03 15:47:47 +0200140 * Maximum length of CharSequences accepted by Builder and friends.
141 *
142 * <p>
143 * Avoids spamming the system with overly large strings such as full e-mails.
144 */
145 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
146
147 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500148 * A timestamp related to this notification, in milliseconds since the epoch.
Joe Malin8d40d042012-11-05 11:36:40 -0800149 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500150 * Default value: {@link System#currentTimeMillis() Now}.
151 *
152 * Choose a timestamp that will be most relevant to the user. For most finite events, this
153 * corresponds to the time the event happened (or will happen, in the case of events that have
154 * yet to occur but about which the user is being informed). Indefinite events should be
Joe Malin8d40d042012-11-05 11:36:40 -0800155 * timestamped according to when the activity began.
156 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500157 * Some examples:
Joe Malin8d40d042012-11-05 11:36:40 -0800158 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500159 * <ul>
160 * <li>Notification of a new chat message should be stamped when the message was received.</li>
161 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
162 * <li>Notification of a completed file download should be stamped when the download finished.</li>
163 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
164 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
165 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
Joe Malin8d40d042012-11-05 11:36:40 -0800166 * </ul>
167 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 */
169 public long when;
170
171 /**
172 * The resource id of a drawable to use as the icon in the status bar.
Dan Sandler86647982015-05-13 23:41:13 -0400173 *
174 * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 */
Dan Sandler86647982015-05-13 23:41:13 -0400176 @Deprecated
Tor Norbye7b9c9122013-05-30 16:48:33 -0700177 @DrawableRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 public int icon;
179
180 /**
Joe Onorato46439ce2010-11-19 13:56:21 -0800181 * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
182 * leave it at its default value of 0.
183 *
184 * @see android.widget.ImageView#setImageLevel
Griff Hazen959591e2014-05-15 22:26:18 -0700185 * @see android.graphics.drawable.Drawable#setLevel
Joe Onorato46439ce2010-11-19 13:56:21 -0800186 */
187 public int iconLevel;
188
189 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500190 * The number of events that this notification represents. For example, in a new mail
191 * notification, this could be the number of unread messages.
Joe Malin8d40d042012-11-05 11:36:40 -0800192 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500193 * The system may or may not use this field to modify the appearance of the notification. For
194 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
195 * superimposed over the icon in the status bar. Starting with
196 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
197 * {@link Notification.Builder} has displayed the number in the expanded notification view.
Joe Malin8d40d042012-11-05 11:36:40 -0800198 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500199 * If the number is 0 or negative, it is never shown.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 */
201 public int number;
202
203 /**
204 * The intent to execute when the expanded status entry is clicked. If
205 * this is an activity, it must include the
206 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -0800207 * that you take care of task management as described in the
208 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
Dianne Hackborn6ceca582012-01-10 15:24:26 -0800209 * Stack</a> document. In particular, make sure to read the notification section
210 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
211 * Notifications</a> for the correct ways to launch an application from a
212 * notification.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 */
214 public PendingIntent contentIntent;
215
216 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500217 * The intent to execute when the notification is explicitly dismissed by the user, either with
218 * the "Clear All" button or by swiping it away individually.
219 *
220 * This probably shouldn't be launching an activity since several of those will be sent
221 * at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 */
223 public PendingIntent deleteIntent;
224
225 /**
Dianne Hackborn170bae72010-09-03 15:14:28 -0700226 * An intent to launch instead of posting the notification to the status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -0800227 *
Chris Wren47c20a12014-06-18 17:27:29 -0400228 * <p>
229 * The system UI may choose to display a heads-up notification, instead of
230 * launching this intent, while the user is using the device.
231 * </p>
232 *
Joe Onoratocb109a02011-01-18 17:57:41 -0800233 * @see Notification.Builder#setFullScreenIntent
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400234 */
235 public PendingIntent fullScreenIntent;
236
237 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400238 * Text that summarizes this notification for accessibility services.
239 *
240 * As of the L release, this text is no longer shown on screen, but it is still useful to
241 * accessibility services (where it serves as an audible announcement of the notification's
242 * appearance).
Joe Onoratoef1e7762010-09-17 18:38:38 -0400243 *
Joe Onorato46439ce2010-11-19 13:56:21 -0800244 * @see #tickerView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 */
246 public CharSequence tickerText;
247
248 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400249 * Formerly, a view showing the {@link #tickerText}.
250 *
251 * No longer displayed in the status bar as of API 21.
Joe Onoratoef1e7762010-09-17 18:38:38 -0400252 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -0400253 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800254 public RemoteViews tickerView;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400255
256 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400257 * The view that will represent this notification in the notification list (which is pulled
258 * down from the status bar).
259 *
260 * As of N, this field is not used. The notification view is determined by the inputs to
261 * {@link Notification.Builder}; a custom RemoteViews can optionally be
262 * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400264 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 public RemoteViews contentView;
266
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400267 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -0400268 * A large-format version of {@link #contentView}, giving the Notification an
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400269 * opportunity to show more detail. The system UI may choose to show this
270 * instead of the normal content view at its discretion.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400271 *
272 * As of N, this field is not used. The expanded notification view is determined by the
273 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
274 * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400275 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400276 @Deprecated
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400277 public RemoteViews bigContentView;
278
Chris Wren8fd39ec2014-02-27 17:43:26 -0500279
280 /**
Chris Wren47c20a12014-06-18 17:27:29 -0400281 * A medium-format version of {@link #contentView}, providing the Notification an
282 * opportunity to add action buttons to contentView. At its discretion, the system UI may
283 * choose to show this as a heads-up notification, which will pop up so the user can see
284 * it without leaving their current activity.
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400285 *
286 * As of N, this field is not used. The heads-up notification view is determined by the
287 * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
288 * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
Chris Wren8fd39ec2014-02-27 17:43:26 -0500289 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400290 @Deprecated
Chris Wren8fd39ec2014-02-27 17:43:26 -0500291 public RemoteViews headsUpContentView;
292
Daniel Sandlerf3b73432012-03-27 15:01:25 -0400293 /**
Dan Sandler86647982015-05-13 23:41:13 -0400294 * A large bitmap to be shown in the notification content area.
295 *
296 * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 */
Dan Sandler86647982015-05-13 23:41:13 -0400298 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -0800299 public Bitmap largeIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300
301 /**
302 * The sound to play.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500303 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 * <p>
Chris Wren47c20a12014-06-18 17:27:29 -0400305 * A notification that is noisy is more likely to be presented as a heads-up notification.
306 * </p>
307 *
308 * <p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500309 * To play the default notification sound, see {@link #defaults}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 * </p>
311 */
312 public Uri sound;
313
314 /**
315 * Use this constant as the value for audioStreamType to request that
316 * the default stream type for notifications be used. Currently the
Jeff Sharkey098d5802012-04-26 17:30:34 -0700317 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
John Spurlockc0650f022014-07-19 13:22:39 -0400318 *
319 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700321 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 public static final int STREAM_DEFAULT = -1;
323
324 /**
325 * The audio stream type to use when playing the sound.
326 * Should be one of the STREAM_ constants from
327 * {@link android.media.AudioManager}.
John Spurlockc0650f022014-07-19 13:22:39 -0400328 *
329 * @deprecated Use {@link #audioAttributes} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700331 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 public int audioStreamType = STREAM_DEFAULT;
333
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 /**
John Spurlockc0650f022014-07-19 13:22:39 -0400335 * The default value of {@link #audioAttributes}.
336 */
337 public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
338 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
339 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
340 .build();
341
342 /**
343 * The {@link AudioAttributes audio attributes} to use when playing the sound.
344 */
345 public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
346
347 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500348 * The pattern with which to vibrate.
349 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 * <p>
351 * To vibrate the default pattern, see {@link #defaults}.
352 * </p>
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500353 *
Chris Wren47c20a12014-06-18 17:27:29 -0400354 * <p>
355 * A notification that vibrates is more likely to be presented as a heads-up notification.
356 * </p>
357 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 * @see android.os.Vibrator#vibrate(long[],int)
359 */
360 public long[] vibrate;
361
362 /**
363 * The color of the led. The hardware will do its best approximation.
364 *
365 * @see #FLAG_SHOW_LIGHTS
366 * @see #flags
367 */
Tor Norbye80756e32015-03-02 09:39:27 -0800368 @ColorInt
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 public int ledARGB;
370
371 /**
372 * The number of milliseconds for the LED to be on while it's flashing.
373 * The hardware will do its best approximation.
374 *
375 * @see #FLAG_SHOW_LIGHTS
376 * @see #flags
377 */
378 public int ledOnMS;
379
380 /**
381 * The number of milliseconds for the LED to be off while it's flashing.
382 * The hardware will do its best approximation.
383 *
384 * @see #FLAG_SHOW_LIGHTS
385 * @see #flags
386 */
387 public int ledOffMS;
388
389 /**
390 * Specifies which values should be taken from the defaults.
391 * <p>
392 * To set, OR the desired from {@link #DEFAULT_SOUND},
393 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
394 * values, use {@link #DEFAULT_ALL}.
395 * </p>
396 */
397 public int defaults;
398
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 /**
400 * Bit to be bitwise-ored into the {@link #flags} field that should be
401 * set if you want the LED on for this notification.
402 * <ul>
403 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
404 * or 0 for both ledOnMS and ledOffMS.</li>
405 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
406 * <li>To flash the LED, pass the number of milliseconds that it should
407 * be on and off to ledOnMS and ledOffMS.</li>
408 * </ul>
409 * <p>
410 * Since hardware varies, you are not guaranteed that any of the values
411 * you pass are honored exactly. Use the system defaults (TODO) if possible
412 * because they will be set to values that work on any given hardware.
413 * <p>
414 * The alpha channel must be set for forward compatibility.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500415 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 */
417 public static final int FLAG_SHOW_LIGHTS = 0x00000001;
418
419 /**
420 * Bit to be bitwise-ored into the {@link #flags} field that should be
421 * set if this notification is in reference to something that is ongoing,
422 * like a phone call. It should not be set if this notification is in
423 * reference to something that happened at a particular point in time,
424 * like a missed phone call.
425 */
426 public static final int FLAG_ONGOING_EVENT = 0x00000002;
427
428 /**
429 * Bit to be bitwise-ored into the {@link #flags} field that if set,
Scott Mainb8b36452009-04-26 15:50:49 -0700430 * the audio will be repeated until the notification is
431 * cancelled or the notification window is opened.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 */
433 public static final int FLAG_INSISTENT = 0x00000004;
434
435 /**
436 * Bit to be bitwise-ored into the {@link #flags} field that should be
Griff Hazen293977b2014-04-28 08:37:20 -0700437 * set if you would only like the sound, vibrate and ticker to be played
438 * if the notification was not already showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 */
440 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
441
442 /**
443 * Bit to be bitwise-ored into the {@link #flags} field that should be
444 * set if the notification should be canceled when it is clicked by the
Daniel Sandler8aa9ae62012-12-04 23:31:47 -0500445 * user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 */
447 public static final int FLAG_AUTO_CANCEL = 0x00000010;
448
449 /**
450 * Bit to be bitwise-ored into the {@link #flags} field that should be
451 * set if the notification should not be canceled when the user clicks
452 * the Clear all button.
453 */
454 public static final int FLAG_NO_CLEAR = 0x00000020;
455
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700456 /**
457 * Bit to be bitwise-ored into the {@link #flags} field that should be
458 * set if this notification represents a currently running service. This
459 * will normally be set for you by {@link Service#startForeground}.
460 */
461 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
462
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400463 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500464 * Obsolete flag indicating high-priority notifications; use the priority field instead.
Joe Malin8d40d042012-11-05 11:36:40 -0800465 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500466 * @deprecated Use {@link #priority} with a positive value.
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400467 */
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500468 public static final int FLAG_HIGH_PRIORITY = 0x00000080;
Daniel Sandlere46cbd32010-06-17 10:35:26 -0400469
Griff Hazendfcb0802014-02-11 12:00:00 -0800470 /**
471 * Bit to be bitswise-ored into the {@link #flags} field that should be
472 * set if this notification is relevant to the current device only
473 * and it is not recommended that it bridge to other devices.
474 */
475 public static final int FLAG_LOCAL_ONLY = 0x00000100;
476
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700477 /**
478 * Bit to be bitswise-ored into the {@link #flags} field that should be
479 * set if this notification is the group summary for a group of notifications.
480 * Grouped notifications may display in a cluster or stack on devices which
481 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
482 */
483 public static final int FLAG_GROUP_SUMMARY = 0x00000200;
484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 public int flags;
486
Tor Norbyed9273d62013-05-30 15:59:53 -0700487 /** @hide */
488 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
489 @Retention(RetentionPolicy.SOURCE)
490 public @interface Priority {}
491
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500493 * Default notification {@link #priority}. If your application does not prioritize its own
494 * notifications, use this value for all notifications.
495 */
496 public static final int PRIORITY_DEFAULT = 0;
497
498 /**
499 * Lower {@link #priority}, for items that are less important. The UI may choose to show these
500 * items smaller, or at a different position in the list, compared with your app's
501 * {@link #PRIORITY_DEFAULT} items.
502 */
503 public static final int PRIORITY_LOW = -1;
504
505 /**
506 * Lowest {@link #priority}; these items might not be shown to the user except under special
507 * circumstances, such as detailed notification logs.
508 */
509 public static final int PRIORITY_MIN = -2;
510
511 /**
512 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
513 * show these items larger, or at a different position in notification lists, compared with
514 * your app's {@link #PRIORITY_DEFAULT} items.
515 */
516 public static final int PRIORITY_HIGH = 1;
517
518 /**
519 * Highest {@link #priority}, for your application's most important items that require the
520 * user's prompt attention or input.
521 */
522 public static final int PRIORITY_MAX = 2;
523
524 /**
525 * Relative priority for this notification.
Joe Malin8d40d042012-11-05 11:36:40 -0800526 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500527 * Priority is an indication of how much of the user's valuable attention should be consumed by
528 * this notification. Low-priority notifications may be hidden from the user in certain
529 * situations, while the user might be interrupted for a higher-priority notification. The
Daniel Sandler6738eee2012-11-16 12:03:32 -0500530 * system will make a determination about how to interpret this priority when presenting
531 * the notification.
Chris Wren47c20a12014-06-18 17:27:29 -0400532 *
533 * <p>
534 * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
535 * as a heads-up notification.
536 * </p>
537 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500538 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700539 @Priority
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500540 public int priority;
Joe Malin8d40d042012-11-05 11:36:40 -0800541
Dan Sandler26e81cf2014-05-06 10:01:27 -0400542 /**
543 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
544 * to be applied by the standard Style templates when presenting this notification.
545 *
546 * The current template design constructs a colorful header image by overlaying the
547 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
548 * ignored.
549 */
Tor Norbye80756e32015-03-02 09:39:27 -0800550 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400551 public int color = COLOR_DEFAULT;
552
553 /**
554 * Special value of {@link #color} telling the system not to decorate this notification with
555 * any special color but instead use default colors when presenting this notification.
556 */
Tor Norbye80756e32015-03-02 09:39:27 -0800557 @ColorInt
Dan Sandler26e81cf2014-05-06 10:01:27 -0400558 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600559
560 /**
561 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
562 * the notification's presence and contents in untrusted situations (namely, on the secure
563 * lockscreen).
564 *
565 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
566 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
567 * shown in all situations, but the contents are only available if the device is unlocked for
568 * the appropriate user.
569 *
570 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
571 * can be read even in an "insecure" context (that is, above a secure lockscreen).
572 * To modify the public version of this notification—for example, to redact some portions—see
573 * {@link Builder#setPublicVersion(Notification)}.
574 *
575 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
576 * and ticker until the user has bypassed the lockscreen.
577 */
578 public int visibility;
579
Griff Hazenfc3922d2014-08-20 11:56:44 -0700580 /**
581 * Notification visibility: Show this notification in its entirety on all lockscreens.
582 *
583 * {@see #visibility}
584 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600585 public static final int VISIBILITY_PUBLIC = 1;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700586
587 /**
588 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
589 * private information on secure lockscreens.
590 *
591 * {@see #visibility}
592 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600593 public static final int VISIBILITY_PRIVATE = 0;
Griff Hazenfc3922d2014-08-20 11:56:44 -0700594
595 /**
596 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
597 *
598 * {@see #visibility}
599 */
Dan Sandler0bf2ed82013-12-21 23:33:41 -0600600 public static final int VISIBILITY_SECRET = -1;
601
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500602 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400603 * Notification category: incoming call (voice or video) or similar synchronous communication request.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500604 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400605 public static final String CATEGORY_CALL = "call";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500606
607 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400608 * Notification category: incoming direct message (SMS, instant message, etc.).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500609 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400610 public static final String CATEGORY_MESSAGE = "msg";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500611
612 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400613 * Notification category: asynchronous bulk message (email).
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500614 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400615 public static final String CATEGORY_EMAIL = "email";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500616
617 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400618 * Notification category: calendar event.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500619 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400620 public static final String CATEGORY_EVENT = "event";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500621
622 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400623 * Notification category: promotion or advertisement.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500624 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400625 public static final String CATEGORY_PROMO = "promo";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500626
627 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400628 * Notification category: alarm or timer.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500629 */
John Spurlockfd7f1e02014-03-18 16:41:57 -0400630 public static final String CATEGORY_ALARM = "alarm";
631
632 /**
633 * Notification category: progress of a long-running background operation.
634 */
635 public static final String CATEGORY_PROGRESS = "progress";
636
637 /**
638 * Notification category: social network or sharing update.
639 */
640 public static final String CATEGORY_SOCIAL = "social";
641
642 /**
643 * Notification category: error in background operation or authentication status.
644 */
645 public static final String CATEGORY_ERROR = "err";
646
647 /**
648 * Notification category: media transport control for playback.
649 */
650 public static final String CATEGORY_TRANSPORT = "transport";
651
652 /**
653 * Notification category: system or device status update. Reserved for system use.
654 */
655 public static final String CATEGORY_SYSTEM = "sys";
656
657 /**
658 * Notification category: indication of running background service.
659 */
660 public static final String CATEGORY_SERVICE = "service";
661
662 /**
John Spurlock0a69c8c2014-03-21 13:30:57 -0400663 * Notification category: a specific, timely recommendation for a single thing.
664 * For example, a news app might want to recommend a news story it believes the user will
665 * want to read next.
666 */
667 public static final String CATEGORY_RECOMMENDATION = "recommendation";
668
669 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400670 * Notification category: ongoing information about device or contextual status.
671 */
672 public static final String CATEGORY_STATUS = "status";
673
674 /**
John Spurlock24d3dad2015-04-02 12:24:02 -0400675 * Notification category: user-scheduled reminder.
676 */
677 public static final String CATEGORY_REMINDER = "reminder";
678
679 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400680 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
681 * that best describes this Notification. May be used by the system for ranking and filtering.
682 */
683 public String category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500684
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700685 private String mGroupKey;
686
687 /**
688 * Get the key used to group this notification into a cluster or stack
689 * with other notifications on devices which support such rendering.
690 */
691 public String getGroup() {
692 return mGroupKey;
693 }
694
695 private String mSortKey;
696
697 /**
698 * Get a sort key that orders this notification among other notifications from the
699 * same package. This can be useful if an external sort was already applied and an app
700 * would like to preserve this. Notifications will be sorted lexicographically using this
701 * value, although providing different priorities in addition to providing sort key may
702 * cause this value to be ignored.
703 *
704 * <p>This sort key can also be used to order members of a notification group. See
705 * {@link Builder#setGroup}.
706 *
707 * @see String#compareTo(String)
708 */
709 public String getSortKey() {
710 return mSortKey;
711 }
712
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500713 /**
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400714 * Additional semantic data to be carried around with this Notification.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400715 * <p>
716 * The extras keys defined here are intended to capture the original inputs to {@link Builder}
717 * APIs, and are intended to be used by
718 * {@link android.service.notification.NotificationListenerService} implementations to extract
719 * detailed information from notification objects.
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500720 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400721 public Bundle extras = new Bundle();
722
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400723 /**
724 * {@link #extras} key: this is the title of the notification,
725 * as supplied to {@link Builder#setContentTitle(CharSequence)}.
726 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500727 public static final String EXTRA_TITLE = "android.title";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400728
729 /**
730 * {@link #extras} key: this is the title of the notification when shown in expanded form,
731 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
732 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400733 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400734
735 /**
736 * {@link #extras} key: this is the main text payload, as supplied to
737 * {@link Builder#setContentText(CharSequence)}.
738 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500739 public static final String EXTRA_TEXT = "android.text";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400740
741 /**
742 * {@link #extras} key: this is a third line of text, as supplied to
743 * {@link Builder#setSubText(CharSequence)}.
744 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400745 public static final String EXTRA_SUB_TEXT = "android.subText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400746
747 /**
748 * {@link #extras} key: this is a small piece of additional text as supplied to
749 * {@link Builder#setContentInfo(CharSequence)}.
750 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400751 public static final String EXTRA_INFO_TEXT = "android.infoText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400752
753 /**
754 * {@link #extras} key: this is a line of summary information intended to be shown
755 * alongside expanded notifications, as supplied to (e.g.)
756 * {@link BigTextStyle#setSummaryText(CharSequence)}.
757 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400758 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400759
760 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +0200761 * {@link #extras} key: this is the longer text shown in the big form of a
762 * {@link BigTextStyle} notification, as supplied to
763 * {@link BigTextStyle#bigText(CharSequence)}.
764 */
765 public static final String EXTRA_BIG_TEXT = "android.bigText";
766
767 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400768 * {@link #extras} key: this is the resource ID of the notification's main small icon, as
769 * supplied to {@link Builder#setSmallIcon(int)}.
770 */
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -0500771 public static final String EXTRA_SMALL_ICON = "android.icon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400772
773 /**
774 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
775 * notification payload, as
776 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
777 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400778 public static final String EXTRA_LARGE_ICON = "android.largeIcon";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400779
780 /**
781 * {@link #extras} key: this is a bitmap to be used instead of the one from
782 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
783 * shown in its expanded form, as supplied to
784 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
785 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400786 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400787
788 /**
789 * {@link #extras} key: this is the progress value supplied to
790 * {@link Builder#setProgress(int, int, boolean)}.
791 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400792 public static final String EXTRA_PROGRESS = "android.progress";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400793
794 /**
795 * {@link #extras} key: this is the maximum value supplied to
796 * {@link Builder#setProgress(int, int, boolean)}.
797 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400798 public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400799
800 /**
801 * {@link #extras} key: whether the progress bar is indeterminate, supplied to
802 * {@link Builder#setProgress(int, int, boolean)}.
803 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400804 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400805
806 /**
807 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
808 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
809 * {@link Builder#setUsesChronometer(boolean)}.
810 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400811 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400812
813 /**
814 * {@link #extras} key: whether {@link #when} should be shown,
815 * as supplied to {@link Builder#setShowWhen(boolean)}.
816 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400817 public static final String EXTRA_SHOW_WHEN = "android.showWhen";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400818
819 /**
820 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
821 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
822 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400823 public static final String EXTRA_PICTURE = "android.picture";
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400824
825 /**
826 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
827 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
828 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400829 public static final String EXTRA_TEXT_LINES = "android.textLines";
Dan Sandler842dd772014-05-15 09:36:47 -0400830
831 /**
832 * {@link #extras} key: A string representing the name of the specific
833 * {@link android.app.Notification.Style} used to create this notification.
834 */
Chris Wren91ad5632013-06-05 15:05:57 -0400835 public static final String EXTRA_TEMPLATE = "android.template";
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400836
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400837 /**
Chris Wrene6c48932014-09-29 17:19:27 -0400838 * {@link #extras} key: A String array containing the people that this notification relates to,
839 * each of which was supplied to {@link Builder#addPerson(String)}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400840 */
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400841 public static final String EXTRA_PEOPLE = "android.people";
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500842
843 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -0400844 * Allow certain system-generated notifications to appear before the device is provisioned.
845 * Only available to notifications coming from the android package.
846 * @hide
847 */
848 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
849
850 /**
Jose Limae9e3b3b2014-05-18 23:44:50 -0700851 * {@link #extras} key: A
852 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
853 * in the background when the notification is selected. The URI must point to an image stream
854 * suitable for passing into
855 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
856 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
857 * URI used for this purpose must require no permissions to read the image data.
858 */
859 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
860
861 /**
Dan Sandler842dd772014-05-15 09:36:47 -0400862 * {@link #extras} key: A
Jeff Browndba34ba2014-06-24 20:46:03 -0700863 * {@link android.media.session.MediaSession.Token} associated with a
Dan Sandler842dd772014-05-15 09:36:47 -0400864 * {@link android.app.Notification.MediaStyle} notification.
865 */
866 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
867
868 /**
Bryan Mawhinneye191f902014-07-22 12:50:09 +0100869 * {@link #extras} key: the indices of actions to be shown in the compact view,
870 * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
871 */
872 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
873
Christoph Studer943aa672014-08-03 20:31:16 +0200874 /**
Kenny Guy8942bcd2014-09-08 21:09:47 +0100875 * {@link #extras} key: the user that built the notification.
876 *
877 * @hide
878 */
879 public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
880
881 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400882 * @hide
883 */
884 public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
885
Dan Sandlerd63f9322015-05-06 15:18:49 -0400886 private Icon mSmallIcon;
887 private Icon mLargeIcon;
888
Chris Wren51c75102013-07-16 20:49:17 -0400889 /**
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400890 * Structure to encapsulate a named action that can be shown as part of this notification.
891 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
892 * selected by the user.
893 * <p>
Griff Hazen959591e2014-05-15 22:26:18 -0700894 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
895 * or {@link Notification.Builder#addAction(Notification.Action)}
896 * to attach actions.
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400897 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -0500898 public static class Action implements Parcelable {
Griff Hazen959591e2014-05-15 22:26:18 -0700899 private final Bundle mExtras;
Dan Sandler86647982015-05-13 23:41:13 -0400900 private Icon mIcon;
Griff Hazen61a9e862014-05-22 16:05:19 -0700901 private final RemoteInput[] mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700902
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400903 /**
904 * Small icon representing the action.
Dan Sandler86647982015-05-13 23:41:13 -0400905 *
906 * @deprecated Use {@link Action#getIcon()} instead.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400907 */
Dan Sandler86647982015-05-13 23:41:13 -0400908 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400909 public int icon;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700910
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400911 /**
912 * Title of the action.
913 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400914 public CharSequence title;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700915
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400916 /**
917 * Intent to send when the user invokes this action. May be null, in which case the action
918 * may be rendered in a disabled presentation by the system UI.
919 */
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400920 public PendingIntent actionIntent;
Griff Hazen959591e2014-05-15 22:26:18 -0700921
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400922 private Action(Parcel in) {
Dan Sandler86647982015-05-13 23:41:13 -0400923 if (in.readInt() != 0) {
924 mIcon = Icon.CREATOR.createFromParcel(in);
Dan Sandler68079d52015-07-22 10:45:30 -0400925 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
926 icon = mIcon.getResId();
927 }
Dan Sandler86647982015-05-13 23:41:13 -0400928 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400929 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
930 if (in.readInt() == 1) {
931 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
932 }
Griff Hazen959591e2014-05-15 22:26:18 -0700933 mExtras = in.readBundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700934 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
Daniel Sandlera0a938c2012-03-15 08:42:37 -0400935 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700936
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400937 /**
Dan Sandler86647982015-05-13 23:41:13 -0400938 * @deprecated Use {@link android.app.Notification.Action.Builder}.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400939 */
Dan Sandler86647982015-05-13 23:41:13 -0400940 @Deprecated
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400941 public Action(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -0400942 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -0700943 }
944
Dan Sandler86647982015-05-13 23:41:13 -0400945 private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700946 RemoteInput[] remoteInputs) {
Dan Sandler86647982015-05-13 23:41:13 -0400947 this.mIcon = icon;
Gus Prevasf5bff46f2015-08-24 10:34:28 -0400948 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
949 this.icon = icon.getResId();
950 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -0400951 this.title = title;
952 this.actionIntent = intent;
Griff Hazen959591e2014-05-15 22:26:18 -0700953 this.mExtras = extras != null ? extras : new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700954 this.mRemoteInputs = remoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700955 }
956
957 /**
Dan Sandler86647982015-05-13 23:41:13 -0400958 * Return an icon representing the action.
959 */
960 public Icon getIcon() {
961 if (mIcon == null && icon != 0) {
962 // you snuck an icon in here without using the builder; let's try to keep it
963 mIcon = Icon.createWithResource("", icon);
964 }
965 return mIcon;
966 }
967
968 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700969 * Get additional metadata carried around with this Action.
970 */
971 public Bundle getExtras() {
972 return mExtras;
973 }
974
975 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700976 * Get the list of inputs to be collected from the user when this action is sent.
977 * May return null if no remote inputs were added.
978 */
979 public RemoteInput[] getRemoteInputs() {
980 return mRemoteInputs;
981 }
982
983 /**
Griff Hazen959591e2014-05-15 22:26:18 -0700984 * Builder class for {@link Action} objects.
985 */
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700986 public static final class Builder {
Dan Sandler86647982015-05-13 23:41:13 -0400987 private final Icon mIcon;
Griff Hazen959591e2014-05-15 22:26:18 -0700988 private final CharSequence mTitle;
989 private final PendingIntent mIntent;
990 private final Bundle mExtras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700991 private ArrayList<RemoteInput> mRemoteInputs;
Griff Hazen959591e2014-05-15 22:26:18 -0700992
993 /**
994 * Construct a new builder for {@link Action} object.
995 * @param icon icon to show for this action
996 * @param title the title of the action
997 * @param intent the {@link PendingIntent} to fire when users trigger this action
998 */
Dan Sandler86647982015-05-13 23:41:13 -0400999 @Deprecated
Griff Hazen959591e2014-05-15 22:26:18 -07001000 public Builder(int icon, CharSequence title, PendingIntent intent) {
Dan Sandler86647982015-05-13 23:41:13 -04001001 this(Icon.createWithResource("", icon), title, intent, new Bundle(), null);
1002 }
1003
1004 /**
1005 * Construct a new builder for {@link Action} object.
1006 * @param icon icon to show for this action
1007 * @param title the title of the action
1008 * @param intent the {@link PendingIntent} to fire when users trigger this action
1009 */
1010 public Builder(Icon icon, CharSequence title, PendingIntent intent) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001011 this(icon, title, intent, new Bundle(), null);
Griff Hazen959591e2014-05-15 22:26:18 -07001012 }
1013
1014 /**
1015 * Construct a new builder for {@link Action} object using the fields from an
1016 * {@link Action}.
1017 * @param action the action to read fields from.
1018 */
1019 public Builder(Action action) {
Dan Sandler86647982015-05-13 23:41:13 -04001020 this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001021 action.getRemoteInputs());
Griff Hazen959591e2014-05-15 22:26:18 -07001022 }
1023
Dan Sandler86647982015-05-13 23:41:13 -04001024 private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001025 RemoteInput[] remoteInputs) {
Griff Hazen959591e2014-05-15 22:26:18 -07001026 mIcon = icon;
1027 mTitle = title;
1028 mIntent = intent;
1029 mExtras = extras;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001030 if (remoteInputs != null) {
1031 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1032 Collections.addAll(mRemoteInputs, remoteInputs);
1033 }
Griff Hazen959591e2014-05-15 22:26:18 -07001034 }
1035
1036 /**
1037 * Merge additional metadata into this builder.
1038 *
1039 * <p>Values within the Bundle will replace existing extras values in this Builder.
1040 *
1041 * @see Notification.Action#extras
1042 */
1043 public Builder addExtras(Bundle extras) {
1044 if (extras != null) {
1045 mExtras.putAll(extras);
1046 }
1047 return this;
1048 }
1049
1050 /**
1051 * Get the metadata Bundle used by this Builder.
1052 *
1053 * <p>The returned Bundle is shared with this Builder.
1054 */
1055 public Bundle getExtras() {
1056 return mExtras;
1057 }
1058
1059 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001060 * Add an input to be collected from the user when this action is sent.
1061 * Response values can be retrieved from the fired intent by using the
1062 * {@link RemoteInput#getResultsFromIntent} function.
1063 * @param remoteInput a {@link RemoteInput} to add to the action
1064 * @return this object for method chaining
1065 */
1066 public Builder addRemoteInput(RemoteInput remoteInput) {
1067 if (mRemoteInputs == null) {
1068 mRemoteInputs = new ArrayList<RemoteInput>();
1069 }
1070 mRemoteInputs.add(remoteInput);
1071 return this;
1072 }
1073
1074 /**
1075 * Apply an extender to this action builder. Extenders may be used to add
1076 * metadata or change options on this builder.
1077 */
Griff Hazen61a9e862014-05-22 16:05:19 -07001078 public Builder extend(Extender extender) {
1079 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001080 return this;
1081 }
1082
1083 /**
Griff Hazen959591e2014-05-15 22:26:18 -07001084 * Combine all of the options that have been set and return a new {@link Action}
1085 * object.
1086 * @return the built action
1087 */
1088 public Action build() {
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001089 RemoteInput[] remoteInputs = mRemoteInputs != null
1090 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1091 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
Griff Hazen959591e2014-05-15 22:26:18 -07001092 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001093 }
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001094
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001095 @Override
1096 public Action clone() {
1097 return new Action(
Dan Sandler86647982015-05-13 23:41:13 -04001098 getIcon(),
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001099 title,
1100 actionIntent, // safe to alias
1101 new Bundle(mExtras),
1102 getRemoteInputs());
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001103 }
1104 @Override
1105 public int describeContents() {
1106 return 0;
1107 }
1108 @Override
1109 public void writeToParcel(Parcel out, int flags) {
Dan Sandler86647982015-05-13 23:41:13 -04001110 final Icon ic = getIcon();
1111 if (ic != null) {
1112 out.writeInt(1);
1113 ic.writeToParcel(out, 0);
1114 } else {
1115 out.writeInt(0);
1116 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001117 TextUtils.writeToParcel(title, out, flags);
1118 if (actionIntent != null) {
1119 out.writeInt(1);
1120 actionIntent.writeToParcel(out, flags);
1121 } else {
1122 out.writeInt(0);
1123 }
Griff Hazen959591e2014-05-15 22:26:18 -07001124 out.writeBundle(mExtras);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001125 out.writeTypedArray(mRemoteInputs, flags);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001126 }
Griff Hazen959591e2014-05-15 22:26:18 -07001127 public static final Parcelable.Creator<Action> CREATOR =
1128 new Parcelable.Creator<Action>() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001129 public Action createFromParcel(Parcel in) {
1130 return new Action(in);
1131 }
1132 public Action[] newArray(int size) {
1133 return new Action[size];
1134 }
1135 };
Griff Hazen61a9e862014-05-22 16:05:19 -07001136
1137 /**
1138 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1139 * metadata or change options on an action builder.
1140 */
1141 public interface Extender {
1142 /**
1143 * Apply this extender to a notification action builder.
1144 * @param builder the builder to be modified.
1145 * @return the build object for chaining.
1146 */
1147 public Builder extend(Builder builder);
1148 }
1149
1150 /**
1151 * Wearable extender for notification actions. To add extensions to an action,
1152 * create a new {@link android.app.Notification.Action.WearableExtender} object using
1153 * the {@code WearableExtender()} constructor and apply it to a
1154 * {@link android.app.Notification.Action.Builder} using
1155 * {@link android.app.Notification.Action.Builder#extend}.
1156 *
1157 * <pre class="prettyprint">
1158 * Notification.Action action = new Notification.Action.Builder(
1159 * R.drawable.archive_all, "Archive all", actionIntent)
Griff Hazen14f57992014-05-26 09:07:14 -07001160 * .extend(new Notification.Action.WearableExtender()
Griff Hazen61a9e862014-05-22 16:05:19 -07001161 * .setAvailableOffline(false))
Griff Hazen14f57992014-05-26 09:07:14 -07001162 * .build();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07001163 */
1164 public static final class WearableExtender implements Extender {
1165 /** Notification action extra which contains wearable extensions */
1166 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1167
Pete Gastaf6781d2014-10-07 15:17:05 -04001168 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07001169 private static final String KEY_FLAGS = "flags";
Pete Gastaf6781d2014-10-07 15:17:05 -04001170 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1171 private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1172 private static final String KEY_CANCEL_LABEL = "cancelLabel";
Griff Hazen61a9e862014-05-22 16:05:19 -07001173
1174 // Flags bitwise-ored to mFlags
1175 private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1176
1177 // Default value for flags integer
1178 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1179
1180 private int mFlags = DEFAULT_FLAGS;
1181
Pete Gastaf6781d2014-10-07 15:17:05 -04001182 private CharSequence mInProgressLabel;
1183 private CharSequence mConfirmLabel;
1184 private CharSequence mCancelLabel;
1185
Griff Hazen61a9e862014-05-22 16:05:19 -07001186 /**
1187 * Create a {@link android.app.Notification.Action.WearableExtender} with default
1188 * options.
1189 */
1190 public WearableExtender() {
1191 }
1192
1193 /**
1194 * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1195 * wearable options present in an existing notification action.
1196 * @param action the notification action to inspect.
1197 */
1198 public WearableExtender(Action action) {
1199 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1200 if (wearableBundle != null) {
1201 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
Pete Gastaf6781d2014-10-07 15:17:05 -04001202 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1203 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1204 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
Griff Hazen61a9e862014-05-22 16:05:19 -07001205 }
1206 }
1207
1208 /**
1209 * Apply wearable extensions to a notification action that is being built. This is
1210 * typically called by the {@link android.app.Notification.Action.Builder#extend}
1211 * method of {@link android.app.Notification.Action.Builder}.
1212 */
1213 @Override
1214 public Action.Builder extend(Action.Builder builder) {
1215 Bundle wearableBundle = new Bundle();
1216
1217 if (mFlags != DEFAULT_FLAGS) {
1218 wearableBundle.putInt(KEY_FLAGS, mFlags);
1219 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001220 if (mInProgressLabel != null) {
1221 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1222 }
1223 if (mConfirmLabel != null) {
1224 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1225 }
1226 if (mCancelLabel != null) {
1227 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1228 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001229
1230 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1231 return builder;
1232 }
1233
1234 @Override
1235 public WearableExtender clone() {
1236 WearableExtender that = new WearableExtender();
1237 that.mFlags = this.mFlags;
Pete Gastaf6781d2014-10-07 15:17:05 -04001238 that.mInProgressLabel = this.mInProgressLabel;
1239 that.mConfirmLabel = this.mConfirmLabel;
1240 that.mCancelLabel = this.mCancelLabel;
Griff Hazen61a9e862014-05-22 16:05:19 -07001241 return that;
1242 }
1243
1244 /**
1245 * Set whether this action is available when the wearable device is not connected to
1246 * a companion device. The user can still trigger this action when the wearable device is
1247 * offline, but a visual hint will indicate that the action may not be available.
1248 * Defaults to true.
1249 */
1250 public WearableExtender setAvailableOffline(boolean availableOffline) {
1251 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1252 return this;
1253 }
1254
1255 /**
1256 * Get whether this action is available when the wearable device is not connected to
1257 * a companion device. The user can still trigger this action when the wearable device is
1258 * offline, but a visual hint will indicate that the action may not be available.
1259 * Defaults to true.
1260 */
1261 public boolean isAvailableOffline() {
1262 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1263 }
1264
1265 private void setFlag(int mask, boolean value) {
1266 if (value) {
1267 mFlags |= mask;
1268 } else {
1269 mFlags &= ~mask;
1270 }
1271 }
Pete Gastaf6781d2014-10-07 15:17:05 -04001272
1273 /**
1274 * Set a label to display while the wearable is preparing to automatically execute the
1275 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1276 *
1277 * @param label the label to display while the action is being prepared to execute
1278 * @return this object for method chaining
1279 */
1280 public WearableExtender setInProgressLabel(CharSequence label) {
1281 mInProgressLabel = label;
1282 return this;
1283 }
1284
1285 /**
1286 * Get the label to display while the wearable is preparing to automatically execute
1287 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1288 *
1289 * @return the label to display while the action is being prepared to execute
1290 */
1291 public CharSequence getInProgressLabel() {
1292 return mInProgressLabel;
1293 }
1294
1295 /**
1296 * Set a label to display to confirm that the action should be executed.
1297 * This is usually an imperative verb like "Send".
1298 *
1299 * @param label the label to confirm the action should be executed
1300 * @return this object for method chaining
1301 */
1302 public WearableExtender setConfirmLabel(CharSequence label) {
1303 mConfirmLabel = label;
1304 return this;
1305 }
1306
1307 /**
1308 * Get the label to display to confirm that the action should be executed.
1309 * This is usually an imperative verb like "Send".
1310 *
1311 * @return the label to confirm the action should be executed
1312 */
1313 public CharSequence getConfirmLabel() {
1314 return mConfirmLabel;
1315 }
1316
1317 /**
1318 * Set a label to display to cancel the action.
1319 * This is usually an imperative verb, like "Cancel".
1320 *
1321 * @param label the label to display to cancel the action
1322 * @return this object for method chaining
1323 */
1324 public WearableExtender setCancelLabel(CharSequence label) {
1325 mCancelLabel = label;
1326 return this;
1327 }
1328
1329 /**
1330 * Get the label to display to cancel the action.
1331 * This is usually an imperative verb like "Cancel".
1332 *
1333 * @return the label to display to cancel the action
1334 */
1335 public CharSequence getCancelLabel() {
1336 return mCancelLabel;
1337 }
Griff Hazen61a9e862014-05-22 16:05:19 -07001338 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001339 }
1340
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04001341 /**
1342 * Array of all {@link Action} structures attached to this notification by
1343 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1344 * {@link android.service.notification.NotificationListenerService} that provide an alternative
1345 * interface for invoking actions.
1346 */
Daniel Sandlerea2a3172013-02-20 22:24:20 -05001347 public Action[] actions;
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001348
1349 /**
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001350 * Replacement version of this notification whose content will be shown
1351 * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1352 * and {@link #VISIBILITY_PUBLIC}.
1353 */
1354 public Notification publicVersion;
1355
1356 /**
Julia Reynolds74303cf2015-10-16 11:37:55 -04001357 * Structure to encapsulate a topic that is shown in Notification settings.
1358 * It must include an id and label.
1359 */
1360 public static class Topic implements Parcelable {
1361 private final String id;
1362 private final CharSequence label;
1363
1364 public Topic(String id, CharSequence label) {
1365 this.id = id;
1366 this.label = safeCharSequence(label);
1367 }
1368
1369 private Topic(Parcel in) {
1370 if (in.readInt() != 0) {
1371 id = in.readString();
1372 } else {
1373 id = null;
1374 }
1375 label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1376 }
1377
1378 public String getId() {
1379 return id;
1380 }
1381
1382 public CharSequence getLabel() {
1383 return label;
1384 }
1385
1386 @Override
1387 public String toString() {
1388 return new StringBuilder(Topic.class.getSimpleName()).append('[')
1389 .append("id=").append(id)
1390 .append(",label=").append(label)
1391 .append(']').toString();
1392 }
1393
1394 @Override
1395 public boolean equals(Object o) {
1396 if (!(o instanceof Topic)) return false;
1397 if (o == this) return true;
1398 final Topic other = (Topic) o;
1399 return Objects.equals(other.id, id)
1400 && Objects.equals(other.label, label);
1401 }
1402
1403 @Override
1404 public int hashCode() {
1405 return Objects.hash(id, label);
1406 }
1407
1408 @Override
1409 public Topic clone() {
1410 return new Topic(id, label);
1411 }
1412
1413 @Override
1414 public int describeContents() {
1415 return 0;
1416 }
1417
1418 @Override
1419 public void writeToParcel(Parcel out, int flags) {
1420 if (id != null) {
1421 out.writeInt(1);
1422 out.writeString(id);
1423 } else {
1424 out.writeInt(0);
1425 }
1426 TextUtils.writeToParcel(label, out, flags);
1427 }
1428 public static final Parcelable.Creator<Topic> CREATOR =
1429 new Parcelable.Creator<Topic>() {
1430 public Topic createFromParcel(Parcel in) {
1431 return new Topic(in);
1432 }
1433 public Topic[] newArray(int size) {
1434 return new Topic[size];
1435 }
1436 };
1437 }
1438
Julia Reynolds233a5f92015-10-19 13:51:23 -04001439 @SystemApi
1440 public static final String TOPIC_DEFAULT = "system_default_topic";
1441
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001442 private Topic topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001443
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001444 public Topic getTopic() {
1445 return topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04001446 }
1447
1448 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001449 * Constructs a Notification object with default values.
Joe Onorato46439ce2010-11-19 13:56:21 -08001450 * You might want to consider using {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 */
1452 public Notification()
1453 {
1454 this.when = System.currentTimeMillis();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001455 this.priority = PRIORITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001456 }
1457
1458 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001459 * @hide
1460 */
1461 public Notification(Context context, int icon, CharSequence tickerText, long when,
1462 CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1463 {
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001464 new Builder(context)
1465 .setWhen(when)
1466 .setSmallIcon(icon)
1467 .setTicker(tickerText)
1468 .setContentTitle(contentTitle)
1469 .setContentText(contentText)
1470 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1471 .buildInto(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 }
1473
1474 /**
1475 * Constructs a Notification object with the information needed to
1476 * have a status bar icon without the standard expanded view.
1477 *
1478 * @param icon The resource id of the icon to put in the status bar.
1479 * @param tickerText The text that flows by in the status bar when the notification first
1480 * activates.
1481 * @param when The time to show in the time field. In the System.currentTimeMillis
1482 * timebase.
Joe Onorato46439ce2010-11-19 13:56:21 -08001483 *
1484 * @deprecated Use {@link Builder} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001486 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 public Notification(int icon, CharSequence tickerText, long when)
1488 {
1489 this.icon = icon;
1490 this.tickerText = tickerText;
1491 this.when = when;
1492 }
1493
1494 /**
1495 * Unflatten the notification from a parcel.
1496 */
1497 public Notification(Parcel parcel)
1498 {
1499 int version = parcel.readInt();
1500
1501 when = parcel.readLong();
Dan Sandler3936e7a2015-05-19 20:59:12 -04001502 if (parcel.readInt() != 0) {
1503 mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
Dan Sandler4e787062015-06-17 15:09:48 -04001504 if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1505 icon = mSmallIcon.getResId();
1506 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001507 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 number = parcel.readInt();
1509 if (parcel.readInt() != 0) {
1510 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1511 }
1512 if (parcel.readInt() != 0) {
1513 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1514 }
1515 if (parcel.readInt() != 0) {
1516 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1517 }
1518 if (parcel.readInt() != 0) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001519 tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001520 }
1521 if (parcel.readInt() != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1523 }
Joe Onorato561d3852010-11-20 18:09:34 -08001524 if (parcel.readInt() != 0) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04001525 mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
Joe Onorato561d3852010-11-20 18:09:34 -08001526 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001527 defaults = parcel.readInt();
1528 flags = parcel.readInt();
1529 if (parcel.readInt() != 0) {
1530 sound = Uri.CREATOR.createFromParcel(parcel);
1531 }
1532
1533 audioStreamType = parcel.readInt();
John Spurlockc0650f022014-07-19 13:22:39 -04001534 if (parcel.readInt() != 0) {
1535 audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1536 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 vibrate = parcel.createLongArray();
1538 ledARGB = parcel.readInt();
1539 ledOnMS = parcel.readInt();
1540 ledOffMS = parcel.readInt();
1541 iconLevel = parcel.readInt();
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001542
1543 if (parcel.readInt() != 0) {
1544 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1545 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001546
1547 priority = parcel.readInt();
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001548
John Spurlockfd7f1e02014-03-18 16:41:57 -04001549 category = parcel.readString();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001550
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001551 mGroupKey = parcel.readString();
1552
1553 mSortKey = parcel.readString();
1554
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001555 extras = parcel.readBundle(); // may be null
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001556
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001557 actions = parcel.createTypedArray(Action.CREATOR); // may be null
1558
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001559 if (parcel.readInt() != 0) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001560 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1561 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001562
Chris Wren8fd39ec2014-02-27 17:43:26 -05001563 if (parcel.readInt() != 0) {
1564 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1565 }
1566
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001567 visibility = parcel.readInt();
1568
1569 if (parcel.readInt() != 0) {
1570 publicVersion = Notification.CREATOR.createFromParcel(parcel);
1571 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001572
1573 color = parcel.readInt();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001574
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001575 if (parcel.readInt() != 0) {
1576 topic = Topic.CREATOR.createFromParcel(parcel);
1577 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001578 }
1579
Andy Stadler110988c2010-12-03 14:29:16 -08001580 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -07001581 public Notification clone() {
1582 Notification that = new Notification();
Daniel Sandler1a497d32013-04-18 14:52:45 -04001583 cloneInto(that, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001584 return that;
1585 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001586
Daniel Sandler1a497d32013-04-18 14:52:45 -04001587 /**
1588 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1589 * of this into that.
1590 * @hide
1591 */
1592 public void cloneInto(Notification that, boolean heavy) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001593 that.when = this.when;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001594 that.mSmallIcon = this.mSmallIcon;
Joe Onorato18e69df2010-05-17 22:26:12 -07001595 that.number = this.number;
1596
1597 // PendingIntents are global, so there's no reason (or way) to clone them.
1598 that.contentIntent = this.contentIntent;
1599 that.deleteIntent = this.deleteIntent;
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001600 that.fullScreenIntent = this.fullScreenIntent;
Joe Onorato18e69df2010-05-17 22:26:12 -07001601
1602 if (this.tickerText != null) {
1603 that.tickerText = this.tickerText.toString();
1604 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001605 if (heavy && this.tickerView != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001606 that.tickerView = this.tickerView.clone();
Joe Onoratoef1e7762010-09-17 18:38:38 -04001607 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001608 if (heavy && this.contentView != null) {
Joe Onorato18e69df2010-05-17 22:26:12 -07001609 that.contentView = this.contentView.clone();
1610 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001611 if (heavy && this.mLargeIcon != null) {
1612 that.mLargeIcon = this.mLargeIcon;
Joe Onorato561d3852010-11-20 18:09:34 -08001613 }
Jozef BABJAKa8b91832011-02-22 08:05:08 +01001614 that.iconLevel = this.iconLevel;
Joe Onorato18e69df2010-05-17 22:26:12 -07001615 that.sound = this.sound; // android.net.Uri is immutable
1616 that.audioStreamType = this.audioStreamType;
John Spurlockc0650f022014-07-19 13:22:39 -04001617 if (this.audioAttributes != null) {
1618 that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1619 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001620
1621 final long[] vibrate = this.vibrate;
1622 if (vibrate != null) {
1623 final int N = vibrate.length;
1624 final long[] vib = that.vibrate = new long[N];
1625 System.arraycopy(vibrate, 0, vib, 0, N);
1626 }
1627
1628 that.ledARGB = this.ledARGB;
1629 that.ledOnMS = this.ledOnMS;
1630 that.ledOffMS = this.ledOffMS;
1631 that.defaults = this.defaults;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001632
Joe Onorato18e69df2010-05-17 22:26:12 -07001633 that.flags = this.flags;
1634
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001635 that.priority = this.priority;
Joe Malin8d40d042012-11-05 11:36:40 -08001636
John Spurlockfd7f1e02014-03-18 16:41:57 -04001637 that.category = this.category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001638
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001639 that.mGroupKey = this.mGroupKey;
1640
1641 that.mSortKey = this.mSortKey;
1642
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001643 if (this.extras != null) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001644 try {
1645 that.extras = new Bundle(this.extras);
1646 // will unparcel
1647 that.extras.size();
1648 } catch (BadParcelableException e) {
1649 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1650 that.extras = null;
1651 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001652 }
1653
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001654 if (this.actions != null) {
1655 that.actions = new Action[this.actions.length];
1656 for(int i=0; i<this.actions.length; i++) {
1657 that.actions[i] = this.actions[i].clone();
1658 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001659 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001660
Daniel Sandler1a497d32013-04-18 14:52:45 -04001661 if (heavy && this.bigContentView != null) {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001662 that.bigContentView = this.bigContentView.clone();
1663 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001664
Chris Wren8fd39ec2014-02-27 17:43:26 -05001665 if (heavy && this.headsUpContentView != null) {
1666 that.headsUpContentView = this.headsUpContentView.clone();
1667 }
1668
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001669 that.visibility = this.visibility;
1670
1671 if (this.publicVersion != null) {
1672 that.publicVersion = new Notification();
1673 this.publicVersion.cloneInto(that.publicVersion, heavy);
1674 }
1675
Dan Sandler26e81cf2014-05-06 10:01:27 -04001676 that.color = this.color;
1677
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001678 if (this.topic != null) {
1679 that.topic = this.topic.clone();
Julia Reynolds74303cf2015-10-16 11:37:55 -04001680 }
1681
Daniel Sandler1a497d32013-04-18 14:52:45 -04001682 if (!heavy) {
1683 that.lightenPayload(); // will clean out extras
1684 }
1685 }
1686
1687 /**
1688 * Removes heavyweight parts of the Notification object for archival or for sending to
1689 * listeners when the full contents are not necessary.
1690 * @hide
1691 */
1692 public final void lightenPayload() {
1693 tickerView = null;
1694 contentView = null;
1695 bigContentView = null;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001696 headsUpContentView = null;
Dan Sandlerd63f9322015-05-06 15:18:49 -04001697 mLargeIcon = null;
Dan Sandler50128532015-12-08 15:42:41 -05001698 if (extras != null && !extras.isEmpty()) {
1699 final Set<String> keyset = extras.keySet();
1700 final int N = keyset.size();
1701 final String[] keys = keyset.toArray(new String[N]);
1702 for (int i=0; i<N; i++) {
1703 final String key = keys[i];
1704 final Object obj = extras.get(key);
1705 if (obj != null &&
1706 ( obj instanceof Parcelable
1707 || obj instanceof Parcelable[]
1708 || obj instanceof SparseArray
1709 || obj instanceof ArrayList)) {
1710 extras.remove(key);
1711 }
1712 }
Daniel Sandler1a497d32013-04-18 14:52:45 -04001713 }
Joe Onorato18e69df2010-05-17 22:26:12 -07001714 }
1715
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001716 /**
1717 * Make sure this CharSequence is safe to put into a bundle, which basically
1718 * means it had better not be some custom Parcelable implementation.
1719 * @hide
1720 */
1721 public static CharSequence safeCharSequence(CharSequence cs) {
Christoph Studer535ec612014-09-03 15:47:47 +02001722 if (cs == null) return cs;
1723 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1724 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1725 }
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04001726 if (cs instanceof Parcelable) {
1727 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1728 + " instance is a custom Parcelable and not allowed in Notification");
1729 return cs.toString();
1730 }
1731
1732 return cs;
1733 }
1734
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001735 public int describeContents() {
1736 return 0;
1737 }
1738
1739 /**
Dan Sandler4e787062015-06-17 15:09:48 -04001740 * Flatten this notification into a parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 */
1742 public void writeToParcel(Parcel parcel, int flags)
1743 {
1744 parcel.writeInt(1);
1745
1746 parcel.writeLong(when);
Dan Sandler4e787062015-06-17 15:09:48 -04001747 if (mSmallIcon == null && icon != 0) {
1748 // you snuck an icon in here without using the builder; let's try to keep it
1749 mSmallIcon = Icon.createWithResource("", icon);
1750 }
Dan Sandler3936e7a2015-05-19 20:59:12 -04001751 if (mSmallIcon != null) {
1752 parcel.writeInt(1);
1753 mSmallIcon.writeToParcel(parcel, 0);
1754 } else {
1755 parcel.writeInt(0);
1756 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 parcel.writeInt(number);
1758 if (contentIntent != null) {
1759 parcel.writeInt(1);
1760 contentIntent.writeToParcel(parcel, 0);
1761 } else {
1762 parcel.writeInt(0);
1763 }
1764 if (deleteIntent != null) {
1765 parcel.writeInt(1);
1766 deleteIntent.writeToParcel(parcel, 0);
1767 } else {
1768 parcel.writeInt(0);
1769 }
1770 if (tickerText != null) {
1771 parcel.writeInt(1);
1772 TextUtils.writeToParcel(tickerText, parcel, flags);
1773 } else {
1774 parcel.writeInt(0);
1775 }
Joe Onorato46439ce2010-11-19 13:56:21 -08001776 if (tickerView != null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -04001777 parcel.writeInt(1);
Joe Onorato46439ce2010-11-19 13:56:21 -08001778 tickerView.writeToParcel(parcel, 0);
Joe Onoratoef1e7762010-09-17 18:38:38 -04001779 } else {
1780 parcel.writeInt(0);
1781 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 if (contentView != null) {
1783 parcel.writeInt(1);
1784 contentView.writeToParcel(parcel, 0);
1785 } else {
1786 parcel.writeInt(0);
1787 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04001788 if (mLargeIcon != null) {
Joe Onorato561d3852010-11-20 18:09:34 -08001789 parcel.writeInt(1);
Dan Sandlerd63f9322015-05-06 15:18:49 -04001790 mLargeIcon.writeToParcel(parcel, 0);
Joe Onorato561d3852010-11-20 18:09:34 -08001791 } else {
1792 parcel.writeInt(0);
1793 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794
1795 parcel.writeInt(defaults);
1796 parcel.writeInt(this.flags);
1797
1798 if (sound != null) {
1799 parcel.writeInt(1);
1800 sound.writeToParcel(parcel, 0);
1801 } else {
1802 parcel.writeInt(0);
1803 }
1804 parcel.writeInt(audioStreamType);
John Spurlockc0650f022014-07-19 13:22:39 -04001805
1806 if (audioAttributes != null) {
1807 parcel.writeInt(1);
1808 audioAttributes.writeToParcel(parcel, 0);
1809 } else {
1810 parcel.writeInt(0);
1811 }
1812
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001813 parcel.writeLongArray(vibrate);
1814 parcel.writeInt(ledARGB);
1815 parcel.writeInt(ledOnMS);
1816 parcel.writeInt(ledOffMS);
1817 parcel.writeInt(iconLevel);
Daniel Sandlere46cbd32010-06-17 10:35:26 -04001818
1819 if (fullScreenIntent != null) {
1820 parcel.writeInt(1);
1821 fullScreenIntent.writeToParcel(parcel, 0);
1822 } else {
1823 parcel.writeInt(0);
1824 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001825
1826 parcel.writeInt(priority);
Joe Malin8d40d042012-11-05 11:36:40 -08001827
John Spurlockfd7f1e02014-03-18 16:41:57 -04001828 parcel.writeString(category);
Joe Malin8d40d042012-11-05 11:36:40 -08001829
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001830 parcel.writeString(mGroupKey);
1831
1832 parcel.writeString(mSortKey);
1833
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001834 parcel.writeBundle(extras); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001835
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001836 parcel.writeTypedArray(actions, 0); // null ok
Daniel Sandlera0a938c2012-03-15 08:42:37 -04001837
Daniel Sandlerf3b73432012-03-27 15:01:25 -04001838 if (bigContentView != null) {
1839 parcel.writeInt(1);
1840 bigContentView.writeToParcel(parcel, 0);
1841 } else {
1842 parcel.writeInt(0);
1843 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001844
Chris Wren8fd39ec2014-02-27 17:43:26 -05001845 if (headsUpContentView != null) {
1846 parcel.writeInt(1);
1847 headsUpContentView.writeToParcel(parcel, 0);
1848 } else {
1849 parcel.writeInt(0);
1850 }
1851
Dan Sandler0bf2ed82013-12-21 23:33:41 -06001852 parcel.writeInt(visibility);
1853
1854 if (publicVersion != null) {
1855 parcel.writeInt(1);
1856 publicVersion.writeToParcel(parcel, 0);
1857 } else {
1858 parcel.writeInt(0);
1859 }
Dan Sandler26e81cf2014-05-06 10:01:27 -04001860
1861 parcel.writeInt(color);
Julia Reynolds74303cf2015-10-16 11:37:55 -04001862
Julia Reynolds054c5dc2015-11-17 15:36:30 -05001863 if (topic != null) {
1864 parcel.writeInt(1);
1865 topic.writeToParcel(parcel, 0);
1866 } else {
1867 parcel.writeInt(0);
1868 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001869 }
1870
1871 /**
1872 * Parcelable.Creator that instantiates Notification objects
1873 */
1874 public static final Parcelable.Creator<Notification> CREATOR
1875 = new Parcelable.Creator<Notification>()
1876 {
1877 public Notification createFromParcel(Parcel parcel)
1878 {
1879 return new Notification(parcel);
1880 }
1881
1882 public Notification[] newArray(int size)
1883 {
1884 return new Notification[size];
1885 }
1886 };
1887
1888 /**
1889 * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1890 * layout.
1891 *
1892 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1893 * in the view.</p>
1894 * @param context The context for your application / activity.
1895 * @param contentTitle The title that goes in the expanded entry.
1896 * @param contentText The text that goes in the expanded entry.
1897 * @param contentIntent The intent to launch when the user clicks the expanded notification.
1898 * If this is an activity, it must include the
1899 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
Scott Main7aee61f2011-02-08 11:25:01 -08001900 * that you take care of task management as described in the
1901 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1902 * Stack</a> document.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001903 *
Joe Onorato46439ce2010-11-19 13:56:21 -08001904 * @deprecated Use {@link Builder} instead.
Chris Wrena05db382015-06-24 15:18:34 -04001905 * @removed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 */
Joe Onorato46439ce2010-11-19 13:56:21 -08001907 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001908 public void setLatestEventInfo(Context context,
1909 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001910 if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
1911 Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
1912 new Throwable());
1913 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001914
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001915 // ensure that any information already set directly is preserved
1916 final Notification.Builder builder = new Notification.Builder(context, this);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001917
1918 // now apply the latestEventInfo fields
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001919 if (contentTitle != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001920 builder.setContentTitle(contentTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001921 }
1922 if (contentText != null) {
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001923 builder.setContentText(contentText);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05001925 builder.setContentIntent(contentIntent);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04001926
1927 builder.build(); // callers expect this notification to be ready to use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001928 }
1929
Julia Reynoldsda303542015-11-23 14:00:20 -05001930 /**
1931 * @hide
1932 */
1933 public static void addFieldsFromContext(Context context, Notification notification) {
1934 if (notification.extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO) == null) {
1935 notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
1936 context.getApplicationInfo());
1937 }
1938 if (!notification.extras.containsKey(EXTRA_ORIGINATING_USERID)) {
1939 notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
1940 }
1941 }
1942
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001943 @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 Reynolds054c5dc2015-11-17 15:36:30 -05002011 if (topic != null) {
2012 sb.append("topic=");
2013 sb.append(topic.toString());
Julia Reynolds74303cf2015-10-16 11:37:55 -04002014 }
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002015 sb.append(")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016 return sb.toString();
2017 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002018
Dan Sandler1b718782014-07-18 12:43:45 -04002019 /**
2020 * {@hide}
2021 */
2022 public static String visibilityToString(int vis) {
2023 switch (vis) {
2024 case VISIBILITY_PRIVATE:
2025 return "PRIVATE";
2026 case VISIBILITY_PUBLIC:
2027 return "PUBLIC";
2028 case VISIBILITY_SECRET:
2029 return "SECRET";
2030 default:
2031 return "UNKNOWN(" + String.valueOf(vis) + ")";
2032 }
2033 }
2034
Joe Onoratocb109a02011-01-18 17:57:41 -08002035 /**
John Spurlock1d881a12015-03-18 19:21:54 -04002036 * {@hide}
2037 */
2038 public static String priorityToString(@Priority int pri) {
2039 switch (pri) {
2040 case PRIORITY_MIN:
2041 return "MIN";
2042 case PRIORITY_LOW:
2043 return "LOW";
2044 case PRIORITY_DEFAULT:
2045 return "DEFAULT";
2046 case PRIORITY_HIGH:
2047 return "HIGH";
2048 case PRIORITY_MAX:
2049 return "MAX";
2050 default:
2051 return "UNKNOWN(" + String.valueOf(pri) + ")";
2052 }
2053 }
2054
2055 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002056 * The small icon representing this notification in the status bar and content view.
2057 *
2058 * @return the small icon representing this notification.
2059 *
2060 * @see Builder#getSmallIcon()
2061 * @see Builder#setSmallIcon(Icon)
2062 */
2063 public Icon getSmallIcon() {
2064 return mSmallIcon;
2065 }
2066
2067 /**
2068 * Used when notifying to clean up legacy small icons.
2069 * @hide
2070 */
2071 public void setSmallIcon(Icon icon) {
2072 mSmallIcon = icon;
2073 }
2074
2075 /**
2076 * The large icon shown in this notification's content view.
2077 * @see Builder#getLargeIcon()
2078 * @see Builder#setLargeIcon(Icon)
2079 */
2080 public Icon getLargeIcon() {
2081 return mLargeIcon;
2082 }
2083
2084 /**
Christoph Studer4600f9b2014-07-22 22:44:43 +02002085 * @hide
2086 */
Christoph Studerc8db24b2014-07-25 17:50:30 +02002087 public boolean isGroupSummary() {
2088 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2089 }
2090
2091 /**
2092 * @hide
2093 */
2094 public boolean isGroupChild() {
2095 return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2096 }
2097
2098 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002099 * Builder class for {@link Notification} objects.
Joe Malin8d40d042012-11-05 11:36:40 -08002100 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002101 * Provides a convenient way to set the various fields of a {@link Notification} and generate
Scott Main183bf112012-08-13 19:12:13 -07002102 * content views using the platform's notification layout template. If your app supports
2103 * versions of Android as old as API level 4, you can instead use
2104 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2105 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2106 * library</a>.
Joe Malin8d40d042012-11-05 11:36:40 -08002107 *
Scott Main183bf112012-08-13 19:12:13 -07002108 * <p>Example:
Joe Malin8d40d042012-11-05 11:36:40 -08002109 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002110 * <pre class="prettyprint">
Scott Main183bf112012-08-13 19:12:13 -07002111 * Notification noti = new Notification.Builder(mContext)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002112 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
2113 * .setContentText(subject)
2114 * .setSmallIcon(R.drawable.new_mail)
2115 * .setLargeIcon(aBitmap)
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002116 * .build();
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002117 * </pre>
Joe Onoratocb109a02011-01-18 17:57:41 -08002118 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002119 public static class Builder {
Daniel Sandler602ad1c2012-06-12 16:06:27 -04002120 private static final int MAX_ACTION_BUTTONS = 3;
Jorim Jaggi445d3c02014-08-19 22:33:42 +02002121 private static final float LARGE_TEXT_SCALE = 1.3f;
Daniel Sandler8680bf82012-05-15 16:52:52 -04002122
Joe Onorato46439ce2010-11-19 13:56:21 -08002123 private Context mContext;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002124 private Notification mN;
2125 private Bundle mUserExtras = new Bundle();
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002126 private Style mStyle;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002127 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2128 private ArrayList<String> mPersonList = new ArrayList<String>();
2129 private NotificationColorUtil mColorUtil;
2130 private boolean mColorUtilInited = false;
Christoph Studer7ac80e62014-08-04 16:01:57 +02002131
2132 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002133 * Constructs a new Builder with the defaults:
Joe Onoratocb109a02011-01-18 17:57:41 -08002134 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002135
2136 * <table>
2137 * <tr><th align=right>priority</th>
2138 * <td>{@link #PRIORITY_DEFAULT}</td></tr>
2139 * <tr><th align=right>when</th>
2140 * <td>now ({@link System#currentTimeMillis()})</td></tr>
2141 * <tr><th align=right>audio stream</th>
2142 * <td>{@link #STREAM_DEFAULT}</td></tr>
2143 * </table>
Joe Onoratocb109a02011-01-18 17:57:41 -08002144 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002145
2146 * @param context
2147 * A {@link Context} that will be used by the Builder to construct the
2148 * RemoteViews. The Context will not be held past the lifetime of this Builder
2149 * object.
Joe Onoratocb109a02011-01-18 17:57:41 -08002150 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002151 public Builder(Context context) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002152 this(context, null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002153 }
2154
Joe Onoratocb109a02011-01-18 17:57:41 -08002155 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002156 * @hide
Christoph Studer4600f9b2014-07-22 22:44:43 +02002157 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002158 public Builder(Context context, Notification toAdopt) {
2159 mContext = context;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002160
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002161 if (toAdopt == null) {
2162 mN = new Notification();
2163 mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2164 mN.priority = PRIORITY_DEFAULT;
2165 mN.visibility = VISIBILITY_PRIVATE;
2166 } else {
2167 mN = toAdopt;
2168 if (mN.actions != null) {
2169 Collections.addAll(mActions, mN.actions);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002170 }
2171
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002172 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2173 Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2174 }
2175
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002176 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2177 if (!TextUtils.isEmpty(templateClass)) {
2178 final Class<? extends Style> styleClass
2179 = getNotificationStyleClass(templateClass);
2180 if (styleClass == null) {
2181 Log.d(TAG, "Unknown style class: " + templateClass);
2182 } else {
2183 try {
2184 final Constructor<? extends Style> ctor = styleClass.getConstructor();
2185 ctor.setAccessible(true);
2186 final Style style = ctor.newInstance();
2187 style.restoreFromExtras(mN.extras);
2188
2189 if (style != null) {
2190 setStyle(style);
2191 }
2192 } catch (Throwable t) {
2193 Log.e(TAG, "Could not create Style", t);
2194 }
2195 }
2196 }
2197
2198 }
2199 }
2200
2201 private NotificationColorUtil getColorUtil() {
2202 if (!mColorUtilInited) {
2203 mColorUtilInited = true;
2204 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
2205 mColorUtil = NotificationColorUtil.getInstance(mContext);
Christoph Studer4600f9b2014-07-22 22:44:43 +02002206 }
2207 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002208 return mColorUtil;
Christoph Studer4600f9b2014-07-22 22:44:43 +02002209 }
2210
2211 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002212 * Add a timestamp pertaining to the notification (usually the time the event occurred).
Daniel Sandler0c890492012-09-12 17:23:10 -07002213 * It will be shown in the notification content view by default; use
Griff Hazen50c11652014-05-16 09:46:31 -07002214 * {@link #setShowWhen(boolean) setShowWhen} to control this.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002215 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002216 * @see Notification#when
Joe Onoratocb109a02011-01-18 17:57:41 -08002217 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002218 public Builder setWhen(long when) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002219 mN.when = when;
Joe Onorato46439ce2010-11-19 13:56:21 -08002220 return this;
2221 }
2222
Joe Onoratocb109a02011-01-18 17:57:41 -08002223 /**
Griff Hazen50c11652014-05-16 09:46:31 -07002224 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
Daniel Sandler0c890492012-09-12 17:23:10 -07002225 * in the content view.
2226 */
2227 public Builder setShowWhen(boolean show) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002228 mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
Daniel Sandler0c890492012-09-12 17:23:10 -07002229 return this;
2230 }
2231
2232 /**
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002233 * Show the {@link Notification#when} field as a stopwatch.
Joe Malin8d40d042012-11-05 11:36:40 -08002234 *
2235 * Instead of presenting <code>when</code> as a timestamp, the notification will show an
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002236 * automatically updating display of the minutes and seconds since <code>when</code>.
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002237 *
Daniel Sandlerd33b8032012-05-10 11:41:48 -04002238 * Useful when showing an elapsed time (like an ongoing phone call).
2239 *
2240 * @see android.widget.Chronometer
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002241 * @see Notification#when
2242 */
2243 public Builder setUsesChronometer(boolean b) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002244 mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
Daniel Sandlera2985ed2012-04-03 16:42:00 -04002245 return this;
2246 }
2247
2248 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002249 * Set the small icon resource, which will be used to represent the notification in the
2250 * status bar.
Joe Onoratocb109a02011-01-18 17:57:41 -08002251 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002252
2253 * The platform template for the expanded view will draw this icon in the left, unless a
2254 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2255 * icon will be moved to the right-hand side.
2256 *
2257
2258 * @param icon
2259 * A resource ID in the application's package of the drawable to use.
2260 * @see Notification#icon
Joe Onoratocb109a02011-01-18 17:57:41 -08002261 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002262 public Builder setSmallIcon(@DrawableRes int icon) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002263 return setSmallIcon(icon != 0
2264 ? Icon.createWithResource(mContext, icon)
2265 : null);
Joe Onorato46439ce2010-11-19 13:56:21 -08002266 }
2267
Joe Onoratocb109a02011-01-18 17:57:41 -08002268 /**
2269 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2270 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2271 * LevelListDrawable}.
2272 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002273 * @param icon A resource ID in the application's package of the drawable to use.
Joe Onoratocb109a02011-01-18 17:57:41 -08002274 * @param level The level to use for the icon.
2275 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002276 * @see Notification#icon
2277 * @see Notification#iconLevel
Joe Onoratocb109a02011-01-18 17:57:41 -08002278 */
Tor Norbye7b9c9122013-05-30 16:48:33 -07002279 public Builder setSmallIcon(@DrawableRes int icon, int level) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002280 mN.iconLevel = level;
Dan Sandlerd63f9322015-05-06 15:18:49 -04002281 return setSmallIcon(icon);
2282 }
2283
2284 /**
2285 * Set the small icon, which will be used to represent the notification in the
2286 * status bar and content view (unless overriden there by a
2287 * {@link #setLargeIcon(Bitmap) large icon}).
2288 *
2289 * @param icon An Icon object to use.
2290 * @see Notification#icon
2291 */
2292 public Builder setSmallIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002293 mN.setSmallIcon(icon);
2294 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
2295 mN.icon = icon.getResId();
2296 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002297 return this;
2298 }
2299
Joe Onoratocb109a02011-01-18 17:57:41 -08002300 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002301 * Set the first line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002302 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002303 public Builder setContentTitle(CharSequence title) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002304 mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
Joe Onorato46439ce2010-11-19 13:56:21 -08002305 return this;
2306 }
2307
Joe Onoratocb109a02011-01-18 17:57:41 -08002308 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002309 * Set the second line of text in the platform notification template.
Joe Onoratocb109a02011-01-18 17:57:41 -08002310 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002311 public Builder setContentText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002312 mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
Joe Onorato46439ce2010-11-19 13:56:21 -08002313 return this;
2314 }
2315
Joe Onoratocb109a02011-01-18 17:57:41 -08002316 /**
Joe Malin8d40d042012-11-05 11:36:40 -08002317 * Set the third line of text in the platform notification template.
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002318 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2319 * same location in the standard template.
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002320 */
2321 public Builder setSubText(CharSequence text) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002322 mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002323 return this;
2324 }
2325
2326 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002327 * Set the large number at the right-hand side of the notification. This is
2328 * equivalent to setContentInfo, although it might show the number in a different
2329 * font size for readability.
2330 */
Joe Onorato8595a3d2010-11-19 18:12:07 -08002331 public Builder setNumber(int number) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002332 mN.number = number;
Joe Onorato8595a3d2010-11-19 18:12:07 -08002333 return this;
2334 }
2335
Joe Onoratocb109a02011-01-18 17:57:41 -08002336 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002337 * A small piece of additional information pertaining to this notification.
2338 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002339 * The platform template will draw this on the last line of the notification, at the far
2340 * right (to the right of a smallIcon if it has been placed there).
Joe Onoratocb109a02011-01-18 17:57:41 -08002341 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002342 public Builder setContentInfo(CharSequence info) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002343 mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
Joe Onorato46439ce2010-11-19 13:56:21 -08002344 return this;
2345 }
2346
Joe Onoratocb109a02011-01-18 17:57:41 -08002347 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002348 * Set the progress this notification represents.
2349 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002350 * The platform template will represent this using a {@link ProgressBar}.
Jeff Sharkey1c400132011-08-05 14:50:13 -07002351 */
2352 public Builder setProgress(int max, int progress, boolean indeterminate) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002353 mN.extras.putInt(EXTRA_PROGRESS, progress);
2354 mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
2355 mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
Jeff Sharkey1c400132011-08-05 14:50:13 -07002356 return this;
2357 }
2358
2359 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002360 * Supply a custom RemoteViews to use instead of the platform template.
2361 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002362 * Use {@link #setCustomContentView(RemoteViews)} instead.
Joe Onoratocb109a02011-01-18 17:57:41 -08002363 */
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002364 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002365 public Builder setContent(RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002366 return setCustomContentView(views);
2367 }
2368
2369 /**
2370 * Supply custom RemoteViews to use instead of the platform template.
2371 *
2372 * This will override the layout that would otherwise be constructed by this Builder
2373 * object.
2374 */
2375 public Builder setCustomContentView(RemoteViews contentView) {
2376 mN.contentView = contentView;
2377 return this;
2378 }
2379
2380 /**
2381 * Supply custom RemoteViews to use instead of the platform template in the expanded form.
2382 *
2383 * This will override the expanded layout that would otherwise be constructed by this
2384 * Builder object.
2385 */
2386 public Builder setCustomBigContentView(RemoteViews contentView) {
2387 mN.bigContentView = contentView;
2388 return this;
2389 }
2390
2391 /**
2392 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
2393 *
2394 * This will override the heads-up layout that would otherwise be constructed by this
2395 * Builder object.
2396 */
2397 public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
2398 mN.headsUpContentView = contentView;
Joe Onorato46439ce2010-11-19 13:56:21 -08002399 return this;
2400 }
2401
Joe Onoratocb109a02011-01-18 17:57:41 -08002402 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002403 * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2404 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002405 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2406 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2407 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002408 * to assign PendingIntents to individual views in that custom layout (i.e., to create
Daniel Sandlerf3b73432012-03-27 15:01:25 -04002409 * clickable buttons inside the notification view).
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002410 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002411 * @see Notification#contentIntent Notification.contentIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002412 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002413 public Builder setContentIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002414 mN.contentIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002415 return this;
2416 }
2417
Joe Onoratocb109a02011-01-18 17:57:41 -08002418 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002419 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2420 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002421 * @see Notification#deleteIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002422 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002423 public Builder setDeleteIntent(PendingIntent intent) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002424 mN.deleteIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002425 return this;
2426 }
2427
Joe Onoratocb109a02011-01-18 17:57:41 -08002428 /**
2429 * An intent to launch instead of posting the notification to the status bar.
2430 * Only for use with extremely high-priority notifications demanding the user's
2431 * <strong>immediate</strong> attention, such as an incoming phone call or
2432 * alarm clock that the user has explicitly set to a particular time.
2433 * If this facility is used for something else, please give the user an option
2434 * to turn it off and use a normal notification, as this can be extremely
2435 * disruptive.
2436 *
Chris Wren47c20a12014-06-18 17:27:29 -04002437 * <p>
2438 * The system UI may choose to display a heads-up notification, instead of
2439 * launching this intent, while the user is using the device.
2440 * </p>
2441 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002442 * @param intent The pending intent to launch.
2443 * @param highPriority Passing true will cause this notification to be sent
2444 * even if other notifications are suppressed.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002445 *
2446 * @see Notification#fullScreenIntent
Joe Onoratocb109a02011-01-18 17:57:41 -08002447 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002448 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002449 mN.fullScreenIntent = intent;
Joe Onorato46439ce2010-11-19 13:56:21 -08002450 setFlag(FLAG_HIGH_PRIORITY, highPriority);
2451 return this;
2452 }
2453
Joe Onoratocb109a02011-01-18 17:57:41 -08002454 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002455 * Set the "ticker" text which is sent to accessibility services.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002456 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002457 * @see Notification#tickerText
Joe Onoratocb109a02011-01-18 17:57:41 -08002458 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002459 public Builder setTicker(CharSequence tickerText) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002460 mN.tickerText = safeCharSequence(tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -08002461 return this;
2462 }
2463
Joe Onoratocb109a02011-01-18 17:57:41 -08002464 /**
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002465 * Obsolete version of {@link #setTicker(CharSequence)}.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002466 *
Joe Onoratocb109a02011-01-18 17:57:41 -08002467 */
Dan Sandler5fcdf6e2014-07-18 11:31:15 -04002468 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002469 public Builder setTicker(CharSequence tickerText, RemoteViews views) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002470 setTicker(tickerText);
2471 // views is ignored
Joe Onorato46439ce2010-11-19 13:56:21 -08002472 return this;
2473 }
2474
Joe Onoratocb109a02011-01-18 17:57:41 -08002475 /**
Dan Sandlerd63f9322015-05-06 15:18:49 -04002476 * Add a large icon to the notification content view.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002477 *
2478 * In the platform template, this image will be shown on the left of the notification view
Dan Sandlerd63f9322015-05-06 15:18:49 -04002479 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2480 * badge atop the large icon).
Dan Sandler08a04c12015-05-06 15:18:49 -04002481 */
Dan Sandlerd63f9322015-05-06 15:18:49 -04002482 public Builder setLargeIcon(Bitmap b) {
2483 return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
2484 }
2485
2486 /**
2487 * Add a large icon to the notification content view.
2488 *
2489 * In the platform template, this image will be shown on the left of the notification view
2490 * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
2491 * badge atop the large icon).
2492 */
2493 public Builder setLargeIcon(Icon icon) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002494 mN.mLargeIcon = icon;
2495 mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
Joe Onorato46439ce2010-11-19 13:56:21 -08002496 return this;
2497 }
2498
Joe Onoratocb109a02011-01-18 17:57:41 -08002499 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002500 * Set the sound to play.
2501 *
John Spurlockc0650f022014-07-19 13:22:39 -04002502 * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2503 * for notifications.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002504 *
Chris Wren47c20a12014-06-18 17:27:29 -04002505 * <p>
2506 * A notification that is noisy is more likely to be presented as a heads-up notification.
2507 * </p>
2508 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002509 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002510 */
Joe Onorato52f80cd2010-11-21 15:34:48 -08002511 public Builder setSound(Uri sound) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002512 mN.sound = sound;
2513 mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
Joe Onorato52f80cd2010-11-21 15:34:48 -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, along with a specific stream on which to play it.
Joe Onoratocb109a02011-01-18 17:57:41 -08002519 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002520 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2521 *
Chris Wren47c20a12014-06-18 17:27:29 -04002522 * <p>
2523 * A notification that is noisy is more likely to be presented as a heads-up notification.
2524 * </p>
John Spurlockc0650f022014-07-19 13:22:39 -04002525 * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002526 * @see Notification#sound
Joe Onoratocb109a02011-01-18 17:57:41 -08002527 */
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002528 @Deprecated
Joe Onorato46439ce2010-11-19 13:56:21 -08002529 public Builder setSound(Uri sound, int streamType) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002530 mN.sound = sound;
2531 mN.audioStreamType = streamType;
Joe Onorato46439ce2010-11-19 13:56:21 -08002532 return this;
2533 }
2534
Joe Onoratocb109a02011-01-18 17:57:41 -08002535 /**
John Spurlockc0650f022014-07-19 13:22:39 -04002536 * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2537 * use during playback.
2538 *
2539 * <p>
2540 * A notification that is noisy is more likely to be presented as a heads-up notification.
2541 * </p>
2542 *
2543 * @see Notification#sound
2544 */
2545 public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002546 mN.sound = sound;
2547 mN.audioAttributes = audioAttributes;
John Spurlockc0650f022014-07-19 13:22:39 -04002548 return this;
2549 }
2550
2551 /**
Joe Onoratocb109a02011-01-18 17:57:41 -08002552 * Set the vibration pattern to use.
2553 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002554 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2555 * <code>pattern</code> parameter.
2556 *
Chris Wren47c20a12014-06-18 17:27:29 -04002557 * <p>
2558 * A notification that vibrates is more likely to be presented as a heads-up notification.
2559 * </p>
2560 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002561 * @see Notification#vibrate
Joe Onoratocb109a02011-01-18 17:57:41 -08002562 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002563 public Builder setVibrate(long[] pattern) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002564 mN.vibrate = pattern;
Joe Onorato46439ce2010-11-19 13:56:21 -08002565 return this;
2566 }
2567
Joe Onoratocb109a02011-01-18 17:57:41 -08002568 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002569 * Set the desired color for the indicator LED on the device, as well as the
2570 * blink duty cycle (specified in milliseconds).
2571 *
2572
2573 * Not all devices will honor all (or even any) of these values.
2574 *
2575
2576 * @see Notification#ledARGB
2577 * @see Notification#ledOnMS
2578 * @see Notification#ledOffMS
Joe Onoratocb109a02011-01-18 17:57:41 -08002579 */
Tor Norbye80756e32015-03-02 09:39:27 -08002580 public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002581 mN.ledARGB = argb;
2582 mN.ledOnMS = onMs;
2583 mN.ledOffMS = offMs;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002584 if (onMs != 0 || offMs != 0) {
2585 mN.flags |= FLAG_SHOW_LIGHTS;
2586 }
2587 if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
2588 mN.flags |= FLAG_SHOW_LIGHTS;
2589 }
Joe Onorato46439ce2010-11-19 13:56:21 -08002590 return this;
2591 }
2592
Joe Onoratocb109a02011-01-18 17:57:41 -08002593 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002594 * Set whether this is an "ongoing" notification.
Joe Onoratocb109a02011-01-18 17:57:41 -08002595 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002596
2597 * Ongoing notifications cannot be dismissed by the user, so your application or service
2598 * must take care of canceling them.
2599 *
2600
2601 * They are typically used to indicate a background task that the user is actively engaged
2602 * with (e.g., playing music) or is pending in some way and therefore occupying the device
2603 * (e.g., a file download, sync operation, active network connection).
2604 *
2605
2606 * @see Notification#FLAG_ONGOING_EVENT
2607 * @see Service#setForeground(boolean)
Joe Onoratocb109a02011-01-18 17:57:41 -08002608 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002609 public Builder setOngoing(boolean ongoing) {
2610 setFlag(FLAG_ONGOING_EVENT, ongoing);
2611 return this;
2612 }
2613
Joe Onoratocb109a02011-01-18 17:57:41 -08002614 /**
2615 * Set this flag if you would only like the sound, vibrate
2616 * and ticker to be played if the notification is not already showing.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002617 *
2618 * @see Notification#FLAG_ONLY_ALERT_ONCE
Joe Onoratocb109a02011-01-18 17:57:41 -08002619 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002620 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2621 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2622 return this;
2623 }
2624
Joe Onoratocb109a02011-01-18 17:57:41 -08002625 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002626 * Make this notification automatically dismissed when the user touches it. The
2627 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2628 *
2629 * @see Notification#FLAG_AUTO_CANCEL
Joe Onoratocb109a02011-01-18 17:57:41 -08002630 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002631 public Builder setAutoCancel(boolean autoCancel) {
Joe Onorato281d83f2011-01-04 17:13:10 -08002632 setFlag(FLAG_AUTO_CANCEL, autoCancel);
Joe Onorato46439ce2010-11-19 13:56:21 -08002633 return this;
2634 }
2635
Joe Onoratocb109a02011-01-18 17:57:41 -08002636 /**
Griff Hazendfcb0802014-02-11 12:00:00 -08002637 * Set whether or not this notification should not bridge to other devices.
2638 *
2639 * <p>Some notifications can be bridged to other devices for remote display.
2640 * This hint can be set to recommend this notification not be bridged.
2641 */
2642 public Builder setLocalOnly(boolean localOnly) {
2643 setFlag(FLAG_LOCAL_ONLY, localOnly);
2644 return this;
2645 }
2646
2647 /**
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002648 * Set which notification properties will be inherited from system defaults.
Joe Onoratocb109a02011-01-18 17:57:41 -08002649 * <p>
2650 * The value should be one or more of the following fields combined with
2651 * bitwise-or:
2652 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2653 * <p>
2654 * For all default values, use {@link #DEFAULT_ALL}.
2655 */
Joe Onorato46439ce2010-11-19 13:56:21 -08002656 public Builder setDefaults(int defaults) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002657 mN.defaults = defaults;
Joe Onorato46439ce2010-11-19 13:56:21 -08002658 return this;
2659 }
2660
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002661 /**
2662 * Set the priority of this notification.
2663 *
2664 * @see Notification#priority
2665 */
Tor Norbyed9273d62013-05-30 15:59:53 -07002666 public Builder setPriority(@Priority int pri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002667 mN.priority = pri;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002668 return this;
2669 }
Joe Malin8d40d042012-11-05 11:36:40 -08002670
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002671 /**
John Spurlockfd7f1e02014-03-18 16:41:57 -04002672 * Set the notification category.
Joe Malin8d40d042012-11-05 11:36:40 -08002673 *
John Spurlockfd7f1e02014-03-18 16:41:57 -04002674 * @see Notification#category
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002675 */
John Spurlockfd7f1e02014-03-18 16:41:57 -04002676 public Builder setCategory(String category) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002677 mN.category = category;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002678 return this;
2679 }
2680
2681 /**
Chris Wrendde75302014-03-26 17:24:15 -04002682 * Add a person that is relevant to this notification.
2683 *
Chris Wrene6c48932014-09-29 17:19:27 -04002684 * <P>
2685 * Depending on user preferences, this annotation may allow the notification to pass
2686 * through interruption filters, and to appear more prominently in the user interface.
2687 * </P>
2688 *
2689 * <P>
2690 * The person should be specified by the {@code String} representation of a
2691 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2692 * </P>
2693 *
2694 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2695 * URIs. The path part of these URIs must exist in the contacts database, in the
2696 * appropriate column, or the reference will be discarded as invalid. Telephone schema
2697 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2698 * </P>
2699 *
2700 * @param uri A URI for the person.
Chris Wrendde75302014-03-26 17:24:15 -04002701 * @see Notification#EXTRA_PEOPLE
2702 */
Chris Wrene6c48932014-09-29 17:19:27 -04002703 public Builder addPerson(String uri) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002704 mPersonList.add(uri);
Chris Wrendde75302014-03-26 17:24:15 -04002705 return this;
2706 }
2707
2708 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002709 * Set this notification to be part of a group of notifications sharing the same key.
2710 * Grouped notifications may display in a cluster or stack on devices which
2711 * support such rendering.
2712 *
2713 * <p>To make this notification the summary for its group, also call
2714 * {@link #setGroupSummary}. A sort order can be specified for group members by using
2715 * {@link #setSortKey}.
2716 * @param groupKey The group key of the group.
2717 * @return this object for method chaining
2718 */
2719 public Builder setGroup(String groupKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002720 mN.mGroupKey = groupKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002721 return this;
2722 }
2723
2724 /**
2725 * Set this notification to be the group summary for a group of notifications.
2726 * Grouped notifications may display in a cluster or stack on devices which
2727 * support such rendering. Requires a group key also be set using {@link #setGroup}.
2728 * @param isGroupSummary Whether this notification should be a group summary.
2729 * @return this object for method chaining
2730 */
2731 public Builder setGroupSummary(boolean isGroupSummary) {
2732 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2733 return this;
2734 }
2735
2736 /**
2737 * Set a sort key that orders this notification among other notifications from the
2738 * same package. This can be useful if an external sort was already applied and an app
2739 * would like to preserve this. Notifications will be sorted lexicographically using this
2740 * value, although providing different priorities in addition to providing sort key may
2741 * cause this value to be ignored.
2742 *
2743 * <p>This sort key can also be used to order members of a notification group. See
Griff Hazen9e1379f2014-05-20 12:50:51 -07002744 * {@link #setGroup}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002745 *
2746 * @see String#compareTo(String)
2747 */
2748 public Builder setSortKey(String sortKey) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002749 mN.mSortKey = sortKey;
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002750 return this;
2751 }
2752
2753 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002754 * Merge additional metadata into this notification.
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002755 *
Griff Hazen720042b2014-02-24 15:46:56 -08002756 * <p>Values within the Bundle will replace existing extras values in this Builder.
2757 *
2758 * @see Notification#extras
2759 */
Griff Hazen959591e2014-05-15 22:26:18 -07002760 public Builder addExtras(Bundle extras) {
2761 if (extras != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002762 mUserExtras.putAll(extras);
Griff Hazen720042b2014-02-24 15:46:56 -08002763 }
2764 return this;
2765 }
2766
2767 /**
2768 * Set metadata for this notification.
2769 *
2770 * <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 -04002771 * current contents are copied into the Notification each time {@link #build()} is
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002772 * called.
2773 *
Griff Hazen720042b2014-02-24 15:46:56 -08002774 * <p>Replaces any existing extras values with those from the provided Bundle.
2775 * Use {@link #addExtras} to merge in metadata instead.
2776 *
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002777 * @see Notification#extras
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002778 */
Griff Hazen959591e2014-05-15 22:26:18 -07002779 public Builder setExtras(Bundle extras) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002780 if (extras != null) {
2781 mUserExtras = extras;
2782 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05002783 return this;
2784 }
2785
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002786 /**
Griff Hazen720042b2014-02-24 15:46:56 -08002787 * Get the current metadata Bundle used by this notification Builder.
2788 *
2789 * <p>The returned Bundle is shared with this Builder.
2790 *
2791 * <p>The current contents of this Bundle are copied into the Notification each time
2792 * {@link #build()} is called.
2793 *
2794 * @see Notification#extras
2795 */
2796 public Bundle getExtras() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002797 return mUserExtras;
2798 }
2799
2800 private Bundle getAllExtras() {
2801 final Bundle saveExtras = (Bundle) mUserExtras.clone();
2802 saveExtras.putAll(mN.extras);
2803 return saveExtras;
Griff Hazen720042b2014-02-24 15:46:56 -08002804 }
2805
2806 /**
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002807 * Add an action to this notification. Actions are typically displayed by
2808 * the system as a button adjacent to the notification content.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04002809 * <p>
2810 * Every action must have an icon (32dp square and matching the
2811 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2812 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2813 * <p>
2814 * A notification in its expanded form can display up to 3 actions, from left to right in
2815 * the order they were added. Actions will not be displayed when the notification is
2816 * collapsed, however, so be sure that any essential functions may be accessed by the user
2817 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002818 *
2819 * @param icon Resource ID of a drawable that represents the action.
2820 * @param title Text describing the action.
2821 * @param intent PendingIntent to be fired when the action is invoked.
Dan Sandler86647982015-05-13 23:41:13 -04002822 *
2823 * @deprecated Use {@link #addAction(Action)} instead.
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002824 */
Dan Sandler86647982015-05-13 23:41:13 -04002825 @Deprecated
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002826 public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04002827 mActions.add(new Action(icon, safeCharSequence(title), intent));
Daniel Sandlera0a938c2012-03-15 08:42:37 -04002828 return this;
2829 }
2830
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002831 /**
Griff Hazen959591e2014-05-15 22:26:18 -07002832 * Add an action to this notification. Actions are typically displayed by
2833 * the system as a button adjacent to the notification content.
2834 * <p>
2835 * Every action must have an icon (32dp square and matching the
2836 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2837 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2838 * <p>
2839 * A notification in its expanded form can display up to 3 actions, from left to right in
2840 * the order they were added. Actions will not be displayed when the notification is
2841 * collapsed, however, so be sure that any essential functions may be accessed by the user
2842 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2843 *
2844 * @param action The action to add.
2845 */
2846 public Builder addAction(Action action) {
2847 mActions.add(action);
2848 return this;
2849 }
2850
2851 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002852 * Alter the complete list of actions attached to this notification.
2853 * @see #addAction(Action).
2854 *
2855 * @param actions
2856 * @return
2857 */
2858 public Builder setActions(Action... actions) {
2859 mActions.clear();
2860 for (int i = 0; i < actions.length; i++) {
2861 mActions.add(actions[i]);
2862 }
2863 return this;
2864 }
2865
2866 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002867 * Add a rich notification style to be applied at build time.
2868 *
2869 * @param style Object responsible for modifying the notification style.
2870 */
2871 public Builder setStyle(Style style) {
2872 if (mStyle != style) {
2873 mStyle = style;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002874 if (mStyle != null) {
2875 mStyle.setBuilder(this);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002876 mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
2877 } else {
2878 mN.extras.remove(EXTRA_TEMPLATE);
Daniel Sandlerc08dea22012-06-28 08:35:24 -07002879 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04002880 }
2881 return this;
2882 }
2883
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002884 /**
2885 * Specify the value of {@link #visibility}.
Griff Hazenb720abe2014-05-20 13:15:30 -07002886 *
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002887 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2888 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2889 *
2890 * @return The same Builder.
2891 */
2892 public Builder setVisibility(int visibility) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002893 mN.visibility = visibility;
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002894 return this;
2895 }
2896
2897 /**
2898 * Supply a replacement Notification whose contents should be shown in insecure contexts
2899 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2900 * @param n A replacement notification, presumably with some or all info redacted.
2901 * @return The same Builder.
2902 */
2903 public Builder setPublicVersion(Notification n) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002904 if (n != null) {
2905 mN.publicVersion = new Notification();
2906 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
2907 } else {
2908 mN.publicVersion = null;
2909 }
Dan Sandler0bf2ed82013-12-21 23:33:41 -06002910 return this;
2911 }
2912
Griff Hazenb720abe2014-05-20 13:15:30 -07002913 /**
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002914 * Apply an extender to this notification builder. Extenders may be used to add
2915 * metadata or change options on this builder.
2916 */
Griff Hazen61a9e862014-05-22 16:05:19 -07002917 public Builder extend(Extender extender) {
2918 extender.extend(this);
Griff Hazen5cadc3b2014-05-20 09:55:39 -07002919 return this;
2920 }
2921
Dan Sandler4e787062015-06-17 15:09:48 -04002922 /**
2923 * @hide
2924 */
2925 public void setFlag(int mask, boolean value) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002926 if (value) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002927 mN.flags |= mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002928 } else {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002929 mN.flags &= ~mask;
Joe Onorato46439ce2010-11-19 13:56:21 -08002930 }
2931 }
2932
Dan Sandler26e81cf2014-05-06 10:01:27 -04002933 /**
2934 * Sets {@link Notification#color}.
2935 *
2936 * @param argb The accent color to use
2937 *
2938 * @return The same Builder.
2939 */
Tor Norbye80756e32015-03-02 09:39:27 -08002940 public Builder setColor(@ColorInt int argb) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04002941 mN.color = argb;
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05002942 sanitizeColor();
Dan Sandler26e81cf2014-05-06 10:01:27 -04002943 return this;
2944 }
2945
Julia Reynolds74303cf2015-10-16 11:37:55 -04002946 /**
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002947 * Sets the topic of this notification. Topics are typically displayed in Notification
Julia Reynolds74303cf2015-10-16 11:37:55 -04002948 * settings.
2949 * <p>
2950 * Every topic must have an id and a textual label.
2951 *
2952 * @param topic The topic to add.
2953 */
Julia Reynolds054c5dc2015-11-17 15:36:30 -05002954 public Builder setTopic(Topic topic) {
2955 mN.topic = topic;
Julia Reynolds74303cf2015-10-16 11:37:55 -04002956 return this;
2957 }
2958
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002959 private Drawable getProfileBadgeDrawable() {
Christoph Studer7ac80e62014-08-04 16:01:57 +02002960 // Note: This assumes that the current user can read the profile badge of the
2961 // originating user.
Svetoslavc7d62f02014-09-04 15:39:54 -07002962 return mContext.getPackageManager().getUserBadgeForDensity(
Julia Reynoldsda303542015-11-23 14:00:20 -05002963 new UserHandle(mContext.getUserId()), 0);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002964 }
2965
2966 private Bitmap getProfileBadge() {
2967 Drawable badge = getProfileBadgeDrawable();
Kenny Guy8a0101b2014-05-08 23:34:12 +01002968 if (badge == null) {
2969 return null;
2970 }
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002971 final int size = mContext.getResources().getDimensionPixelSize(
2972 R.dimen.notification_badge_size);
2973 Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002974 Canvas canvas = new Canvas(bitmap);
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02002975 badge.setBounds(0, 0, size, size);
Kenny Guy8a0101b2014-05-08 23:34:12 +01002976 badge.draw(canvas);
2977 return bitmap;
2978 }
2979
Kenny Guy98193ea2014-07-24 19:54:37 +01002980 private boolean addProfileBadge(RemoteViews contentView, int resId) {
2981 Bitmap profileBadge = getProfileBadge();
2982
2983 contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
Kenny Guy98193ea2014-07-24 19:54:37 +01002984 contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2985
2986 if (profileBadge != null) {
2987 contentView.setImageViewBitmap(resId, profileBadge);
2988 contentView.setViewVisibility(resId, View.VISIBLE);
2989
2990 // Make sure Line 3 is visible. As badge will be here if there
2991 // is no text to display.
2992 if (resId == R.id.profile_badge_line3) {
2993 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2994 }
2995 return true;
2996 }
2997 return false;
2998 }
2999
Christoph Studerfe718432014-09-01 18:21:18 +02003000 private void resetStandardTemplate(RemoteViews contentView) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003001 resetNotificationHeader(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003002 resetContentMargins(contentView);
Christoph Studerfe718432014-09-01 18:21:18 +02003003 contentView.setViewVisibility(R.id.right_icon, View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003004 contentView.setViewVisibility(R.id.title, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003005 contentView.setTextViewText(R.id.title, null);
3006 contentView.setTextViewText(R.id.text, null);
Christoph Studerfe718432014-09-01 18:21:18 +02003007 contentView.setViewVisibility(R.id.line3, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003008 contentView.setViewVisibility(R.id.text_line_1, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003009 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003010 }
3011
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003012 /**
3013 * Resets the notification header to its original state
3014 */
3015 private void resetNotificationHeader(RemoteViews contentView) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003016 contentView.setImageViewResource(R.id.icon, 0);
Selim Cinek7b836392015-12-04 20:02:59 -08003017 contentView.setBoolean(R.id.notification_header, "setExpanded", false);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003018 contentView.setTextViewText(R.id.app_name_text, null);
Christoph Studerca1db712014-09-10 17:31:33 +02003019 contentView.setViewVisibility(R.id.chronometer, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003020 contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003021 contentView.setViewVisibility(R.id.header_content_info, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003022 contentView.setViewVisibility(R.id.number_of_children, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003023 contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
3024 contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
3025 contentView.setViewVisibility(R.id.time_divider, View.GONE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003026 }
3027
3028 private void resetContentMargins(RemoteViews contentView) {
3029 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003030 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Christoph Studerfe718432014-09-01 18:21:18 +02003031 }
3032
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003033 private RemoteViews applyStandardTemplate(int resId) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003034 return applyStandardTemplate(resId, true /* hasProgress */);
3035 }
3036
3037 /**
3038 * @param hasProgress whether the progress bar should be shown and set
3039 */
3040 private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
Kenny Guy77320062014-08-27 21:37:15 +01003041 RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
Dan Sandler539aad42014-08-04 00:43:39 -04003042
Christoph Studerfe718432014-09-01 18:21:18 +02003043 resetStandardTemplate(contentView);
3044
Daniel Sandler9f7936a2012-05-21 16:14:28 -04003045 boolean showLine3 = false;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003046 final Bundle ex = mN.extras;
Dan Sandler190d58d2014-05-15 09:33:39 -04003047
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003048 bindNotificationHeader(contentView);
3049 bindLargeIcon(contentView);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003050 if (ex.getCharSequence(EXTRA_TITLE) != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -08003051 contentView.setViewVisibility(R.id.title, View.VISIBLE);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003052 contentView.setTextViewText(R.id.title,
3053 processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
Joe Onorato561d3852010-11-20 18:09:34 -08003054 }
Selim Cinek29603462015-11-17 19:04:39 -08003055 boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003056 if (ex.getCharSequence(EXTRA_TEXT) != null) {
Selim Cinek29603462015-11-17 19:04:39 -08003057 contentView.setTextViewText(showProgress ? R.id.text_line_1 : R.id.text,
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003058 processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
Selim Cinek29603462015-11-17 19:04:39 -08003059 if (showProgress) {
3060 contentView.setViewVisibility(R.id.text_line_1, View.VISIBLE);
Joe Onorato059a2f82011-01-04 10:27:01 -08003061 }
Selim Cinek29603462015-11-17 19:04:39 -08003062 showLine3 = !showProgress;
Joe Onorato561d3852010-11-20 18:09:34 -08003063 }
Selim Cinek29603462015-11-17 19:04:39 -08003064 // We want to add badge to first line of text.
3065 if (addProfileBadge(contentView, R.id.profile_badge_line3)) {
3066 showLine3 = true;
3067 }
3068 // Note getStandardView may hide line 3 again.
3069 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
Selim Cinek860b6da2015-12-16 19:02:19 -08003070 setContentMinHeight(contentView, showProgress || mN.mLargeIcon != null);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003071
Selim Cinek29603462015-11-17 19:04:39 -08003072 return contentView;
3073 }
3074
Selim Cinek860b6da2015-12-16 19:02:19 -08003075 /**
3076 * @param remoteView the remote view to update the minheight in
3077 * @param hasMinHeight does it have a mimHeight
3078 * @hide
3079 */
3080 void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
3081 int minHeight = 0;
3082 if (hasMinHeight) {
3083 // we need to set the minHeight of the notification
3084 minHeight = mContext.getResources().getDimensionPixelSize(
3085 com.android.internal.R.dimen.notification_min_content_height);
3086 }
3087 remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
3088 }
3089
Selim Cinek29603462015-11-17 19:04:39 -08003090 private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003091 final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
3092 final int progress = ex.getInt(EXTRA_PROGRESS, 0);
3093 final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3094 if (hasProgress && (max != 0 || ind)) {
Selim Cinek29603462015-11-17 19:04:39 -08003095 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003096 contentView.setProgressBar(
Selim Cinek29603462015-11-17 19:04:39 -08003097 R.id.progress, max, progress, ind);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003098 contentView.setProgressBackgroundTintList(
3099 R.id.progress, ColorStateList.valueOf(mContext.getColor(
3100 R.color.notification_progress_background_color)));
Selim Cinek29603462015-11-17 19:04:39 -08003101 if (mN.color != COLOR_DEFAULT) {
3102 ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003103 contentView.setProgressTintList(R.id.progress, colorStateList);
3104 contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003105 }
Selim Cinek29603462015-11-17 19:04:39 -08003106 return true;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003107 } else {
3108 contentView.setViewVisibility(R.id.progress, View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -08003109 return false;
Jeff Sharkey1c400132011-08-05 14:50:13 -07003110 }
Joe Onorato561d3852010-11-20 18:09:34 -08003111 }
3112
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003113 private void bindLargeIcon(RemoteViews contentView) {
3114 if (mN.mLargeIcon != null) {
3115 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
3116 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
3117 processLargeLegacyIcon(mN.mLargeIcon, contentView);
3118 int endMargin = mContext.getResources().getDimensionPixelSize(
3119 R.dimen.notification_content_picture_margin);
3120 contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003121 contentView.setViewLayoutMarginEnd(R.id.line3, endMargin);
Selim Cinek29603462015-11-17 19:04:39 -08003122 contentView.setViewLayoutMarginEnd(R.id.progress, endMargin);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003123 }
3124 }
3125
3126 private void bindNotificationHeader(RemoteViews contentView) {
3127 bindSmallIcon(contentView);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003128 bindChildCountColor(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003129 bindHeaderAppName(contentView);
Selim Cinek03d0d652015-11-13 13:18:09 -05003130 bindHeaderSubText(contentView);
Selim Cinek29603462015-11-17 19:04:39 -08003131 bindContentInfo(contentView);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003132 bindHeaderChronometerAndTime(contentView);
3133 bindExpandButton(contentView);
3134 }
3135
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003136 private void bindChildCountColor(RemoteViews contentView) {
3137 contentView.setTextColor(R.id.number_of_children, resolveColor());
3138 }
3139
Selim Cinek29603462015-11-17 19:04:39 -08003140 private void bindContentInfo(RemoteViews contentView) {
3141 boolean visible = false;
3142 if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
3143 contentView.setTextViewText(R.id.header_content_info,
3144 processLegacyText(mN.extras.getCharSequence(EXTRA_INFO_TEXT)));
3145 contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
3146 visible = true;
3147 } else if (mN.number > 0) {
3148 final int tooBig = mContext.getResources().getInteger(
3149 R.integer.status_bar_notification_info_maxnum);
3150 if (mN.number > tooBig) {
3151 contentView.setTextViewText(R.id.header_content_info, processLegacyText(
3152 mContext.getResources().getString(
3153 R.string.status_bar_notification_info_overflow)));
3154 } else {
3155 contentView.setTextViewText(R.id.header_content_info,
3156 processLegacyText(String.valueOf(mN.number)));
3157 }
3158 contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
3159 visible = true;
3160 }
3161 if (visible) {
3162 contentView.setViewVisibility(R.id.content_info_divider, View.VISIBLE);
3163 }
3164 }
3165
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003166 private void bindExpandButton(RemoteViews contentView) {
3167 contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
3168 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003169 contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
3170 resolveColor());
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003171 }
3172
3173 private void bindHeaderChronometerAndTime(RemoteViews contentView) {
3174 if (showsTimeOrChronometer()) {
Selim Cinek29603462015-11-17 19:04:39 -08003175 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003176 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
3177 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
3178 contentView.setLong(R.id.chronometer, "setBase",
3179 mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
3180 contentView.setBoolean(R.id.chronometer, "setStarted", true);
3181 } else {
3182 contentView.setViewVisibility(R.id.time, View.VISIBLE);
3183 contentView.setLong(R.id.time, "setTime", mN.when);
3184 }
3185 }
3186 }
3187
Selim Cinek03d0d652015-11-13 13:18:09 -05003188 private void bindHeaderSubText(RemoteViews contentView) {
3189 CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
3190 if (subText == null && mStyle != null && mStyle.mSummaryTextSet
3191 && mStyle.hasSummaryInHeader()) {
3192 subText = mStyle.mSummaryText;
3193 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003194 if (subText != null) {
3195 // TODO: Remove the span entirely to only have the string with propper formating.
3196 contentView.setTextViewText(R.id.header_sub_text, processLegacyText(subText));
3197 contentView.setViewVisibility(R.id.header_sub_text, View.VISIBLE);
Selim Cinek29603462015-11-17 19:04:39 -08003198 contentView.setViewVisibility(R.id.sub_text_divider, View.VISIBLE);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003199 }
3200 }
3201
3202 private void bindHeaderAppName(RemoteViews contentView) {
3203 PackageManager packageManager = mContext.getPackageManager();
3204 ApplicationInfo info = null;
3205 try {
3206 info = packageManager.getApplicationInfo(mContext.getApplicationInfo().packageName,
3207 0);
3208 } catch (final NameNotFoundException e) {
3209 return;
3210 }
3211 CharSequence appName = info != null ? packageManager.getApplicationLabel(info)
3212 : null;
3213 if (TextUtils.isEmpty(appName)) {
3214 return;
3215 }
3216 contentView.setTextViewText(R.id.app_name_text, appName);
3217 }
3218
3219 private void bindSmallIcon(RemoteViews contentView) {
3220 contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
3221 processSmallIconColor(mN.mSmallIcon, contentView);
3222 }
3223
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003224 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003225 * @return true if the built notification will show the time or the chronometer; false
3226 * otherwise
3227 */
3228 private boolean showsTimeOrChronometer() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003229 return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003230 }
3231
Christoph Studerfe718432014-09-01 18:21:18 +02003232 private void resetStandardTemplateWithActions(RemoteViews big) {
3233 big.setViewVisibility(R.id.actions, View.GONE);
Christoph Studerfe718432014-09-01 18:21:18 +02003234 big.removeAllViews(R.id.actions);
3235 }
3236
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003237 private RemoteViews applyStandardTemplateWithActions(int layoutId) {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003238 RemoteViews big = applyStandardTemplate(layoutId);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003239
Christoph Studerfe718432014-09-01 18:21:18 +02003240 resetStandardTemplateWithActions(big);
3241
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003242 int N = mActions.size();
3243 if (N > 0) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003244 big.setViewVisibility(R.id.actions, View.VISIBLE);
Daniel 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() {
Selim Cinek850a8542015-11-11 11:48:36 -05003276 RemoteViews result = null;
Julia Reynolds089e3e42015-11-18 09:59:57 -05003277 if (mN.bigContentView != null) {
3278 return mN.bigContentView;
3279 } else if (mStyle != null) {
Selim Cinek850a8542015-11-11 11:48:36 -05003280 result = mStyle.makeBigContentView();
Julia Reynolds089e3e42015-11-18 09:59:57 -05003281 } else if (mActions.size() == 0) {
3282 return null;
3283 }
Selim Cinek850a8542015-11-11 11:48:36 -05003284 if (result == null) {
3285 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
Selim Cinek90dcf6d2015-11-18 20:24:13 -08003286 } else {
3287 hideLine1Text(result);
Selim Cinek850a8542015-11-11 11:48:36 -05003288 }
3289 adaptNotificationHeaderForBigContentView(result);
3290 return result;
3291 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003292
Selim Cinekeaa29ca2015-11-23 13:51:13 -08003293 /**
3294 * Construct a RemoteViews for the final notification header only
3295 *
3296 * @hide
3297 */
3298 public RemoteViews makeNotificationHeader() {
3299 RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
3300 R.layout.notification_template_header);
3301 resetNotificationHeader(header);
3302 bindNotificationHeader(header);
3303 return header;
3304 }
3305
Selim Cinek29603462015-11-17 19:04:39 -08003306 private void hideLine1Text(RemoteViews result) {
3307 result.setViewVisibility(R.id.text_line_1, View.GONE);
3308 }
3309
Selim Cinek850a8542015-11-11 11:48:36 -05003310 private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
Selim Cinek7b836392015-12-04 20:02:59 -08003311 result.setBoolean(R.id.notification_header, "setExpanded", true);
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003312 }
3313
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003314 /**
3315 * Construct a RemoteViews for the final heads-up notification layout.
3316 */
3317 public RemoteViews makeHeadsUpContentView() {
Julia Reynolds089e3e42015-11-18 09:59:57 -05003318 if (mN.headsUpContentView != null) {
3319 return mN.headsUpContentView;
3320 } else if (mStyle != null) {
3321 final RemoteViews styleView = mStyle.makeHeadsUpContentView();
3322 if (styleView != null) {
3323 return styleView;
3324 }
3325 } else if (mActions.size() == 0) {
3326 return null;
3327 }
3328
Chris Wren8fd39ec2014-02-27 17:43:26 -05003329
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003330 return applyStandardTemplateWithActions(getBigBaseLayoutResource());
Chris Wren8fd39ec2014-02-27 17:43:26 -05003331 }
3332
Selim Cinek624c02db2015-12-14 21:00:02 -08003333 /**
3334 * Construct a RemoteViews for the display in public contexts like on the lockscreen.
3335 *
3336 * @hide
3337 */
3338 public RemoteViews makePublicContentView() {
3339 if (mN.publicVersion != null) {
3340 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
3341 return builder.makeContentView();
3342 }
3343 Bundle savedBundle = mN.extras;
3344 Style style = mStyle;
3345 mStyle = null;
3346 Icon largeIcon = mN.mLargeIcon;
3347 mN.mLargeIcon = null;
3348 Bundle publicExtras = new Bundle();
3349 publicExtras.putBoolean(EXTRA_SHOW_WHEN,
3350 savedBundle.getBoolean(EXTRA_SHOW_WHEN));
3351 publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
3352 savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
3353 publicExtras.putCharSequence(EXTRA_TITLE,
3354 mContext.getString(R.string.notification_hidden_text));
3355 mN.extras = publicExtras;
3356 final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
3357 mN.extras = savedBundle;
3358 mN.mLargeIcon = largeIcon;
3359 mStyle = style;
3360 return publicView;
3361 }
3362
3363
Chris Wren8fd39ec2014-02-27 17:43:26 -05003364
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003365 private RemoteViews generateActionButton(Action action) {
Daniel Sandler8680bf82012-05-15 16:52:52 -04003366 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07003367 RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003368 tombstone ? getActionTombstoneLayoutResource()
3369 : getActionLayoutResource());
Dan Sandler68079d52015-07-22 10:45:30 -04003370 final Icon ai = action.getIcon();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003371 button.setTextViewText(R.id.action0, processLegacyText(action.title));
Daniel Sandler8680bf82012-05-15 16:52:52 -04003372 if (!tombstone) {
Daniel Sandlere5518842012-05-10 16:20:40 -04003373 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
Daniel Sandlere5518842012-05-10 16:20:40 -04003374 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003375 button.setContentDescription(R.id.action0, action.title);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08003376 if (action.mRemoteInputs != null) {
3377 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
3378 }
3379 if (mN.color != COLOR_DEFAULT) {
3380 button.setTextColor(R.id.action0, mN.color);
3381 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -04003382 return button;
3383 }
3384
Joe Onoratocb109a02011-01-18 17:57:41 -08003385 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003386 * @return Whether we are currently building a notification from a legacy (an app that
Alan Viverette3cb07a462014-06-06 14:19:53 -07003387 * doesn't create material notifications by itself) app.
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003388 */
3389 private boolean isLegacy() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003390 return getColorUtil() != null;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003391 }
3392
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003393 private CharSequence processLegacyText(CharSequence charSequence) {
3394 if (isLegacy()) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003395 return getColorUtil().invertCharSequenceColors(charSequence);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003396 } else {
3397 return charSequence;
3398 }
3399 }
3400
Dan Sandler26e81cf2014-05-06 10:01:27 -04003401 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003402 * Apply any necessariy colors to the small icon
Dan Sandler26e81cf2014-05-06 10:01:27 -04003403 */
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003404 private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
Selim Cinekea4bef72015-12-02 15:51:10 -08003405 boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
3406 if (colorable) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003407 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003408 PorterDuff.Mode.SRC_ATOP, -1);
Selim Cinekea4bef72015-12-02 15:51:10 -08003409
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003410 }
Selim Cinekea4bef72015-12-02 15:51:10 -08003411 contentView.setInt(R.id.notification_header, "setOriginalIconColor",
3412 colorable ? resolveColor() : NotificationHeaderView.NO_COLOR);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003413 }
3414
Dan Sandler26e81cf2014-05-06 10:01:27 -04003415 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003416 * Make the largeIcon dark if it's a fake smallIcon (that is,
Dan Sandler26e81cf2014-05-06 10:01:27 -04003417 * if it's grayscale).
3418 */
3419 // TODO: also check bounds, transparency, that sort of thing.
Dan Sandlerd63f9322015-05-06 15:18:49 -04003420 private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
3421 if (largeIcon != null && isLegacy()
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003422 && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003423 // resolve color will fall back to the default when legacy
3424 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
Dan Sandler190d58d2014-05-15 09:33:39 -04003425 PorterDuff.Mode.SRC_ATOP, -1);
Jorim Jaggi92df1f22014-12-16 19:44:41 +01003426 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003427 }
3428
Julia Reynolds10ee1fc2015-11-09 11:04:55 -05003429 private void sanitizeColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003430 if (mN.color != COLOR_DEFAULT) {
3431 mN.color |= 0xFF000000; // no alpha for custom colors
Jorim Jaggi74419312014-06-10 20:57:21 +02003432 }
Jorim Jaggi74419312014-06-10 20:57:21 +02003433 }
3434
Selim Cinek5bf069a2015-11-10 19:14:27 -05003435 int resolveColor() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003436 if (mN.color == COLOR_DEFAULT) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003437 return mContext.getColor(R.color.notification_icon_default_color);
Dan Sandler26e81cf2014-05-06 10:01:27 -04003438 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003439 return mN.color;
Dan Sandler26e81cf2014-05-06 10:01:27 -04003440 }
3441
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003442 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003443 * Apply the unstyled operations and return a new {@link Notification} object.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003444 * @hide
Joe Onoratocb109a02011-01-18 17:57:41 -08003445 */
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003446 public Notification buildUnstyled() {
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003447 if (mActions.size() > 0) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003448 mN.actions = new Action[mActions.size()];
3449 mActions.toArray(mN.actions);
Daniel Sandlera0a938c2012-03-15 08:42:37 -04003450 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003451 if (!mPersonList.isEmpty()) {
3452 mN.extras.putStringArray(EXTRA_PEOPLE,
3453 mPersonList.toArray(new String[mPersonList.size()]));
Dan Sandler0bf2ed82013-12-21 23:33:41 -06003454 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003455 return mN;
Joe Onorato46439ce2010-11-19 13:56:21 -08003456 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003457
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003458 public static Notification.Builder recoverBuilder(Context context, Notification n) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003459 // Re-create notification context so we can access app resources.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003460 ApplicationInfo applicationInfo = n.extras.getParcelable(
3461 EXTRA_BUILDER_APPLICATION_INFO);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003462 Context builderContext;
Julia Reynoldsda303542015-11-23 14:00:20 -05003463 if (applicationInfo != null) {
3464 try {
3465 builderContext = context.createApplicationContext(applicationInfo,
3466 Context.CONTEXT_RESTRICTED);
3467 } catch (NameNotFoundException e) {
3468 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3469 builderContext = context; // try with our context
3470 }
3471 } else {
3472 builderContext = context; // try with given context
Christoph Studer4600f9b2014-07-22 22:44:43 +02003473 }
3474
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003475 return new Builder(builderContext, n);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003476 }
3477
3478 private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3479 Class<? extends Style>[] classes = new Class[]{
3480 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3481 for (Class<? extends Style> innerClass : classes) {
3482 if (templateClass.equals(innerClass.getName())) {
3483 return innerClass;
3484 }
3485 }
3486 return null;
3487 }
3488
3489 private void setBuilderContentView(Notification n, RemoteViews contentView) {
3490 n.contentView = contentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003491 }
3492
3493 private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3494 n.bigContentView = bigContentView;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003495 }
3496
3497 private void setBuilderHeadsUpContentView(Notification n,
3498 RemoteViews headsUpContentView) {
3499 n.headsUpContentView = headsUpContentView;
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003500 }
3501
3502 /**
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003503 * @deprecated Use {@link #build()} instead.
3504 */
3505 @Deprecated
3506 public Notification getNotification() {
3507 return build();
3508 }
3509
3510 /**
3511 * Combine all of the options that have been set and return a new {@link Notification}
3512 * object.
3513 */
3514 public Notification build() {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003515 // first, add any extras from the calling code
3516 if (mUserExtras != null) {
3517 mN.extras = getAllExtras();
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003518 }
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003519
3520 // lazy stuff from mContext; see comment in Builder(Context, Notification)
Julia Reynoldsda303542015-11-23 14:00:20 -05003521 Notification.addFieldsFromContext(mContext, mN);
Christoph Studer943aa672014-08-03 20:31:16 +02003522
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003523 buildUnstyled();
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003524
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003525 if (mStyle != null) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003526 mStyle.buildStyled(mN);
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003527 }
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003528
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003529 return mN;
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003530 }
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003531
3532 /**
3533 * Apply this Builder to an existing {@link Notification} object.
3534 *
3535 * @hide
3536 */
3537 public Notification buildInto(Notification n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -04003538 build().cloneInto(n, true);
Daniel Sandlerbe6e7e02013-02-01 17:49:11 -05003539 return n;
3540 }
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003541
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003542 private int getBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003543 return R.layout.notification_template_material_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003544 }
3545
3546 private int getBigBaseLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003547 return R.layout.notification_template_material_big_base;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003548 }
3549
3550 private int getBigPictureLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003551 return R.layout.notification_template_material_big_picture;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003552 }
3553
3554 private int getBigTextLayoutResource() {
Jorim Jaggi445d3c02014-08-19 22:33:42 +02003555 return R.layout.notification_template_material_big_text;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003556 }
3557
3558 private int getInboxLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003559 return R.layout.notification_template_material_inbox;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003560 }
3561
3562 private int getActionLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003563 return R.layout.notification_material_action;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003564 }
3565
3566 private int getActionTombstoneLayoutResource() {
Alan Viverette3cb07a462014-06-06 14:19:53 -07003567 return R.layout.notification_material_action_tombstone;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003568 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003569 }
3570
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003571 /**
3572 * An object that can apply a rich notification style to a {@link Notification.Builder}
3573 * object.
3574 */
Griff Hazendfcb0802014-02-11 12:00:00 -08003575 public static abstract class Style {
Chris Wrend6297db2012-05-03 16:20:13 -04003576 private CharSequence mBigContentTitle;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003577
3578 /**
3579 * @hide
3580 */
3581 protected CharSequence mSummaryText = null;
3582
3583 /**
3584 * @hide
3585 */
3586 protected boolean mSummaryTextSet = false;
Chris Wrend6297db2012-05-03 16:20:13 -04003587
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003588 protected Builder mBuilder;
3589
Chris Wrend6297db2012-05-03 16:20:13 -04003590 /**
3591 * Overrides ContentTitle in the big form of the template.
3592 * This defaults to the value passed to setContentTitle().
3593 */
3594 protected void internalSetBigContentTitle(CharSequence title) {
3595 mBigContentTitle = title;
3596 }
3597
3598 /**
3599 * Set the first line of text after the detail section in the big form of the template.
3600 */
3601 protected void internalSetSummaryText(CharSequence cs) {
3602 mSummaryText = cs;
Daniel Sandler619738c2012-06-07 16:33:08 -04003603 mSummaryTextSet = true;
Chris Wrend6297db2012-05-03 16:20:13 -04003604 }
3605
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003606 public void setBuilder(Builder builder) {
3607 if (mBuilder != builder) {
3608 mBuilder = builder;
Daniel Sandlerc08dea22012-06-28 08:35:24 -07003609 if (mBuilder != null) {
3610 mBuilder.setStyle(this);
3611 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003612 }
3613 }
3614
Chris Wrend6297db2012-05-03 16:20:13 -04003615 protected void checkBuilder() {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003616 if (mBuilder == null) {
3617 throw new IllegalArgumentException("Style requires a valid Builder object");
3618 }
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003619 }
Chris Wrend6297db2012-05-03 16:20:13 -04003620
3621 protected RemoteViews getStandardView(int layoutId) {
3622 checkBuilder();
3623
Christoph Studer4600f9b2014-07-22 22:44:43 +02003624 // Nasty.
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003625 CharSequence oldBuilderContentTitle =
3626 mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003627 if (mBigContentTitle != null) {
3628 mBuilder.setContentTitle(mBigContentTitle);
3629 }
3630
Chris Wrend6297db2012-05-03 16:20:13 -04003631 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3632
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003633 mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003634
Chris Wrend6297db2012-05-03 16:20:13 -04003635 if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3636 contentView.setViewVisibility(R.id.line1, View.GONE);
Chris Wren67dc9a02012-05-16 01:03:20 -04003637 } else {
3638 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
Chris Wrend6297db2012-05-03 16:20:13 -04003639 }
3640
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05003641 // Clear text in case we use the line to show the profile badge.
3642 contentView.setTextViewText(com.android.internal.R.id.text, "");
3643 contentView.setViewVisibility(com.android.internal.R.id.line3, View.GONE);
3644
Chris Wrend6297db2012-05-03 16:20:13 -04003645 return contentView;
3646 }
3647
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003648 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003649 * Construct a Style-specific RemoteViews for the final 1U notification layout.
3650 * The default implementation has nothing additional to add.
3651 * @hide
3652 */
3653 public RemoteViews makeContentView() {
3654 return null;
3655 }
3656
3657 /**
3658 * Construct a Style-specific RemoteViews for the final big notification layout.
3659 * @hide
3660 */
3661 public RemoteViews makeBigContentView() {
3662 return null;
3663 }
3664
3665 /**
3666 * Construct a Style-specific RemoteViews for the final HUN layout.
3667 * @hide
3668 */
3669 public RemoteViews makeHeadsUpContentView() {
3670 return null;
3671 }
3672
3673 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003674 * Apply any style-specific extras to this notification before shipping it out.
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003675 * @hide
3676 */
3677 public void addExtras(Bundle extras) {
3678 if (mSummaryTextSet) {
3679 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3680 }
3681 if (mBigContentTitle != null) {
3682 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3683 }
Chris Wren91ad5632013-06-05 15:05:57 -04003684 extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003685 }
3686
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003687 /**
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003688 * Reconstruct the internal state of this Style object from extras.
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003689 * @hide
3690 */
Christoph Studer4600f9b2014-07-22 22:44:43 +02003691 protected void restoreFromExtras(Bundle extras) {
3692 if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3693 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3694 mSummaryTextSet = true;
3695 }
3696 if (extras.containsKey(EXTRA_TITLE_BIG)) {
3697 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3698 }
3699 }
3700
3701
3702 /**
3703 * @hide
3704 */
3705 public Notification buildStyled(Notification wip) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003706 addExtras(wip.extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003707 return wip;
3708 }
3709
Daniel Sandler0ec46202015-06-24 01:27:05 -04003710 /**
3711 * @hide
3712 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003713 public void purgeResources() {}
3714
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003715 /**
3716 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3717 * attached to.
3718 *
3719 * @return the fully constructed Notification.
3720 */
3721 public Notification build() {
3722 checkBuilder();
3723 return mBuilder.build();
3724 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02003725
3726 /**
3727 * @hide
3728 * @return true if the style positions the progress bar on the second line; false if the
3729 * style hides the progress bar
3730 */
3731 protected boolean hasProgress() {
3732 return true;
3733 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003734
3735 /**
3736 * @hide
3737 * @return Whether we should put the summary be put into the notification header
3738 */
3739 public boolean hasSummaryInHeader() {
3740 return true;
3741 }
Joe Onorato46439ce2010-11-19 13:56:21 -08003742 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003743
3744 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003745 * Helper class for generating large-format notifications that include a large image attachment.
Joe Malin8d40d042012-11-05 11:36:40 -08003746 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003747 * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003748 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003749 * Notification notif = new Notification.Builder(mContext)
3750 * .setContentTitle(&quot;New photo from &quot; + sender.toString())
3751 * .setContentText(subject)
3752 * .setSmallIcon(R.drawable.new_post)
3753 * .setLargeIcon(aBitmap)
3754 * .setStyle(new Notification.BigPictureStyle()
3755 * .bigPicture(aBigBitmap))
3756 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003757 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003758 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003759 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003760 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003761 public static class BigPictureStyle extends Style {
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003762 private Bitmap mPicture;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003763 private Icon mBigLargeIcon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003764 private boolean mBigLargeIconSet = false;
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003765
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003766 public BigPictureStyle() {
3767 }
3768
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003769 public BigPictureStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003770 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003771 }
3772
Chris Wrend6297db2012-05-03 16:20:13 -04003773 /**
3774 * Overrides ContentTitle in the big form of the template.
3775 * This defaults to the value passed to setContentTitle().
3776 */
3777 public BigPictureStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003778 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003779 return this;
3780 }
3781
3782 /**
3783 * Set the first line of text after the detail section in the big form of the template.
3784 */
3785 public BigPictureStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003786 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003787 return this;
3788 }
3789
Chris Wren0bd664d2012-08-01 13:56:56 -04003790 /**
3791 * Provide the bitmap to be used as the payload for the BigPicture notification.
3792 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003793 public BigPictureStyle bigPicture(Bitmap b) {
3794 mPicture = b;
3795 return this;
3796 }
3797
Chris Wren3745a3d2012-05-22 15:11:52 -04003798 /**
Chris Wren3745a3d2012-05-22 15:11:52 -04003799 * Override the large icon when the big notification is shown.
3800 */
3801 public BigPictureStyle bigLargeIcon(Bitmap b) {
Dan Sandlerd63f9322015-05-06 15:18:49 -04003802 return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3803 }
3804
3805 /**
3806 * Override the large icon when the big notification is shown.
3807 */
3808 public BigPictureStyle bigLargeIcon(Icon icon) {
Chris Wren3745a3d2012-05-22 15:11:52 -04003809 mBigLargeIconSet = true;
Dan Sandlerd63f9322015-05-06 15:18:49 -04003810 mBigLargeIcon = icon;
Chris Wren3745a3d2012-05-22 15:11:52 -04003811 return this;
3812 }
3813
Riley Andrews0394a0c2015-11-03 23:36:52 -08003814 /** @hide */
3815 public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
3816
Daniel Sandler0ec46202015-06-24 01:27:05 -04003817 /**
3818 * @hide
3819 */
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003820 @Override
3821 public void purgeResources() {
3822 super.purgeResources();
Riley Andrews8cee7c12015-11-01 23:36:04 -08003823 if (mPicture != null &&
3824 mPicture.isMutable() &&
Riley Andrews0394a0c2015-11-03 23:36:52 -08003825 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
Jorim Jaggia0d58ae2015-06-03 11:48:13 -07003826 mPicture = mPicture.createAshmemBitmap();
3827 }
3828 if (mBigLargeIcon != null) {
3829 mBigLargeIcon.convertToAshmem();
3830 }
3831 }
Christoph Studer5c510ee2014-12-15 16:32:27 +01003832
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003833 /**
3834 * @hide
3835 */
3836 public RemoteViews makeBigContentView() {
3837 // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
Christoph Studer5c510ee2014-12-15 16:32:27 +01003838 // This covers the following cases:
3839 // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003840 // mN.mLargeIcon
3841 // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Dan Sandlerd63f9322015-05-06 15:18:49 -04003842 Icon oldLargeIcon = null;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003843 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003844 oldLargeIcon = mBuilder.mN.mLargeIcon;
3845 mBuilder.mN.mLargeIcon = mBigLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003846 }
3847
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003848 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
Selim Cinek03d0d652015-11-13 13:18:09 -05003849 if (mSummaryTextSet) {
3850 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
3851 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3852 }
Selim Cinek860b6da2015-12-16 19:02:19 -08003853 mBuilder.setContentMinHeight(contentView, mBuilder.mN.mLargeIcon != null);
Selim Cinek53e64a42015-11-16 10:40:56 -08003854
Christoph Studer5c510ee2014-12-15 16:32:27 +01003855 if (mBigLargeIconSet) {
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003856 mBuilder.mN.mLargeIcon = oldLargeIcon;
Christoph Studer5c510ee2014-12-15 16:32:27 +01003857 }
3858
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003859 contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3860
Selim Cinek29603462015-11-17 19:04:39 -08003861 mBuilder.addProfileBadge(contentView, R.id.profile_badge_line3);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003862 return contentView;
3863 }
3864
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003865 /**
3866 * @hide
3867 */
3868 public void addExtras(Bundle extras) {
3869 super.addExtras(extras);
3870
3871 if (mBigLargeIconSet) {
3872 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3873 }
3874 extras.putParcelable(EXTRA_PICTURE, mPicture);
3875 }
3876
Daniel Sandlercf1d39b2013-09-23 13:35:35 -04003877 /**
3878 * @hide
3879 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003880 @Override
Christoph Studer4600f9b2014-07-22 22:44:43 +02003881 protected void restoreFromExtras(Bundle extras) {
3882 super.restoreFromExtras(extras);
3883
3884 if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
Christoph Studer5c510ee2014-12-15 16:32:27 +01003885 mBigLargeIconSet = true;
Christoph Studer4600f9b2014-07-22 22:44:43 +02003886 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
Chris Wren3745a3d2012-05-22 15:11:52 -04003887 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02003888 mPicture = extras.getParcelable(EXTRA_PICTURE);
3889 }
Selim Cinek03d0d652015-11-13 13:18:09 -05003890
3891 /**
3892 * @hide
3893 */
3894 @Override
3895 public boolean hasSummaryInHeader() {
3896 return false;
3897 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003898 }
3899
3900 /**
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003901 * Helper class for generating large-format notifications that include a lot of text.
Joe Malin8d40d042012-11-05 11:36:40 -08003902 *
Robert Ly91c5ce32014-06-08 15:37:00 -07003903 * Here's how you'd set the <code>BigTextStyle</code> on a notification:
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003904 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07003905 * Notification notif = new Notification.Builder(mContext)
3906 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
3907 * .setContentText(subject)
3908 * .setSmallIcon(R.drawable.new_mail)
3909 * .setLargeIcon(aBitmap)
3910 * .setStyle(new Notification.BigTextStyle()
3911 * .bigText(aVeryLongString))
3912 * .build();
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003913 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08003914 *
Daniel Sandler4dfbe832012-04-11 14:51:46 -04003915 * @see Notification#bigContentView
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003916 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003917 public static class BigTextStyle extends Style {
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003918
3919 private static final int MAX_LINES = 13;
3920 private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3921 private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3922
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003923 private CharSequence mBigText;
3924
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003925 public BigTextStyle() {
3926 }
3927
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003928 public BigTextStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04003929 setBuilder(builder);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003930 }
3931
Chris Wrend6297db2012-05-03 16:20:13 -04003932 /**
3933 * Overrides ContentTitle in the big form of the template.
3934 * This defaults to the value passed to setContentTitle().
3935 */
3936 public BigTextStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003937 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04003938 return this;
3939 }
3940
3941 /**
3942 * Set the first line of text after the detail section in the big form of the template.
3943 */
3944 public BigTextStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003945 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04003946 return this;
3947 }
3948
Chris Wren0bd664d2012-08-01 13:56:56 -04003949 /**
3950 * Provide the longer text to be displayed in the big form of the
3951 * template in place of the content text.
3952 */
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003953 public BigTextStyle bigText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04003954 mBigText = safeCharSequence(cs);
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003955 return this;
3956 }
3957
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003958 /**
3959 * @hide
3960 */
3961 public void addExtras(Bundle extras) {
3962 super.addExtras(extras);
3963
Christoph Studer4600f9b2014-07-22 22:44:43 +02003964 extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3965 }
3966
3967 /**
3968 * @hide
3969 */
3970 @Override
3971 protected void restoreFromExtras(Bundle extras) {
3972 super.restoreFromExtras(extras);
3973
3974 mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
Daniel Sandlerf45564e2013-04-15 15:05:08 -04003975 }
3976
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003977 /**
3978 * @hide
3979 */
3980 public RemoteViews makeBigContentView() {
Christoph Studer4600f9b2014-07-22 22:44:43 +02003981
3982 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003983 CharSequence oldBuilderContentText =
3984 mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
3985 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Daniel Sandler916ad912012-06-13 12:17:07 -04003986
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01003987 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
Joe Malin8d40d042012-11-05 11:36:40 -08003988
Julia Reynoldsd9228f12015-10-20 10:37:27 -04003989 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02003990
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01003991 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003992 contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
Jorim Jaggi457a10d2014-09-08 16:18:23 +02003993 contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
Jorim Jaggid05aa3e2014-08-28 17:52:27 +02003994
Kenny Guy98193ea2014-07-24 19:54:37 +01003995 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3996
Selim Cinek4fb12d32015-11-19 18:10:48 -08003997 contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.mLargeIcon != null);
3998
Daniel Sandlerf3b73432012-03-27 15:01:25 -04003999 return contentView;
4000 }
4001
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004002 private int calculateMaxLines() {
4003 int lineCount = MAX_LINES;
4004 boolean hasActions = mBuilder.mActions.size() > 0;
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004005 boolean hasSummary = (mSummaryTextSet ? mSummaryText
4006 : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004007 if (hasActions) {
4008 lineCount -= LINES_CONSUMED_BY_ACTIONS;
4009 }
4010 if (hasSummary) {
4011 lineCount -= LINES_CONSUMED_BY_SUMMARY;
4012 }
Jorim Jaggi457a10d2014-09-08 16:18:23 +02004013 return lineCount;
4014 }
Daniel Sandlerf3b73432012-03-27 15:01:25 -04004015 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004016
4017 /**
4018 * Helper class for generating large-format notifications that include a list of (up to 5) strings.
Joe Malin8d40d042012-11-05 11:36:40 -08004019 *
Robert Ly91c5ce32014-06-08 15:37:00 -07004020 * Here's how you'd set the <code>InboxStyle</code> on a notification:
Daniel Sandler879c5e02012-04-17 16:46:51 -04004021 * <pre class="prettyprint">
Robert Ly91c5ce32014-06-08 15:37:00 -07004022 * Notification notif = new Notification.Builder(mContext)
4023 * .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
4024 * .setContentText(subject)
4025 * .setSmallIcon(R.drawable.new_mail)
4026 * .setLargeIcon(aBitmap)
4027 * .setStyle(new Notification.InboxStyle()
4028 * .addLine(str1)
4029 * .addLine(str2)
4030 * .setContentTitle(&quot;&quot;)
4031 * .setSummaryText(&quot;+3 more&quot;))
4032 * .build();
Daniel Sandler879c5e02012-04-17 16:46:51 -04004033 * </pre>
Joe Malin8d40d042012-11-05 11:36:40 -08004034 *
Daniel Sandler879c5e02012-04-17 16:46:51 -04004035 * @see Notification#bigContentView
4036 */
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004037 public static class InboxStyle extends Style {
Daniel Sandler879c5e02012-04-17 16:46:51 -04004038 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
4039
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004040 public InboxStyle() {
4041 }
4042
Daniel Sandler879c5e02012-04-17 16:46:51 -04004043 public InboxStyle(Builder builder) {
Chris Wrenfbd96ba2012-05-01 12:03:58 -04004044 setBuilder(builder);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004045 }
4046
Chris Wrend6297db2012-05-03 16:20:13 -04004047 /**
4048 * Overrides ContentTitle in the big form of the template.
4049 * This defaults to the value passed to setContentTitle().
4050 */
4051 public InboxStyle setBigContentTitle(CharSequence title) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004052 internalSetBigContentTitle(safeCharSequence(title));
Chris Wrend6297db2012-05-03 16:20:13 -04004053 return this;
4054 }
4055
4056 /**
4057 * Set the first line of text after the detail section in the big form of the template.
4058 */
4059 public InboxStyle setSummaryText(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004060 internalSetSummaryText(safeCharSequence(cs));
Chris Wrend6297db2012-05-03 16:20:13 -04004061 return this;
4062 }
4063
Chris Wren0bd664d2012-08-01 13:56:56 -04004064 /**
4065 * Append a line to the digest section of the Inbox notification.
4066 */
Daniel Sandler879c5e02012-04-17 16:46:51 -04004067 public InboxStyle addLine(CharSequence cs) {
Daniel Sandlerdcbaf662013-04-26 16:23:09 -04004068 mTexts.add(safeCharSequence(cs));
Daniel Sandler879c5e02012-04-17 16:46:51 -04004069 return this;
4070 }
4071
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004072 /**
4073 * @hide
4074 */
4075 public void addExtras(Bundle extras) {
4076 super.addExtras(extras);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004077
Daniel Sandlerf45564e2013-04-15 15:05:08 -04004078 CharSequence[] a = new CharSequence[mTexts.size()];
4079 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4080 }
4081
Christoph Studer4600f9b2014-07-22 22:44:43 +02004082 /**
4083 * @hide
4084 */
4085 @Override
4086 protected void restoreFromExtras(Bundle extras) {
4087 super.restoreFromExtras(extras);
4088
4089 mTexts.clear();
4090 if (extras.containsKey(EXTRA_TEXT_LINES)) {
4091 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4092 }
4093 }
4094
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004095 /**
4096 * @hide
4097 */
4098 public RemoteViews makeBigContentView() {
Daniel Sandler619738c2012-06-07 16:33:08 -04004099 // Remove the content text so line3 disappears unless you have a summary
Christoph Studer4600f9b2014-07-22 22:44:43 +02004100 // Nasty
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004101 CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
4102 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004103
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01004104 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
Daniel Sandler619738c2012-06-07 16:33:08 -04004105
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004106 mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
Christoph Studer4600f9b2014-07-22 22:44:43 +02004107
Chris Wrend6297db2012-05-03 16:20:13 -04004108 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 -04004109 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
Chris Wrend6297db2012-05-03 16:20:13 -04004110
Chris Wren4ed80d52012-05-17 09:30:03 -04004111 // Make sure all rows are gone in case we reuse a view.
4112 for (int rowId : rowIds) {
4113 contentView.setViewVisibility(rowId, View.GONE);
4114 }
4115
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004116 final boolean largeText =
4117 mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4118 final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4119 R.dimen.notification_subtext_size);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004120 int i=0;
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004121 final float density = mBuilder.mContext.getResources().getDisplayMetrics().density;
4122 int topPadding = (int) (5 * density);
4123 int bottomPadding = (int) (13 * density);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004124 while (i < mTexts.size() && i < rowIds.length) {
4125 CharSequence str = mTexts.get(i);
4126 if (str != null && !str.equals("")) {
4127 contentView.setViewVisibility(rowIds[i], View.VISIBLE);
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01004128 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
Jorim Jaggi445d3c02014-08-19 22:33:42 +02004129 if (largeText) {
4130 contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4131 subTextSize);
4132 }
Selim Cinek9d9fc6e2015-11-12 15:49:14 -05004133 contentView.setViewPadding(rowIds[i], 0, topPadding, 0,
4134 i == rowIds.length - 1 || i == mTexts.size() - 1 ? bottomPadding : 0);
Daniel Sandler879c5e02012-04-17 16:46:51 -04004135 }
4136 i++;
4137 }
Kenny Guy98193ea2014-07-24 19:54:37 +01004138 mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4139
Selim Cinek1e0bf612015-11-20 15:57:26 -08004140 handleInboxImageMargin(contentView, rowIds[0]);
4141
Daniel Sandler879c5e02012-04-17 16:46:51 -04004142 return contentView;
4143 }
Selim Cinek1e0bf612015-11-20 15:57:26 -08004144
4145 private void handleInboxImageMargin(RemoteViews contentView, int id) {
4146 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
4147 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4148 boolean hasProgress = max != 0 || ind;
4149 int endMargin = 0;
4150 if (mTexts.size() > 0 && mBuilder.mN.mLargeIcon != null && !hasProgress) {
4151 endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
4152 R.dimen.notification_content_picture_margin);
4153 }
4154 contentView.setViewLayoutMarginEnd(id, endMargin);
4155 }
Daniel Sandler879c5e02012-04-17 16:46:51 -04004156 }
Dan Sandler842dd772014-05-15 09:36:47 -04004157
4158 /**
4159 * Notification style for media playback notifications.
4160 *
4161 * In the expanded form, {@link Notification#bigContentView}, up to 5
4162 * {@link Notification.Action}s specified with
Dan Sandler86647982015-05-13 23:41:13 -04004163 * {@link Notification.Builder#addAction(Action) addAction} will be
Dan Sandler842dd772014-05-15 09:36:47 -04004164 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4165 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4166 * treated as album artwork.
4167 *
4168 * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4169 * {@link Notification#contentView}; by providing action indices to
Christoph Studerfde6f4d2014-12-12 13:23:26 +01004170 * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
Dan Sandler842dd772014-05-15 09:36:47 -04004171 * in the standard view alongside the usual content.
4172 *
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004173 * Notifications created with MediaStyle will have their category set to
4174 * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4175 * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4176 *
Jeff Browndba34ba2014-06-24 20:46:03 -07004177 * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4178 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
Dan Sandler842dd772014-05-15 09:36:47 -04004179 * the System UI can identify this as a notification representing an active media session
4180 * and respond accordingly (by showing album artwork in the lockscreen, for example).
4181 *
4182 * To use this style with your Notification, feed it to
4183 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4184 * <pre class="prettyprint">
4185 * Notification noti = new Notification.Builder()
4186 * .setSmallIcon(R.drawable.ic_stat_player)
Christoph Studere935fe92014-11-24 14:18:06 +01004187 * .setContentTitle(&quot;Track title&quot;)
4188 * .setContentText(&quot;Artist - Album&quot;)
4189 * .setLargeIcon(albumArtBitmap))
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004190 * .setStyle(<b>new Notification.MediaStyle()</b>
4191 * .setMediaSession(mySession))
Dan Sandler842dd772014-05-15 09:36:47 -04004192 * .build();
4193 * </pre>
4194 *
4195 * @see Notification#bigContentView
4196 */
4197 public static class MediaStyle extends Style {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004198 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
Dan Sandler842dd772014-05-15 09:36:47 -04004199 static final int MAX_MEDIA_BUTTONS = 5;
4200
4201 private int[] mActionsToShowInCompact = null;
Jeff Browndba34ba2014-06-24 20:46:03 -07004202 private MediaSession.Token mToken;
Dan Sandler842dd772014-05-15 09:36:47 -04004203
4204 public MediaStyle() {
4205 }
4206
4207 public MediaStyle(Builder builder) {
4208 setBuilder(builder);
4209 }
4210
4211 /**
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004212 * 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 -04004213 * notification view.
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004214 *
4215 * @param actions the indices of the actions to show in the compact notification view
Dan Sandler842dd772014-05-15 09:36:47 -04004216 */
4217 public MediaStyle setShowActionsInCompactView(int...actions) {
4218 mActionsToShowInCompact = actions;
4219 return this;
4220 }
4221
4222 /**
Jeff Browndba34ba2014-06-24 20:46:03 -07004223 * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4224 * to provide additional playback information and control to the SystemUI.
Dan Sandler842dd772014-05-15 09:36:47 -04004225 */
Jeff Browndba34ba2014-06-24 20:46:03 -07004226 public MediaStyle setMediaSession(MediaSession.Token token) {
Dan Sandler842dd772014-05-15 09:36:47 -04004227 mToken = token;
4228 return this;
4229 }
4230
Christoph Studer4600f9b2014-07-22 22:44:43 +02004231 /**
4232 * @hide
4233 */
Dan Sandler842dd772014-05-15 09:36:47 -04004234 @Override
4235 public Notification buildStyled(Notification wip) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02004236 super.buildStyled(wip);
Bryan Mawhinney6be8de32014-07-18 10:35:12 +01004237 if (wip.category == null) {
4238 wip.category = Notification.CATEGORY_TRANSPORT;
4239 }
Dan Sandler842dd772014-05-15 09:36:47 -04004240 return wip;
4241 }
4242
Christoph Studer4600f9b2014-07-22 22:44:43 +02004243 /**
4244 * @hide
4245 */
4246 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004247 public RemoteViews makeContentView() {
4248 return makeMediaContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004249 }
4250
4251 /**
4252 * @hide
4253 */
4254 @Override
Julia Reynoldsd9228f12015-10-20 10:37:27 -04004255 public RemoteViews makeBigContentView() {
4256 return makeMediaBigContentView();
Christoph Studer4600f9b2014-07-22 22:44:43 +02004257 }
4258
Dan Sandler842dd772014-05-15 09:36:47 -04004259 /** @hide */
4260 @Override
4261 public void addExtras(Bundle extras) {
4262 super.addExtras(extras);
4263
4264 if (mToken != null) {
4265 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4266 }
Bryan Mawhinneye191f902014-07-22 12:50:09 +01004267 if (mActionsToShowInCompact != null) {
4268 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4269 }
Dan Sandler842dd772014-05-15 09:36:47 -04004270 }
4271
Christoph Studer4600f9b2014-07-22 22:44:43 +02004272 /**
4273 * @hide
4274 */
4275 @Override
4276 protected void restoreFromExtras(Bundle extras) {
4277 super.restoreFromExtras(extras);
4278
4279 if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4280 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4281 }
4282 if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4283 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4284 }
4285 }
4286
Selim Cinek5bf069a2015-11-10 19:14:27 -05004287 private RemoteViews generateMediaActionButton(Action action, int color) {
Dan Sandler842dd772014-05-15 09:36:47 -04004288 final boolean tombstone = (action.actionIntent == null);
Selim Cinekf33b1112015-07-15 17:45:11 -07004289 RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
Alan Viverette3cb07a462014-06-06 14:19:53 -07004290 R.layout.notification_material_media_action);
Dan Sandler68079d52015-07-22 10:45:30 -04004291 button.setImageViewIcon(R.id.action0, action.getIcon());
Selim Cinek5bf069a2015-11-10 19:14:27 -05004292 button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
4293 -1);
Dan Sandler842dd772014-05-15 09:36:47 -04004294 if (!tombstone) {
4295 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4296 }
4297 button.setContentDescription(R.id.action0, action.title);
4298 return button;
4299 }
4300
4301 private RemoteViews makeMediaContentView() {
4302 RemoteViews view = mBuilder.applyStandardTemplate(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004303 R.layout.notification_template_material_media, false /* hasProgress */);
Dan Sandler842dd772014-05-15 09:36:47 -04004304
4305 final int numActions = mBuilder.mActions.size();
4306 final int N = mActionsToShowInCompact == null
4307 ? 0
4308 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4309 if (N > 0) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004310 view.removeAllViews(com.android.internal.R.id.media_actions);
Dan Sandler842dd772014-05-15 09:36:47 -04004311 for (int i = 0; i < N; i++) {
4312 if (i >= numActions) {
4313 throw new IllegalArgumentException(String.format(
4314 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4315 i, numActions - 1));
4316 }
4317
4318 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004319 final RemoteViews button = generateMediaActionButton(action,
4320 mBuilder.resolveColor());
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004321 view.addView(com.android.internal.R.id.media_actions, button);
Dan Sandler842dd772014-05-15 09:36:47 -04004322 }
4323 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004324 handleImage(view /* addPaddingToMainColumn */);
Dan Sandler842dd772014-05-15 09:36:47 -04004325 return view;
4326 }
4327
4328 private RemoteViews makeMediaBigContentView() {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004329 final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004330 RemoteViews big = mBuilder.applyStandardTemplate(
4331 R.layout.notification_template_material_big_media,
4332 false);
Dan Sandler842dd772014-05-15 09:36:47 -04004333
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004334 if (actionCount > 0) {
4335 big.removeAllViews(com.android.internal.R.id.media_actions);
4336 for (int i = 0; i < actionCount; i++) {
Selim Cinek5bf069a2015-11-10 19:14:27 -05004337 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
4338 mBuilder.resolveColor());
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 }
Selim Cinek5bf069a2015-11-10 19:14:27 -05004342 handleImage(big);
Dan Sandler842dd772014-05-15 09:36:47 -04004343 return big;
4344 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004345
Selim Cinek5bf069a2015-11-10 19:14:27 -05004346 private void handleImage(RemoteViews contentView) {
4347 if (mBuilder.mN.mLargeIcon != null) {
4348 contentView.setViewLayoutMarginEnd(R.id.line1, 0);
Selim Cinek5bf069a2015-11-10 19:14:27 -05004349 contentView.setViewLayoutMarginEnd(R.id.line3, 0);
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004350 }
4351 }
4352
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02004353 /**
4354 * @hide
4355 */
4356 @Override
4357 protected boolean hasProgress() {
4358 return false;
4359 }
Dan Sandler842dd772014-05-15 09:36:47 -04004360 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004361
Christoph Studer4600f9b2014-07-22 22:44:43 +02004362 // When adding a new Style subclass here, don't forget to update
4363 // Builder.getNotificationStyleClass.
4364
Griff Hazen61a9e862014-05-22 16:05:19 -07004365 /**
4366 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4367 * metadata or change options on a notification builder.
4368 */
4369 public interface Extender {
4370 /**
4371 * Apply this extender to a notification builder.
4372 * @param builder the builder to be modified.
4373 * @return the build object for chaining.
4374 */
4375 public Builder extend(Builder builder);
4376 }
4377
4378 /**
4379 * Helper class to add wearable extensions to notifications.
4380 * <p class="note"> See
4381 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4382 * for Android Wear</a> for more information on how to use this class.
4383 * <p>
4384 * To create a notification with wearable extensions:
4385 * <ol>
4386 * <li>Create a {@link android.app.Notification.Builder}, setting any desired
4387 * properties.
4388 * <li>Create a {@link android.app.Notification.WearableExtender}.
4389 * <li>Set wearable-specific properties using the
4390 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4391 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4392 * notification.
4393 * <li>Post the notification to the notification system with the
4394 * {@code NotificationManager.notify(...)} methods.
4395 * </ol>
4396 *
4397 * <pre class="prettyprint">
4398 * Notification notif = new Notification.Builder(mContext)
4399 * .setContentTitle(&quot;New mail from &quot; + sender.toString())
4400 * .setContentText(subject)
4401 * .setSmallIcon(R.drawable.new_mail)
4402 * .extend(new Notification.WearableExtender()
4403 * .setContentIcon(R.drawable.new_mail))
4404 * .build();
4405 * NotificationManager notificationManger =
4406 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4407 * notificationManger.notify(0, notif);</pre>
4408 *
4409 * <p>Wearable extensions can be accessed on an existing notification by using the
4410 * {@code WearableExtender(Notification)} constructor,
4411 * and then using the {@code get} methods to access values.
4412 *
4413 * <pre class="prettyprint">
4414 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4415 * notification);
Griff Hazen14f57992014-05-26 09:07:14 -07004416 * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004417 */
4418 public static final class WearableExtender implements Extender {
4419 /**
4420 * Sentinel value for an action index that is unset.
4421 */
4422 public static final int UNSET_ACTION_INDEX = -1;
4423
4424 /**
4425 * Size value for use with {@link #setCustomSizePreset} to show this notification with
4426 * default sizing.
4427 * <p>For custom display notifications created using {@link #setDisplayIntent},
Paul Soulosaa4f4bf2015-08-04 11:59:45 -07004428 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
Griff Hazen61a9e862014-05-22 16:05:19 -07004429 * on their content.
4430 */
4431 public static final int SIZE_DEFAULT = 0;
4432
4433 /**
4434 * Size value for use with {@link #setCustomSizePreset} to show this notification
4435 * with an extra small size.
4436 * <p>This value is only applicable for custom display notifications created using
4437 * {@link #setDisplayIntent}.
4438 */
4439 public static final int SIZE_XSMALL = 1;
4440
4441 /**
4442 * Size value for use with {@link #setCustomSizePreset} to show this notification
4443 * with a small size.
4444 * <p>This value is only applicable for custom display notifications created using
4445 * {@link #setDisplayIntent}.
4446 */
4447 public static final int SIZE_SMALL = 2;
4448
4449 /**
4450 * Size value for use with {@link #setCustomSizePreset} to show this notification
4451 * with a medium size.
4452 * <p>This value is only applicable for custom display notifications created using
4453 * {@link #setDisplayIntent}.
4454 */
4455 public static final int SIZE_MEDIUM = 3;
4456
4457 /**
4458 * Size value for use with {@link #setCustomSizePreset} to show this notification
4459 * with a large size.
4460 * <p>This value is only applicable for custom display notifications created using
4461 * {@link #setDisplayIntent}.
4462 */
4463 public static final int SIZE_LARGE = 4;
4464
Griff Hazend5f11f92014-05-27 15:40:09 -07004465 /**
4466 * Size value for use with {@link #setCustomSizePreset} to show this notification
4467 * full screen.
4468 * <p>This value is only applicable for custom display notifications created using
4469 * {@link #setDisplayIntent}.
4470 */
4471 public static final int SIZE_FULL_SCREEN = 5;
4472
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004473 /**
4474 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4475 * short amount of time when this notification is displayed on the screen. This
4476 * is the default value.
4477 */
4478 public static final int SCREEN_TIMEOUT_SHORT = 0;
4479
4480 /**
4481 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4482 * for a longer amount of time when this notification is displayed on the screen.
4483 */
4484 public static final int SCREEN_TIMEOUT_LONG = -1;
4485
Griff Hazen61a9e862014-05-22 16:05:19 -07004486 /** Notification extra which contains wearable extensions */
4487 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4488
Pete Gastaf6781d2014-10-07 15:17:05 -04004489 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
Griff Hazen61a9e862014-05-22 16:05:19 -07004490 private static final String KEY_ACTIONS = "actions";
4491 private static final String KEY_FLAGS = "flags";
4492 private static final String KEY_DISPLAY_INTENT = "displayIntent";
4493 private static final String KEY_PAGES = "pages";
4494 private static final String KEY_BACKGROUND = "background";
4495 private static final String KEY_CONTENT_ICON = "contentIcon";
4496 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4497 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4498 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4499 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4500 private static final String KEY_GRAVITY = "gravity";
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004501 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
Griff Hazen61a9e862014-05-22 16:05:19 -07004502
4503 // Flags bitwise-ored to mFlags
4504 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4505 private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4506 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4507 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004508 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
Griff Hazen61a9e862014-05-22 16:05:19 -07004509
4510 // Default value for flags integer
4511 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4512
4513 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4514 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4515
4516 private ArrayList<Action> mActions = new ArrayList<Action>();
4517 private int mFlags = DEFAULT_FLAGS;
4518 private PendingIntent mDisplayIntent;
4519 private ArrayList<Notification> mPages = new ArrayList<Notification>();
4520 private Bitmap mBackground;
4521 private int mContentIcon;
4522 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4523 private int mContentActionIndex = UNSET_ACTION_INDEX;
4524 private int mCustomSizePreset = SIZE_DEFAULT;
4525 private int mCustomContentHeight;
4526 private int mGravity = DEFAULT_GRAVITY;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004527 private int mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004528
4529 /**
4530 * Create a {@link android.app.Notification.WearableExtender} with default
4531 * options.
4532 */
4533 public WearableExtender() {
4534 }
4535
4536 public WearableExtender(Notification notif) {
4537 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4538 if (wearableBundle != null) {
4539 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4540 if (actions != null) {
4541 mActions.addAll(actions);
4542 }
4543
4544 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4545 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4546
4547 Notification[] pages = getNotificationArrayFromBundle(
4548 wearableBundle, KEY_PAGES);
4549 if (pages != null) {
4550 Collections.addAll(mPages, pages);
4551 }
4552
4553 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4554 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4555 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4556 DEFAULT_CONTENT_ICON_GRAVITY);
4557 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4558 UNSET_ACTION_INDEX);
4559 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4560 SIZE_DEFAULT);
4561 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4562 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004563 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
Griff Hazen61a9e862014-05-22 16:05:19 -07004564 }
4565 }
4566
4567 /**
4568 * Apply wearable extensions to a notification that is being built. This is typically
4569 * called by the {@link android.app.Notification.Builder#extend} method of
4570 * {@link android.app.Notification.Builder}.
4571 */
4572 @Override
4573 public Notification.Builder extend(Notification.Builder builder) {
4574 Bundle wearableBundle = new Bundle();
4575
4576 if (!mActions.isEmpty()) {
4577 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4578 }
4579 if (mFlags != DEFAULT_FLAGS) {
4580 wearableBundle.putInt(KEY_FLAGS, mFlags);
4581 }
4582 if (mDisplayIntent != null) {
4583 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4584 }
4585 if (!mPages.isEmpty()) {
4586 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4587 new Notification[mPages.size()]));
4588 }
4589 if (mBackground != null) {
4590 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4591 }
4592 if (mContentIcon != 0) {
4593 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4594 }
4595 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4596 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4597 }
4598 if (mContentActionIndex != UNSET_ACTION_INDEX) {
4599 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4600 mContentActionIndex);
4601 }
4602 if (mCustomSizePreset != SIZE_DEFAULT) {
4603 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4604 }
4605 if (mCustomContentHeight != 0) {
4606 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4607 }
4608 if (mGravity != DEFAULT_GRAVITY) {
4609 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4610 }
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004611 if (mHintScreenTimeout != 0) {
4612 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4613 }
Griff Hazen61a9e862014-05-22 16:05:19 -07004614
4615 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4616 return builder;
4617 }
4618
4619 @Override
4620 public WearableExtender clone() {
4621 WearableExtender that = new WearableExtender();
4622 that.mActions = new ArrayList<Action>(this.mActions);
4623 that.mFlags = this.mFlags;
4624 that.mDisplayIntent = this.mDisplayIntent;
4625 that.mPages = new ArrayList<Notification>(this.mPages);
4626 that.mBackground = this.mBackground;
4627 that.mContentIcon = this.mContentIcon;
4628 that.mContentIconGravity = this.mContentIconGravity;
4629 that.mContentActionIndex = this.mContentActionIndex;
4630 that.mCustomSizePreset = this.mCustomSizePreset;
4631 that.mCustomContentHeight = this.mCustomContentHeight;
4632 that.mGravity = this.mGravity;
Griff Hazen5f2edfc2014-09-29 16:28:44 -07004633 that.mHintScreenTimeout = this.mHintScreenTimeout;
Griff Hazen61a9e862014-05-22 16:05:19 -07004634 return that;
4635 }
4636
4637 /**
4638 * Add a wearable action to this notification.
4639 *
4640 * <p>When wearable actions are added using this method, the set of actions that
4641 * show on a wearable device splits from devices that only show actions added
4642 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4643 * of which actions display on different devices.
4644 *
4645 * @param action the action to add to this notification
4646 * @return this object for method chaining
4647 * @see android.app.Notification.Action
4648 */
4649 public WearableExtender addAction(Action action) {
4650 mActions.add(action);
4651 return this;
4652 }
4653
4654 /**
4655 * Adds wearable actions to this notification.
4656 *
4657 * <p>When wearable actions are added using this method, the set of actions that
4658 * show on a wearable device splits from devices that only show actions added
4659 * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4660 * of which actions display on different devices.
4661 *
4662 * @param actions the actions to add to this notification
4663 * @return this object for method chaining
4664 * @see android.app.Notification.Action
4665 */
4666 public WearableExtender addActions(List<Action> actions) {
4667 mActions.addAll(actions);
4668 return this;
4669 }
4670
4671 /**
4672 * Clear all wearable actions present on this builder.
4673 * @return this object for method chaining.
4674 * @see #addAction
4675 */
4676 public WearableExtender clearActions() {
4677 mActions.clear();
4678 return this;
4679 }
4680
4681 /**
4682 * Get the wearable actions present on this notification.
4683 */
4684 public List<Action> getActions() {
4685 return mActions;
4686 }
4687
4688 /**
4689 * Set an intent to launch inside of an activity view when displaying
Griff Hazen14f57992014-05-26 09:07:14 -07004690 * this notification. The {@link PendingIntent} provided should be for an activity.
4691 *
4692 * <pre class="prettyprint">
4693 * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4694 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4695 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4696 * Notification notif = new Notification.Builder(context)
4697 * .extend(new Notification.WearableExtender()
4698 * .setDisplayIntent(displayPendingIntent)
4699 * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4700 * .build();</pre>
4701 *
4702 * <p>The activity to launch needs to allow embedding, must be exported, and
Griff Hazen831ca9d2014-06-17 00:38:38 -07004703 * should have an empty task affinity. It is also recommended to use the device
4704 * default light theme.
Griff Hazen14f57992014-05-26 09:07:14 -07004705 *
4706 * <p>Example AndroidManifest.xml entry:
4707 * <pre class="prettyprint">
4708 * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4709 * android:exported=&quot;true&quot;
4710 * android:allowEmbedded=&quot;true&quot;
Griff Hazen831ca9d2014-06-17 00:38:38 -07004711 * android:taskAffinity=&quot;&quot;
4712 * android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
Griff Hazen61a9e862014-05-22 16:05:19 -07004713 *
4714 * @param intent the {@link PendingIntent} for an activity
4715 * @return this object for method chaining
4716 * @see android.app.Notification.WearableExtender#getDisplayIntent
4717 */
4718 public WearableExtender setDisplayIntent(PendingIntent intent) {
4719 mDisplayIntent = intent;
4720 return this;
4721 }
4722
4723 /**
4724 * Get the intent to launch inside of an activity view when displaying this
4725 * notification. This {@code PendingIntent} should be for an activity.
4726 */
4727 public PendingIntent getDisplayIntent() {
4728 return mDisplayIntent;
4729 }
4730
4731 /**
4732 * Add an additional page of content to display with this notification. The current
4733 * notification forms the first page, and pages added using this function form
4734 * subsequent pages. This field can be used to separate a notification into multiple
4735 * sections.
4736 *
4737 * @param page the notification to add as another page
4738 * @return this object for method chaining
4739 * @see android.app.Notification.WearableExtender#getPages
4740 */
4741 public WearableExtender addPage(Notification page) {
4742 mPages.add(page);
4743 return this;
4744 }
4745
4746 /**
4747 * Add additional pages of content to display with this notification. The current
4748 * notification forms the first page, and pages added using this function form
4749 * subsequent pages. This field can be used to separate a notification into multiple
4750 * sections.
4751 *
4752 * @param pages a list of notifications
4753 * @return this object for method chaining
4754 * @see android.app.Notification.WearableExtender#getPages
4755 */
4756 public WearableExtender addPages(List<Notification> pages) {
4757 mPages.addAll(pages);
4758 return this;
4759 }
4760
4761 /**
4762 * Clear all additional pages present on this builder.
4763 * @return this object for method chaining.
4764 * @see #addPage
4765 */
4766 public WearableExtender clearPages() {
4767 mPages.clear();
4768 return this;
4769 }
4770
4771 /**
4772 * Get the array of additional pages of content for displaying this notification. The
4773 * current notification forms the first page, and elements within this array form
4774 * subsequent pages. This field can be used to separate a notification into multiple
4775 * sections.
4776 * @return the pages for this notification
4777 */
4778 public List<Notification> getPages() {
4779 return mPages;
4780 }
4781
4782 /**
4783 * Set a background image to be displayed behind the notification content.
4784 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4785 * will work with any notification style.
4786 *
4787 * @param background the background bitmap
4788 * @return this object for method chaining
4789 * @see android.app.Notification.WearableExtender#getBackground
4790 */
4791 public WearableExtender setBackground(Bitmap background) {
4792 mBackground = background;
4793 return this;
4794 }
4795
4796 /**
4797 * Get a background image to be displayed behind the notification content.
4798 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4799 * will work with any notification style.
4800 *
4801 * @return the background image
4802 * @see android.app.Notification.WearableExtender#setBackground
4803 */
4804 public Bitmap getBackground() {
4805 return mBackground;
4806 }
4807
4808 /**
4809 * Set an icon that goes with the content of this notification.
4810 */
4811 public WearableExtender setContentIcon(int icon) {
4812 mContentIcon = icon;
4813 return this;
4814 }
4815
4816 /**
4817 * Get an icon that goes with the content of this notification.
4818 */
4819 public int getContentIcon() {
4820 return mContentIcon;
4821 }
4822
4823 /**
4824 * Set the gravity that the content icon should have within the notification display.
4825 * Supported values include {@link android.view.Gravity#START} and
4826 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4827 * @see #setContentIcon
4828 */
4829 public WearableExtender setContentIconGravity(int contentIconGravity) {
4830 mContentIconGravity = contentIconGravity;
4831 return this;
4832 }
4833
4834 /**
4835 * Get the gravity that the content icon should have within the notification display.
4836 * Supported values include {@link android.view.Gravity#START} and
4837 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4838 * @see #getContentIcon
4839 */
4840 public int getContentIconGravity() {
4841 return mContentIconGravity;
4842 }
4843
4844 /**
4845 * Set an action from this notification's actions to be clickable with the content of
Griff Hazen14f57992014-05-26 09:07:14 -07004846 * this notification. This action will no longer display separately from the
4847 * notification's content.
4848 *
Griff Hazenca48d352014-05-28 22:37:13 -07004849 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004850 * set, although the list of available actions comes from the main notification and not
4851 * from the child page's notification.
4852 *
4853 * @param actionIndex The index of the action to hoist onto the current notification page.
4854 * If wearable actions were added to the main notification, this index
4855 * will apply to that list, otherwise it will apply to the regular
4856 * actions list.
Griff Hazen61a9e862014-05-22 16:05:19 -07004857 */
4858 public WearableExtender setContentAction(int actionIndex) {
4859 mContentActionIndex = actionIndex;
4860 return this;
4861 }
4862
4863 /**
Griff Hazenca48d352014-05-28 22:37:13 -07004864 * Get the index of the notification action, if any, that was specified as being clickable
4865 * with the content of this notification. This action will no longer display separately
Griff Hazen14f57992014-05-26 09:07:14 -07004866 * from the notification's content.
Griff Hazen61a9e862014-05-22 16:05:19 -07004867 *
Griff Hazenca48d352014-05-28 22:37:13 -07004868 * <p>For notifications with multiple pages, child pages can also have content actions
Griff Hazen14f57992014-05-26 09:07:14 -07004869 * set, although the list of available actions comes from the main notification and not
4870 * from the child page's notification.
4871 *
4872 * <p>If wearable specific actions were added to the main notification, this index will
4873 * apply to that list, otherwise it will apply to the regular actions list.
Griff Hazenca48d352014-05-28 22:37:13 -07004874 *
4875 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
Griff Hazen61a9e862014-05-22 16:05:19 -07004876 */
4877 public int getContentAction() {
4878 return mContentActionIndex;
4879 }
4880
4881 /**
4882 * Set the gravity that this notification should have within the available viewport space.
4883 * Supported values include {@link android.view.Gravity#TOP},
4884 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4885 * The default value is {@link android.view.Gravity#BOTTOM}.
4886 */
4887 public WearableExtender setGravity(int gravity) {
4888 mGravity = gravity;
4889 return this;
4890 }
4891
4892 /**
4893 * Get the gravity that this notification should have within the available viewport space.
4894 * Supported values include {@link android.view.Gravity#TOP},
4895 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4896 * The default value is {@link android.view.Gravity#BOTTOM}.
4897 */
4898 public int getGravity() {
4899 return mGravity;
4900 }
4901
4902 /**
4903 * Set the custom size preset for the display of this notification out of the available
4904 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4905 * {@link #SIZE_LARGE}.
4906 * <p>Some custom size presets are only applicable for custom display notifications created
4907 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4908 * documentation for the preset in question. See also
4909 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4910 */
4911 public WearableExtender setCustomSizePreset(int sizePreset) {
4912 mCustomSizePreset = sizePreset;
4913 return this;
4914 }
4915
4916 /**
4917 * Get the custom size preset for the display of this notification out of the available
4918 * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4919 * {@link #SIZE_LARGE}.
4920 * <p>Some custom size presets are only applicable for custom display notifications created
4921 * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4922 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4923 */
4924 public int getCustomSizePreset() {
4925 return mCustomSizePreset;
4926 }
4927
4928 /**
4929 * Set the custom height in pixels for the display of this notification's content.
4930 * <p>This option is only available for custom display notifications created
4931 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4932 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4933 * {@link #getCustomContentHeight}.
4934 */
4935 public WearableExtender setCustomContentHeight(int height) {
4936 mCustomContentHeight = height;
4937 return this;
4938 }
4939
4940 /**
4941 * Get the custom height in pixels for the display of this notification's content.
4942 * <p>This option is only available for custom display notifications created
4943 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4944 * {@link #setCustomContentHeight}.
4945 */
4946 public int getCustomContentHeight() {
4947 return mCustomContentHeight;
4948 }
4949
4950 /**
4951 * Set whether the scrolling position for the contents of this notification should start
4952 * at the bottom of the contents instead of the top when the contents are too long to
4953 * display within the screen. Default is false (start scroll at the top).
4954 */
4955 public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4956 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4957 return this;
4958 }
4959
4960 /**
4961 * Get whether the scrolling position for the contents of this notification should start
4962 * at the bottom of the contents instead of the top when the contents are too long to
4963 * display within the screen. Default is false (start scroll at the top).
4964 */
4965 public boolean getStartScrollBottom() {
4966 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4967 }
4968
4969 /**
4970 * Set whether the content intent is available when the wearable device is not connected
4971 * to a companion device. The user can still trigger this intent when the wearable device
4972 * is offline, but a visual hint will indicate that the content intent may not be available.
4973 * Defaults to true.
4974 */
4975 public WearableExtender setContentIntentAvailableOffline(
4976 boolean contentIntentAvailableOffline) {
4977 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4978 return this;
4979 }
4980
4981 /**
4982 * Get whether the content intent is available when the wearable device is not connected
4983 * to a companion device. The user can still trigger this intent when the wearable device
4984 * is offline, but a visual hint will indicate that the content intent may not be available.
4985 * Defaults to true.
4986 */
4987 public boolean getContentIntentAvailableOffline() {
4988 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
4989 }
4990
4991 /**
4992 * Set a hint that this notification's icon should not be displayed.
4993 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
4994 * @return this object for method chaining
4995 */
4996 public WearableExtender setHintHideIcon(boolean hintHideIcon) {
4997 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
4998 return this;
4999 }
5000
5001 /**
5002 * Get a hint that this notification's icon should not be displayed.
5003 * @return {@code true} if this icon should not be displayed, false otherwise.
5004 * The default value is {@code false} if this was never set.
5005 */
5006 public boolean getHintHideIcon() {
5007 return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
5008 }
5009
5010 /**
5011 * Set a visual hint that only the background image of this notification should be
5012 * displayed, and other semantic content should be hidden. This hint is only applicable
5013 * to sub-pages added using {@link #addPage}.
5014 */
5015 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
5016 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
5017 return this;
5018 }
5019
5020 /**
5021 * Get a visual hint that only the background image of this notification should be
5022 * displayed, and other semantic content should be hidden. This hint is only applicable
5023 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
5024 */
5025 public boolean getHintShowBackgroundOnly() {
5026 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
5027 }
5028
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005029 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005030 * Set a hint that this notification's background should not be clipped if possible,
5031 * and should instead be resized to fully display on the screen, retaining the aspect
5032 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005033 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
5034 * @return this object for method chaining
5035 */
5036 public WearableExtender setHintAvoidBackgroundClipping(
5037 boolean hintAvoidBackgroundClipping) {
5038 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
5039 return this;
5040 }
5041
5042 /**
Griff Hazen9c5be4e2014-11-17 17:47:50 -08005043 * Get a hint that this notification's background should not be clipped if possible,
5044 * and should instead be resized to fully display on the screen, retaining the aspect
5045 * ratio of the image. This can be useful for images like barcodes or qr codes.
Griff Hazen5f2edfc2014-09-29 16:28:44 -07005046 * @return {@code true} if it's ok if the background is clipped on the screen, false
5047 * otherwise. The default value is {@code false} if this was never set.
5048 */
5049 public boolean getHintAvoidBackgroundClipping() {
5050 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5051 }
5052
5053 /**
5054 * Set a hint that the screen should remain on for at least this duration when
5055 * this notification is displayed on the screen.
5056 * @param timeout The requested screen timeout in milliseconds. Can also be either
5057 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5058 * @return this object for method chaining
5059 */
5060 public WearableExtender setHintScreenTimeout(int timeout) {
5061 mHintScreenTimeout = timeout;
5062 return this;
5063 }
5064
5065 /**
5066 * Get the duration, in milliseconds, that the screen should remain on for
5067 * when this notification is displayed.
5068 * @return the duration in milliseconds if > 0, or either one of the sentinel values
5069 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5070 */
5071 public int getHintScreenTimeout() {
5072 return mHintScreenTimeout;
5073 }
5074
Griff Hazen61a9e862014-05-22 16:05:19 -07005075 private void setFlag(int mask, boolean value) {
5076 if (value) {
5077 mFlags |= mask;
5078 } else {
5079 mFlags &= ~mask;
5080 }
5081 }
5082 }
5083
5084 /**
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005085 * <p>Helper class to add Android Auto extensions to notifications. To create a notification
5086 * with car extensions:
5087 *
5088 * <ol>
5089 * <li>Create an {@link Notification.Builder}, setting any desired
5090 * properties.
5091 * <li>Create a {@link CarExtender}.
5092 * <li>Set car-specific properties using the {@code add} and {@code set} methods of
5093 * {@link CarExtender}.
5094 * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
5095 * to apply the extensions to a notification.
5096 * </ol>
5097 *
5098 * <pre class="prettyprint">
5099 * Notification notification = new Notification.Builder(context)
5100 * ...
5101 * .extend(new CarExtender()
5102 * .set*(...))
5103 * .build();
5104 * </pre>
5105 *
5106 * <p>Car extensions can be accessed on an existing notification by using the
5107 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
5108 * to access values.
5109 */
5110 public static final class CarExtender implements Extender {
5111 private static final String TAG = "CarExtender";
5112
5113 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
5114 private static final String EXTRA_LARGE_ICON = "large_icon";
5115 private static final String EXTRA_CONVERSATION = "car_conversation";
5116 private static final String EXTRA_COLOR = "app_color";
5117
5118 private Bitmap mLargeIcon;
5119 private UnreadConversation mUnreadConversation;
5120 private int mColor = Notification.COLOR_DEFAULT;
5121
5122 /**
5123 * Create a {@link CarExtender} with default options.
5124 */
5125 public CarExtender() {
5126 }
5127
5128 /**
5129 * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
5130 *
5131 * @param notif The notification from which to copy options.
5132 */
5133 public CarExtender(Notification notif) {
5134 Bundle carBundle = notif.extras == null ?
5135 null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
5136 if (carBundle != null) {
5137 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
5138 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
5139
5140 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
5141 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
5142 }
5143 }
5144
5145 /**
5146 * Apply car extensions to a notification that is being built. This is typically called by
5147 * the {@link Notification.Builder#extend(Notification.Extender)}
5148 * method of {@link Notification.Builder}.
5149 */
5150 @Override
5151 public Notification.Builder extend(Notification.Builder builder) {
5152 Bundle carExtensions = new Bundle();
5153
5154 if (mLargeIcon != null) {
5155 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
5156 }
5157 if (mColor != Notification.COLOR_DEFAULT) {
5158 carExtensions.putInt(EXTRA_COLOR, mColor);
5159 }
5160
5161 if (mUnreadConversation != null) {
5162 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
5163 carExtensions.putBundle(EXTRA_CONVERSATION, b);
5164 }
5165
5166 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
5167 return builder;
5168 }
5169
5170 /**
5171 * Sets the accent color to use when Android Auto presents the notification.
5172 *
5173 * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
5174 * to accent the displayed notification. However, not all colors are acceptable in an
5175 * automotive setting. This method can be used to override the color provided in the
5176 * notification in such a situation.
5177 */
Tor Norbye80756e32015-03-02 09:39:27 -08005178 public CarExtender setColor(@ColorInt int color) {
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005179 mColor = color;
5180 return this;
5181 }
5182
5183 /**
5184 * Gets the accent color.
5185 *
Julia Reynoldsd9228f12015-10-20 10:37:27 -04005186 * @see #setColor
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005187 */
Tor Norbye80756e32015-03-02 09:39:27 -08005188 @ColorInt
Zhen Yu Song9d5528b2014-11-14 15:34:39 -08005189 public int getColor() {
5190 return mColor;
5191 }
5192
5193 /**
5194 * Sets the large icon of the car notification.
5195 *
5196 * If no large icon is set in the extender, Android Auto will display the icon
5197 * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
5198 *
5199 * @param largeIcon The large icon to use in the car notification.
5200 * @return This object for method chaining.
5201 */
5202 public CarExtender setLargeIcon(Bitmap largeIcon) {
5203 mLargeIcon = largeIcon;
5204 return this;
5205 }
5206
5207 /**
5208 * Gets the large icon used in this car notification, or null if no icon has been set.
5209 *
5210 * @return The large icon for the car notification.
5211 * @see CarExtender#setLargeIcon
5212 */
5213 public Bitmap getLargeIcon() {
5214 return mLargeIcon;
5215 }
5216
5217 /**
5218 * Sets the unread conversation in a message notification.
5219 *
5220 * @param unreadConversation The unread part of the conversation this notification conveys.
5221 * @return This object for method chaining.
5222 */
5223 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
5224 mUnreadConversation = unreadConversation;
5225 return this;
5226 }
5227
5228 /**
5229 * Returns the unread conversation conveyed by this notification.
5230 * @see #setUnreadConversation(UnreadConversation)
5231 */
5232 public UnreadConversation getUnreadConversation() {
5233 return mUnreadConversation;
5234 }
5235
5236 /**
5237 * A class which holds the unread messages from a conversation.
5238 */
5239 public static class UnreadConversation {
5240 private static final String KEY_AUTHOR = "author";
5241 private static final String KEY_TEXT = "text";
5242 private static final String KEY_MESSAGES = "messages";
5243 private static final String KEY_REMOTE_INPUT = "remote_input";
5244 private static final String KEY_ON_REPLY = "on_reply";
5245 private static final String KEY_ON_READ = "on_read";
5246 private static final String KEY_PARTICIPANTS = "participants";
5247 private static final String KEY_TIMESTAMP = "timestamp";
5248
5249 private final String[] mMessages;
5250 private final RemoteInput mRemoteInput;
5251 private final PendingIntent mReplyPendingIntent;
5252 private final PendingIntent mReadPendingIntent;
5253 private final String[] mParticipants;
5254 private final long mLatestTimestamp;
5255
5256 UnreadConversation(String[] messages, RemoteInput remoteInput,
5257 PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
5258 String[] participants, long latestTimestamp) {
5259 mMessages = messages;
5260 mRemoteInput = remoteInput;
5261 mReadPendingIntent = readPendingIntent;
5262 mReplyPendingIntent = replyPendingIntent;
5263 mParticipants = participants;
5264 mLatestTimestamp = latestTimestamp;
5265 }
5266
5267 /**
5268 * Gets the list of messages conveyed by this notification.
5269 */
5270 public String[] getMessages() {
5271 return mMessages;
5272 }
5273
5274 /**
5275 * Gets the remote input that will be used to convey the response to a message list, or
5276 * null if no such remote input exists.
5277 */
5278 public RemoteInput getRemoteInput() {
5279 return mRemoteInput;
5280 }
5281
5282 /**
5283 * Gets the pending intent that will be triggered when the user replies to this
5284 * notification.
5285 */
5286 public PendingIntent getReplyPendingIntent() {
5287 return mReplyPendingIntent;
5288 }
5289
5290 /**
5291 * Gets the pending intent that Android Auto will send after it reads aloud all messages
5292 * in this object's message list.
5293 */
5294 public PendingIntent getReadPendingIntent() {
5295 return mReadPendingIntent;
5296 }
5297
5298 /**
5299 * Gets the participants in the conversation.
5300 */
5301 public String[] getParticipants() {
5302 return mParticipants;
5303 }
5304
5305 /**
5306 * Gets the firs participant in the conversation.
5307 */
5308 public String getParticipant() {
5309 return mParticipants.length > 0 ? mParticipants[0] : null;
5310 }
5311
5312 /**
5313 * Gets the timestamp of the conversation.
5314 */
5315 public long getLatestTimestamp() {
5316 return mLatestTimestamp;
5317 }
5318
5319 Bundle getBundleForUnreadConversation() {
5320 Bundle b = new Bundle();
5321 String author = null;
5322 if (mParticipants != null && mParticipants.length > 1) {
5323 author = mParticipants[0];
5324 }
5325 Parcelable[] messages = new Parcelable[mMessages.length];
5326 for (int i = 0; i < messages.length; i++) {
5327 Bundle m = new Bundle();
5328 m.putString(KEY_TEXT, mMessages[i]);
5329 m.putString(KEY_AUTHOR, author);
5330 messages[i] = m;
5331 }
5332 b.putParcelableArray(KEY_MESSAGES, messages);
5333 if (mRemoteInput != null) {
5334 b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
5335 }
5336 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
5337 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
5338 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
5339 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
5340 return b;
5341 }
5342
5343 static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
5344 if (b == null) {
5345 return null;
5346 }
5347 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
5348 String[] messages = null;
5349 if (parcelableMessages != null) {
5350 String[] tmp = new String[parcelableMessages.length];
5351 boolean success = true;
5352 for (int i = 0; i < tmp.length; i++) {
5353 if (!(parcelableMessages[i] instanceof Bundle)) {
5354 success = false;
5355 break;
5356 }
5357 tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
5358 if (tmp[i] == null) {
5359 success = false;
5360 break;
5361 }
5362 }
5363 if (success) {
5364 messages = tmp;
5365 } else {
5366 return null;
5367 }
5368 }
5369
5370 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
5371 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
5372
5373 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
5374
5375 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
5376 if (participants == null || participants.length != 1) {
5377 return null;
5378 }
5379
5380 return new UnreadConversation(messages,
5381 remoteInput,
5382 onReply,
5383 onRead,
5384 participants, b.getLong(KEY_TIMESTAMP));
5385 }
5386 };
5387
5388 /**
5389 * Builder class for {@link CarExtender.UnreadConversation} objects.
5390 */
5391 public static class Builder {
5392 private final List<String> mMessages = new ArrayList<String>();
5393 private final String mParticipant;
5394 private RemoteInput mRemoteInput;
5395 private PendingIntent mReadPendingIntent;
5396 private PendingIntent mReplyPendingIntent;
5397 private long mLatestTimestamp;
5398
5399 /**
5400 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
5401 *
5402 * @param name The name of the other participant in the conversation.
5403 */
5404 public Builder(String name) {
5405 mParticipant = name;
5406 }
5407
5408 /**
5409 * Appends a new unread message to the list of messages for this conversation.
5410 *
5411 * The messages should be added from oldest to newest.
5412 *
5413 * @param message The text of the new unread message.
5414 * @return This object for method chaining.
5415 */
5416 public Builder addMessage(String message) {
5417 mMessages.add(message);
5418 return this;
5419 }
5420
5421 /**
5422 * Sets the pending intent and remote input which will convey the reply to this
5423 * notification.
5424 *
5425 * @param pendingIntent The pending intent which will be triggered on a reply.
5426 * @param remoteInput The remote input parcelable which will carry the reply.
5427 * @return This object for method chaining.
5428 *
5429 * @see CarExtender.UnreadConversation#getRemoteInput
5430 * @see CarExtender.UnreadConversation#getReplyPendingIntent
5431 */
5432 public Builder setReplyAction(
5433 PendingIntent pendingIntent, RemoteInput remoteInput) {
5434 mRemoteInput = remoteInput;
5435 mReplyPendingIntent = pendingIntent;
5436
5437 return this;
5438 }
5439
5440 /**
5441 * Sets the pending intent that will be sent once the messages in this notification
5442 * are read.
5443 *
5444 * @param pendingIntent The pending intent to use.
5445 * @return This object for method chaining.
5446 */
5447 public Builder setReadPendingIntent(PendingIntent pendingIntent) {
5448 mReadPendingIntent = pendingIntent;
5449 return this;
5450 }
5451
5452 /**
5453 * Sets the timestamp of the most recent message in an unread conversation.
5454 *
5455 * If a messaging notification has been posted by your application and has not
5456 * yet been cancelled, posting a later notification with the same id and tag
5457 * but without a newer timestamp may result in Android Auto not displaying a
5458 * heads up notification for the later notification.
5459 *
5460 * @param timestamp The timestamp of the most recent message in the conversation.
5461 * @return This object for method chaining.
5462 */
5463 public Builder setLatestTimestamp(long timestamp) {
5464 mLatestTimestamp = timestamp;
5465 return this;
5466 }
5467
5468 /**
5469 * Builds a new unread conversation object.
5470 *
5471 * @return The new unread conversation object.
5472 */
5473 public UnreadConversation build() {
5474 String[] messages = mMessages.toArray(new String[mMessages.size()]);
5475 String[] participants = { mParticipant };
5476 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
5477 mReadPendingIntent, participants, mLatestTimestamp);
5478 }
5479 }
5480 }
5481
5482 /**
Griff Hazen61a9e862014-05-22 16:05:19 -07005483 * Get an array of Notification objects from a parcelable array bundle field.
5484 * Update the bundle to have a typed array so fetches in the future don't need
5485 * to do an array copy.
5486 */
5487 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5488 Parcelable[] array = bundle.getParcelableArray(key);
5489 if (array instanceof Notification[] || array == null) {
5490 return (Notification[]) array;
5491 }
5492 Notification[] typedArray = Arrays.copyOf(array, array.length,
5493 Notification[].class);
5494 bundle.putParcelableArray(key, typedArray);
5495 return typedArray;
5496 }
Christoph Studer4600f9b2014-07-22 22:44:43 +02005497
5498 private static class BuilderRemoteViews extends RemoteViews {
5499 public BuilderRemoteViews(Parcel parcel) {
5500 super(parcel);
5501 }
5502
Kenny Guy77320062014-08-27 21:37:15 +01005503 public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5504 super(appInfo, layoutId);
Christoph Studer4600f9b2014-07-22 22:44:43 +02005505 }
5506
5507 @Override
5508 public BuilderRemoteViews clone() {
5509 Parcel p = Parcel.obtain();
5510 writeToParcel(p, 0);
5511 p.setDataPosition(0);
5512 BuilderRemoteViews brv = new BuilderRemoteViews(p);
5513 p.recycle();
5514 return brv;
5515 }
5516 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005517}