Notification.java revision e191f901d9af36f5eeda99758a8319829a9a5b88
1/*
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
19import android.annotation.IntDef;
20import android.annotation.SdkConstant;
21import android.annotation.SdkConstant.SdkConstantType;
22import android.content.Context;
23import android.content.Intent;
24import android.content.res.Resources;
25import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.PorterDuff;
28import android.graphics.drawable.Drawable;
29import android.media.AudioAttributes;
30import android.media.AudioManager;
31import android.media.session.MediaSession;
32import android.net.Uri;
33import android.os.BadParcelableException;
34import android.os.Bundle;
35import android.os.Parcel;
36import android.os.Parcelable;
37import android.os.SystemClock;
38import android.os.UserHandle;
39import android.os.UserManager;
40import android.text.TextUtils;
41import android.util.Log;
42import android.util.TypedValue;
43import android.view.Gravity;
44import android.view.View;
45import android.widget.ProgressBar;
46import android.widget.RemoteViews;
47
48import com.android.internal.R;
49import com.android.internal.util.NotificationColorUtil;
50
51import java.lang.annotation.Retention;
52import java.lang.annotation.RetentionPolicy;
53import java.text.NumberFormat;
54import java.util.ArrayList;
55import java.util.Arrays;
56import java.util.Collections;
57import java.util.List;
58
59/**
60 * A class that represents how a persistent notification is to be presented to
61 * the user using the {@link android.app.NotificationManager}.
62 *
63 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
64 * easier to construct Notifications.</p>
65 *
66 * <div class="special reference">
67 * <h3>Developer Guides</h3>
68 * <p>For a guide to creating notifications, read the
69 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
70 * developer guide.</p>
71 * </div>
72 */
73public class Notification implements Parcelable
74{
75    private static final String TAG = "Notification";
76
77    /**
78     * An activity that provides a user interface for adjusting notification preferences for its
79     * containing application. Optional but recommended for apps that post
80     * {@link android.app.Notification Notifications}.
81     */
82    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
83    public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
84            = "android.intent.category.NOTIFICATION_PREFERENCES";
85
86    /**
87     * Use all default values (where applicable).
88     */
89    public static final int DEFAULT_ALL = ~0;
90
91    /**
92     * Use the default notification sound. This will ignore any given
93     * {@link #sound}.
94     *
95     * <p>
96     * A notification that is noisy is more likely to be presented as a heads-up notification.
97     * </p>
98     *
99     * @see #defaults
100     */
101
102    public static final int DEFAULT_SOUND = 1;
103
104    /**
105     * Use the default notification vibrate. This will ignore any given
106     * {@link #vibrate}. Using phone vibration requires the
107     * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
108     *
109     * <p>
110     * A notification that vibrates is more likely to be presented as a heads-up notification.
111     * </p>
112     *
113     * @see #defaults
114     */
115
116    public static final int DEFAULT_VIBRATE = 2;
117
118    /**
119     * Use the default notification lights. This will ignore the
120     * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
121     * {@link #ledOnMS}.
122     *
123     * @see #defaults
124     */
125
126    public static final int DEFAULT_LIGHTS = 4;
127
128    /**
129     * A timestamp related to this notification, in milliseconds since the epoch.
130     *
131     * Default value: {@link System#currentTimeMillis() Now}.
132     *
133     * Choose a timestamp that will be most relevant to the user. For most finite events, this
134     * corresponds to the time the event happened (or will happen, in the case of events that have
135     * yet to occur but about which the user is being informed). Indefinite events should be
136     * timestamped according to when the activity began.
137     *
138     * Some examples:
139     *
140     * <ul>
141     *   <li>Notification of a new chat message should be stamped when the message was received.</li>
142     *   <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
143     *   <li>Notification of a completed file download should be stamped when the download finished.</li>
144     *   <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
145     *   <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
146     *   <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
147     * </ul>
148     *
149     */
150    public long when;
151
152    /**
153     * The resource id of a drawable to use as the icon in the status bar.
154     * This is required; notifications with an invalid icon resource will not be shown.
155     */
156    public int icon;
157
158    /**
159     * If the icon in the status bar is to have more than one level, you can set this.  Otherwise,
160     * leave it at its default value of 0.
161     *
162     * @see android.widget.ImageView#setImageLevel
163     * @see android.graphics.drawable.Drawable#setLevel
164     */
165    public int iconLevel;
166
167    /**
168     * The number of events that this notification represents. For example, in a new mail
169     * notification, this could be the number of unread messages.
170     *
171     * The system may or may not use this field to modify the appearance of the notification. For
172     * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
173     * superimposed over the icon in the status bar. Starting with
174     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
175     * {@link Notification.Builder} has displayed the number in the expanded notification view.
176     *
177     * If the number is 0 or negative, it is never shown.
178     */
179    public int number;
180
181    /**
182     * The intent to execute when the expanded status entry is clicked.  If
183     * this is an activity, it must include the
184     * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
185     * that you take care of task management as described in the
186     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
187     * Stack</a> document.  In particular, make sure to read the notification section
188     * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
189     * Notifications</a> for the correct ways to launch an application from a
190     * notification.
191     */
192    public PendingIntent contentIntent;
193
194    /**
195     * The intent to execute when the notification is explicitly dismissed by the user, either with
196     * the "Clear All" button or by swiping it away individually.
197     *
198     * This probably shouldn't be launching an activity since several of those will be sent
199     * at the same time.
200     */
201    public PendingIntent deleteIntent;
202
203    /**
204     * An intent to launch instead of posting the notification to the status bar.
205     *
206     * <p>
207     * The system UI may choose to display a heads-up notification, instead of
208     * launching this intent, while the user is using the device.
209     * </p>
210     *
211     * @see Notification.Builder#setFullScreenIntent
212     */
213    public PendingIntent fullScreenIntent;
214
215    /**
216     * Text that summarizes this notification for accessibility services.
217     *
218     * As of the L release, this text is no longer shown on screen, but it is still useful to
219     * accessibility services (where it serves as an audible announcement of the notification's
220     * appearance).
221     *
222     * @see #tickerView
223     */
224    public CharSequence tickerText;
225
226    /**
227     * Formerly, a view showing the {@link #tickerText}.
228     *
229     * No longer displayed in the status bar as of API 21.
230     */
231    @Deprecated
232    public RemoteViews tickerView;
233
234    /**
235     * The view that will represent this notification in the expanded status bar.
236     */
237    public RemoteViews contentView;
238
239    /**
240     * A large-format version of {@link #contentView}, giving the Notification an
241     * opportunity to show more detail. The system UI may choose to show this
242     * instead of the normal content view at its discretion.
243     */
244    public RemoteViews bigContentView;
245
246
247    /**
248     * @hide
249     * A medium-format version of {@link #contentView}, providing the Notification an
250     * opportunity to add action buttons to contentView. At its discretion, the system UI may
251     * choose to show this as a heads-up notification, which will pop up so the user can see
252     * it without leaving their current activity.
253     */
254    public RemoteViews headsUpContentView;
255
256    /**
257     * The bitmap that may escape the bounds of the panel and bar.
258     */
259    public Bitmap largeIcon;
260
261    /**
262     * The sound to play.
263     *
264     * <p>
265     * A notification that is noisy is more likely to be presented as a heads-up notification.
266     * </p>
267     *
268     * <p>
269     * To play the default notification sound, see {@link #defaults}.
270     * </p>
271     */
272    public Uri sound;
273
274    /**
275     * Use this constant as the value for audioStreamType to request that
276     * the default stream type for notifications be used.  Currently the
277     * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
278     *
279     * @deprecated Use {@link #audioAttributes} instead.
280     */
281    public static final int STREAM_DEFAULT = -1;
282
283    /**
284     * The audio stream type to use when playing the sound.
285     * Should be one of the STREAM_ constants from
286     * {@link android.media.AudioManager}.
287     *
288     * @deprecated Use {@link #audioAttributes} instead.
289     */
290    public int audioStreamType = STREAM_DEFAULT;
291
292    /**
293     * The default value of {@link #audioAttributes}.
294     */
295    public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
296            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
297            .setUsage(AudioAttributes.USAGE_NOTIFICATION)
298            .build();
299
300    /**
301     * The {@link AudioAttributes audio attributes} to use when playing the sound.
302     */
303    public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
304
305    /**
306     * The pattern with which to vibrate.
307     *
308     * <p>
309     * To vibrate the default pattern, see {@link #defaults}.
310     * </p>
311     *
312     * <p>
313     * A notification that vibrates is more likely to be presented as a heads-up notification.
314     * </p>
315     *
316     * @see android.os.Vibrator#vibrate(long[],int)
317     */
318    public long[] vibrate;
319
320    /**
321     * The color of the led.  The hardware will do its best approximation.
322     *
323     * @see #FLAG_SHOW_LIGHTS
324     * @see #flags
325     */
326    public int ledARGB;
327
328    /**
329     * The number of milliseconds for the LED to be on while it's flashing.
330     * The hardware will do its best approximation.
331     *
332     * @see #FLAG_SHOW_LIGHTS
333     * @see #flags
334     */
335    public int ledOnMS;
336
337    /**
338     * The number of milliseconds for the LED to be off while it's flashing.
339     * The hardware will do its best approximation.
340     *
341     * @see #FLAG_SHOW_LIGHTS
342     * @see #flags
343     */
344    public int ledOffMS;
345
346    /**
347     * Specifies which values should be taken from the defaults.
348     * <p>
349     * To set, OR the desired from {@link #DEFAULT_SOUND},
350     * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
351     * values, use {@link #DEFAULT_ALL}.
352     * </p>
353     */
354    public int defaults;
355
356    /**
357     * Bit to be bitwise-ored into the {@link #flags} field that should be
358     * set if you want the LED on for this notification.
359     * <ul>
360     * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
361     *      or 0 for both ledOnMS and ledOffMS.</li>
362     * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
363     * <li>To flash the LED, pass the number of milliseconds that it should
364     *      be on and off to ledOnMS and ledOffMS.</li>
365     * </ul>
366     * <p>
367     * Since hardware varies, you are not guaranteed that any of the values
368     * you pass are honored exactly.  Use the system defaults (TODO) if possible
369     * because they will be set to values that work on any given hardware.
370     * <p>
371     * The alpha channel must be set for forward compatibility.
372     *
373     */
374    public static final int FLAG_SHOW_LIGHTS        = 0x00000001;
375
376    /**
377     * Bit to be bitwise-ored into the {@link #flags} field that should be
378     * set if this notification is in reference to something that is ongoing,
379     * like a phone call.  It should not be set if this notification is in
380     * reference to something that happened at a particular point in time,
381     * like a missed phone call.
382     */
383    public static final int FLAG_ONGOING_EVENT      = 0x00000002;
384
385    /**
386     * Bit to be bitwise-ored into the {@link #flags} field that if set,
387     * the audio will be repeated until the notification is
388     * cancelled or the notification window is opened.
389     */
390    public static final int FLAG_INSISTENT          = 0x00000004;
391
392    /**
393     * Bit to be bitwise-ored into the {@link #flags} field that should be
394     * set if you would only like the sound, vibrate and ticker to be played
395     * if the notification was not already showing.
396     */
397    public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;
398
399    /**
400     * Bit to be bitwise-ored into the {@link #flags} field that should be
401     * set if the notification should be canceled when it is clicked by the
402     * user.
403
404     */
405    public static final int FLAG_AUTO_CANCEL        = 0x00000010;
406
407    /**
408     * Bit to be bitwise-ored into the {@link #flags} field that should be
409     * set if the notification should not be canceled when the user clicks
410     * the Clear all button.
411     */
412    public static final int FLAG_NO_CLEAR           = 0x00000020;
413
414    /**
415     * Bit to be bitwise-ored into the {@link #flags} field that should be
416     * set if this notification represents a currently running service.  This
417     * will normally be set for you by {@link Service#startForeground}.
418     */
419    public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
420
421    /**
422     * Obsolete flag indicating high-priority notifications; use the priority field instead.
423     *
424     * @deprecated Use {@link #priority} with a positive value.
425     */
426    public static final int FLAG_HIGH_PRIORITY      = 0x00000080;
427
428    /**
429     * Bit to be bitswise-ored into the {@link #flags} field that should be
430     * set if this notification is relevant to the current device only
431     * and it is not recommended that it bridge to other devices.
432     */
433    public static final int FLAG_LOCAL_ONLY         = 0x00000100;
434
435    /**
436     * Bit to be bitswise-ored into the {@link #flags} field that should be
437     * set if this notification is the group summary for a group of notifications.
438     * Grouped notifications may display in a cluster or stack on devices which
439     * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
440     */
441    public static final int FLAG_GROUP_SUMMARY      = 0x00000200;
442
443    public int flags;
444
445    /** @hide */
446    @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
447    @Retention(RetentionPolicy.SOURCE)
448    public @interface Priority {}
449
450    /**
451     * Default notification {@link #priority}. If your application does not prioritize its own
452     * notifications, use this value for all notifications.
453     */
454    public static final int PRIORITY_DEFAULT = 0;
455
456    /**
457     * Lower {@link #priority}, for items that are less important. The UI may choose to show these
458     * items smaller, or at a different position in the list, compared with your app's
459     * {@link #PRIORITY_DEFAULT} items.
460     */
461    public static final int PRIORITY_LOW = -1;
462
463    /**
464     * Lowest {@link #priority}; these items might not be shown to the user except under special
465     * circumstances, such as detailed notification logs.
466     */
467    public static final int PRIORITY_MIN = -2;
468
469    /**
470     * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
471     * show these items larger, or at a different position in notification lists, compared with
472     * your app's {@link #PRIORITY_DEFAULT} items.
473     */
474    public static final int PRIORITY_HIGH = 1;
475
476    /**
477     * Highest {@link #priority}, for your application's most important items that require the
478     * user's prompt attention or input.
479     */
480    public static final int PRIORITY_MAX = 2;
481
482    /**
483     * Relative priority for this notification.
484     *
485     * Priority is an indication of how much of the user's valuable attention should be consumed by
486     * this notification. Low-priority notifications may be hidden from the user in certain
487     * situations, while the user might be interrupted for a higher-priority notification. The
488     * system will make a determination about how to interpret this priority when presenting
489     * the notification.
490     *
491     * <p>
492     * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
493     * as a heads-up notification.
494     * </p>
495     *
496     */
497    @Priority
498    public int priority;
499
500    /**
501     * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
502     * to be applied by the standard Style templates when presenting this notification.
503     *
504     * The current template design constructs a colorful header image by overlaying the
505     * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
506     * ignored.
507     */
508    public int color = COLOR_DEFAULT;
509
510    /**
511     * Special value of {@link #color} telling the system not to decorate this notification with
512     * any special color but instead use default colors when presenting this notification.
513     */
514    public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
515
516    /**
517     * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
518     * the notification's presence and contents in untrusted situations (namely, on the secure
519     * lockscreen).
520     *
521     * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
522     * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
523     * shown in all situations, but the contents are only available if the device is unlocked for
524     * the appropriate user.
525     *
526     * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
527     * can be read even in an "insecure" context (that is, above a secure lockscreen).
528     * To modify the public version of this notification—for example, to redact some portions—see
529     * {@link Builder#setPublicVersion(Notification)}.
530     *
531     * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
532     * and ticker until the user has bypassed the lockscreen.
533     */
534    public int visibility;
535
536    public static final int VISIBILITY_PUBLIC = 1;
537    public static final int VISIBILITY_PRIVATE = 0;
538    public static final int VISIBILITY_SECRET = -1;
539
540    /**
541     * Notification category: incoming call (voice or video) or similar synchronous communication request.
542     */
543    public static final String CATEGORY_CALL = "call";
544
545    /**
546     * Notification category: incoming direct message (SMS, instant message, etc.).
547     */
548    public static final String CATEGORY_MESSAGE = "msg";
549
550    /**
551     * Notification category: asynchronous bulk message (email).
552     */
553    public static final String CATEGORY_EMAIL = "email";
554
555    /**
556     * Notification category: calendar event.
557     */
558    public static final String CATEGORY_EVENT = "event";
559
560    /**
561     * Notification category: promotion or advertisement.
562     */
563    public static final String CATEGORY_PROMO = "promo";
564
565    /**
566     * Notification category: alarm or timer.
567     */
568    public static final String CATEGORY_ALARM = "alarm";
569
570    /**
571     * Notification category: progress of a long-running background operation.
572     */
573    public static final String CATEGORY_PROGRESS = "progress";
574
575    /**
576     * Notification category: social network or sharing update.
577     */
578    public static final String CATEGORY_SOCIAL = "social";
579
580    /**
581     * Notification category: error in background operation or authentication status.
582     */
583    public static final String CATEGORY_ERROR = "err";
584
585    /**
586     * Notification category: media transport control for playback.
587     */
588    public static final String CATEGORY_TRANSPORT = "transport";
589
590    /**
591     * Notification category: system or device status update.  Reserved for system use.
592     */
593    public static final String CATEGORY_SYSTEM = "sys";
594
595    /**
596     * Notification category: indication of running background service.
597     */
598    public static final String CATEGORY_SERVICE = "service";
599
600    /**
601     * Notification category: a specific, timely recommendation for a single thing.
602     * For example, a news app might want to recommend a news story it believes the user will
603     * want to read next.
604     */
605    public static final String CATEGORY_RECOMMENDATION = "recommendation";
606
607    /**
608     * Notification category: ongoing information about device or contextual status.
609     */
610    public static final String CATEGORY_STATUS = "status";
611
612    /**
613     * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
614     * that best describes this Notification.  May be used by the system for ranking and filtering.
615     */
616    public String category;
617
618    private String mGroupKey;
619
620    /**
621     * Get the key used to group this notification into a cluster or stack
622     * with other notifications on devices which support such rendering.
623     */
624    public String getGroup() {
625        return mGroupKey;
626    }
627
628    private String mSortKey;
629
630    /**
631     * Get a sort key that orders this notification among other notifications from the
632     * same package. This can be useful if an external sort was already applied and an app
633     * would like to preserve this. Notifications will be sorted lexicographically using this
634     * value, although providing different priorities in addition to providing sort key may
635     * cause this value to be ignored.
636     *
637     * <p>This sort key can also be used to order members of a notification group. See
638     * {@link Builder#setGroup}.
639     *
640     * @see String#compareTo(String)
641     */
642    public String getSortKey() {
643        return mSortKey;
644    }
645
646    /**
647     * Additional semantic data to be carried around with this Notification.
648     * <p>
649     * The extras keys defined here are intended to capture the original inputs to {@link Builder}
650     * APIs, and are intended to be used by
651     * {@link android.service.notification.NotificationListenerService} implementations to extract
652     * detailed information from notification objects.
653     */
654    public Bundle extras = new Bundle();
655
656    /**
657     * {@link #extras} key: this is the title of the notification,
658     * as supplied to {@link Builder#setContentTitle(CharSequence)}.
659     */
660    public static final String EXTRA_TITLE = "android.title";
661
662    /**
663     * {@link #extras} key: this is the title of the notification when shown in expanded form,
664     * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
665     */
666    public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
667
668    /**
669     * {@link #extras} key: this is the main text payload, as supplied to
670     * {@link Builder#setContentText(CharSequence)}.
671     */
672    public static final String EXTRA_TEXT = "android.text";
673
674    /**
675     * {@link #extras} key: this is a third line of text, as supplied to
676     * {@link Builder#setSubText(CharSequence)}.
677     */
678    public static final String EXTRA_SUB_TEXT = "android.subText";
679
680    /**
681     * {@link #extras} key: this is a small piece of additional text as supplied to
682     * {@link Builder#setContentInfo(CharSequence)}.
683     */
684    public static final String EXTRA_INFO_TEXT = "android.infoText";
685
686    /**
687     * {@link #extras} key: this is a line of summary information intended to be shown
688     * alongside expanded notifications, as supplied to (e.g.)
689     * {@link BigTextStyle#setSummaryText(CharSequence)}.
690     */
691    public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
692
693    /**
694     * {@link #extras} key: this is the resource ID of the notification's main small icon, as
695     * supplied to {@link Builder#setSmallIcon(int)}.
696     */
697    public static final String EXTRA_SMALL_ICON = "android.icon";
698
699    /**
700     * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
701     * notification payload, as
702     * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
703     */
704    public static final String EXTRA_LARGE_ICON = "android.largeIcon";
705
706    /**
707     * {@link #extras} key: this is a bitmap to be used instead of the one from
708     * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
709     * shown in its expanded form, as supplied to
710     * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
711     */
712    public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
713
714    /**
715     * {@link #extras} key: this is the progress value supplied to
716     * {@link Builder#setProgress(int, int, boolean)}.
717     */
718    public static final String EXTRA_PROGRESS = "android.progress";
719
720    /**
721     * {@link #extras} key: this is the maximum value supplied to
722     * {@link Builder#setProgress(int, int, boolean)}.
723     */
724    public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
725
726    /**
727     * {@link #extras} key: whether the progress bar is indeterminate, supplied to
728     * {@link Builder#setProgress(int, int, boolean)}.
729     */
730    public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
731
732    /**
733     * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
734     * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
735     * {@link Builder#setUsesChronometer(boolean)}.
736     */
737    public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
738
739    /**
740     * {@link #extras} key: whether {@link #when} should be shown,
741     * as supplied to {@link Builder#setShowWhen(boolean)}.
742     */
743    public static final String EXTRA_SHOW_WHEN = "android.showWhen";
744
745    /**
746     * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
747     * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
748     */
749    public static final String EXTRA_PICTURE = "android.picture";
750
751    /**
752     * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
753     * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
754     */
755    public static final String EXTRA_TEXT_LINES = "android.textLines";
756
757    /**
758     * {@link #extras} key: A string representing the name of the specific
759     * {@link android.app.Notification.Style} used to create this notification.
760     */
761    public static final String EXTRA_TEMPLATE = "android.template";
762
763    /**
764     * {@link #extras} key: An array of people that this notification relates to, specified
765     * by contacts provider contact URI.
766     */
767    public static final String EXTRA_PEOPLE = "android.people";
768
769    /**
770     * {@link #extras} key: used to provide hints about the appropriateness of
771     * displaying this notification as a heads-up notification.
772     * @hide
773     */
774    public static final String EXTRA_AS_HEADS_UP = "headsup";
775
776    /**
777     * Allow certain system-generated notifications to appear before the device is provisioned.
778     * Only available to notifications coming from the android package.
779     * @hide
780     */
781    public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
782
783    /**
784     * {@link #extras} key: A
785     * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
786     * in the background when the notification is selected. The URI must point to an image stream
787     * suitable for passing into
788     * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
789     * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
790     * URI used for this purpose must require no permissions to read the image data.
791     */
792    public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
793
794    /**
795     * {@link #extras} key: A
796     * {@link android.media.session.MediaSession.Token} associated with a
797     * {@link android.app.Notification.MediaStyle} notification.
798     */
799    public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
800
801    /**
802     * {@link #extras} key: the indices of actions to be shown in the compact view,
803     * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
804     */
805    public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
806
807    /**
808     * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
809     * displayed in the heads up space.
810     *
811     * <p>
812     * If this notification has a {@link #fullScreenIntent}, then it will always launch the
813     * full-screen intent when posted.
814     * </p>
815     * @hide
816     */
817    public static final int HEADS_UP_NEVER = 0;
818
819    /**
820     * Default value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification may be
821     * displayed as a heads up.
822     * @hide
823     */
824    public static final int HEADS_UP_ALLOWED = 1;
825
826    /**
827     * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification is a
828     * good candidate for display as a heads up.
829     * @hide
830     */
831    public static final int HEADS_UP_REQUESTED = 2;
832
833    /**
834     * Structure to encapsulate a named action that can be shown as part of this notification.
835     * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
836     * selected by the user.
837     * <p>
838     * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
839     * or {@link Notification.Builder#addAction(Notification.Action)}
840     * to attach actions.
841     */
842    public static class Action implements Parcelable {
843        private final Bundle mExtras;
844        private final RemoteInput[] mRemoteInputs;
845
846        /**
847         * Small icon representing the action.
848         */
849        public int icon;
850
851        /**
852         * Title of the action.
853         */
854        public CharSequence title;
855
856        /**
857         * Intent to send when the user invokes this action. May be null, in which case the action
858         * may be rendered in a disabled presentation by the system UI.
859         */
860        public PendingIntent actionIntent;
861
862        private Action(Parcel in) {
863            icon = in.readInt();
864            title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
865            if (in.readInt() == 1) {
866                actionIntent = PendingIntent.CREATOR.createFromParcel(in);
867            }
868            mExtras = in.readBundle();
869            mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
870        }
871
872        /**
873         * Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}.
874         */
875        public Action(int icon, CharSequence title, PendingIntent intent) {
876            this(icon, title, intent, new Bundle(), null);
877        }
878
879        private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
880                RemoteInput[] remoteInputs) {
881            this.icon = icon;
882            this.title = title;
883            this.actionIntent = intent;
884            this.mExtras = extras != null ? extras : new Bundle();
885            this.mRemoteInputs = remoteInputs;
886        }
887
888        /**
889         * Get additional metadata carried around with this Action.
890         */
891        public Bundle getExtras() {
892            return mExtras;
893        }
894
895        /**
896         * Get the list of inputs to be collected from the user when this action is sent.
897         * May return null if no remote inputs were added.
898         */
899        public RemoteInput[] getRemoteInputs() {
900            return mRemoteInputs;
901        }
902
903        /**
904         * Builder class for {@link Action} objects.
905         */
906        public static final class Builder {
907            private final int mIcon;
908            private final CharSequence mTitle;
909            private final PendingIntent mIntent;
910            private final Bundle mExtras;
911            private ArrayList<RemoteInput> mRemoteInputs;
912
913            /**
914             * Construct a new builder for {@link Action} object.
915             * @param icon icon to show for this action
916             * @param title the title of the action
917             * @param intent the {@link PendingIntent} to fire when users trigger this action
918             */
919            public Builder(int icon, CharSequence title, PendingIntent intent) {
920                this(icon, title, intent, new Bundle(), null);
921            }
922
923            /**
924             * Construct a new builder for {@link Action} object using the fields from an
925             * {@link Action}.
926             * @param action the action to read fields from.
927             */
928            public Builder(Action action) {
929                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
930                        action.getRemoteInputs());
931            }
932
933            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
934                    RemoteInput[] remoteInputs) {
935                mIcon = icon;
936                mTitle = title;
937                mIntent = intent;
938                mExtras = extras;
939                if (remoteInputs != null) {
940                    mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
941                    Collections.addAll(mRemoteInputs, remoteInputs);
942                }
943            }
944
945            /**
946             * Merge additional metadata into this builder.
947             *
948             * <p>Values within the Bundle will replace existing extras values in this Builder.
949             *
950             * @see Notification.Action#extras
951             */
952            public Builder addExtras(Bundle extras) {
953                if (extras != null) {
954                    mExtras.putAll(extras);
955                }
956                return this;
957            }
958
959            /**
960             * Get the metadata Bundle used by this Builder.
961             *
962             * <p>The returned Bundle is shared with this Builder.
963             */
964            public Bundle getExtras() {
965                return mExtras;
966            }
967
968            /**
969             * Add an input to be collected from the user when this action is sent.
970             * Response values can be retrieved from the fired intent by using the
971             * {@link RemoteInput#getResultsFromIntent} function.
972             * @param remoteInput a {@link RemoteInput} to add to the action
973             * @return this object for method chaining
974             */
975            public Builder addRemoteInput(RemoteInput remoteInput) {
976                if (mRemoteInputs == null) {
977                    mRemoteInputs = new ArrayList<RemoteInput>();
978                }
979                mRemoteInputs.add(remoteInput);
980                return this;
981            }
982
983            /**
984             * Apply an extender to this action builder. Extenders may be used to add
985             * metadata or change options on this builder.
986             */
987            public Builder extend(Extender extender) {
988                extender.extend(this);
989                return this;
990            }
991
992            /**
993             * Combine all of the options that have been set and return a new {@link Action}
994             * object.
995             * @return the built action
996             */
997            public Action build() {
998                RemoteInput[] remoteInputs = mRemoteInputs != null
999                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1000                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
1001            }
1002        }
1003
1004        @Override
1005        public Action clone() {
1006            return new Action(
1007                    icon,
1008                    title,
1009                    actionIntent, // safe to alias
1010                    new Bundle(mExtras),
1011                    getRemoteInputs());
1012        }
1013        @Override
1014        public int describeContents() {
1015            return 0;
1016        }
1017        @Override
1018        public void writeToParcel(Parcel out, int flags) {
1019            out.writeInt(icon);
1020            TextUtils.writeToParcel(title, out, flags);
1021            if (actionIntent != null) {
1022                out.writeInt(1);
1023                actionIntent.writeToParcel(out, flags);
1024            } else {
1025                out.writeInt(0);
1026            }
1027            out.writeBundle(mExtras);
1028            out.writeTypedArray(mRemoteInputs, flags);
1029        }
1030        public static final Parcelable.Creator<Action> CREATOR =
1031                new Parcelable.Creator<Action>() {
1032            public Action createFromParcel(Parcel in) {
1033                return new Action(in);
1034            }
1035            public Action[] newArray(int size) {
1036                return new Action[size];
1037            }
1038        };
1039
1040        /**
1041         * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1042         * metadata or change options on an action builder.
1043         */
1044        public interface Extender {
1045            /**
1046             * Apply this extender to a notification action builder.
1047             * @param builder the builder to be modified.
1048             * @return the build object for chaining.
1049             */
1050            public Builder extend(Builder builder);
1051        }
1052
1053        /**
1054         * Wearable extender for notification actions. To add extensions to an action,
1055         * create a new {@link android.app.Notification.Action.WearableExtender} object using
1056         * the {@code WearableExtender()} constructor and apply it to a
1057         * {@link android.app.Notification.Action.Builder} using
1058         * {@link android.app.Notification.Action.Builder#extend}.
1059         *
1060         * <pre class="prettyprint">
1061         * Notification.Action action = new Notification.Action.Builder(
1062         *         R.drawable.archive_all, "Archive all", actionIntent)
1063         *         .extend(new Notification.Action.WearableExtender()
1064         *                 .setAvailableOffline(false))
1065         *         .build();</pre>
1066         */
1067        public static final class WearableExtender implements Extender {
1068            /** Notification action extra which contains wearable extensions */
1069            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1070
1071            private static final String KEY_FLAGS = "flags";
1072
1073            // Flags bitwise-ored to mFlags
1074            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1075
1076            // Default value for flags integer
1077            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1078
1079            private int mFlags = DEFAULT_FLAGS;
1080
1081            /**
1082             * Create a {@link android.app.Notification.Action.WearableExtender} with default
1083             * options.
1084             */
1085            public WearableExtender() {
1086            }
1087
1088            /**
1089             * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1090             * wearable options present in an existing notification action.
1091             * @param action the notification action to inspect.
1092             */
1093            public WearableExtender(Action action) {
1094                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1095                if (wearableBundle != null) {
1096                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1097                }
1098            }
1099
1100            /**
1101             * Apply wearable extensions to a notification action that is being built. This is
1102             * typically called by the {@link android.app.Notification.Action.Builder#extend}
1103             * method of {@link android.app.Notification.Action.Builder}.
1104             */
1105            @Override
1106            public Action.Builder extend(Action.Builder builder) {
1107                Bundle wearableBundle = new Bundle();
1108
1109                if (mFlags != DEFAULT_FLAGS) {
1110                    wearableBundle.putInt(KEY_FLAGS, mFlags);
1111                }
1112
1113                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1114                return builder;
1115            }
1116
1117            @Override
1118            public WearableExtender clone() {
1119                WearableExtender that = new WearableExtender();
1120                that.mFlags = this.mFlags;
1121                return that;
1122            }
1123
1124            /**
1125             * Set whether this action is available when the wearable device is not connected to
1126             * a companion device. The user can still trigger this action when the wearable device is
1127             * offline, but a visual hint will indicate that the action may not be available.
1128             * Defaults to true.
1129             */
1130            public WearableExtender setAvailableOffline(boolean availableOffline) {
1131                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1132                return this;
1133            }
1134
1135            /**
1136             * Get whether this action is available when the wearable device is not connected to
1137             * a companion device. The user can still trigger this action when the wearable device is
1138             * offline, but a visual hint will indicate that the action may not be available.
1139             * Defaults to true.
1140             */
1141            public boolean isAvailableOffline() {
1142                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1143            }
1144
1145            private void setFlag(int mask, boolean value) {
1146                if (value) {
1147                    mFlags |= mask;
1148                } else {
1149                    mFlags &= ~mask;
1150                }
1151            }
1152        }
1153    }
1154
1155    /**
1156     * Array of all {@link Action} structures attached to this notification by
1157     * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1158     * {@link android.service.notification.NotificationListenerService} that provide an alternative
1159     * interface for invoking actions.
1160     */
1161    public Action[] actions;
1162
1163    /**
1164     * Replacement version of this notification whose content will be shown
1165     * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1166     * and {@link #VISIBILITY_PUBLIC}.
1167     */
1168    public Notification publicVersion;
1169
1170    /**
1171     * Constructs a Notification object with default values.
1172     * You might want to consider using {@link Builder} instead.
1173     */
1174    public Notification()
1175    {
1176        this.when = System.currentTimeMillis();
1177        this.priority = PRIORITY_DEFAULT;
1178    }
1179
1180    /**
1181     * @hide
1182     */
1183    public Notification(Context context, int icon, CharSequence tickerText, long when,
1184            CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1185    {
1186        this.when = when;
1187        this.icon = icon;
1188        this.tickerText = tickerText;
1189        setLatestEventInfo(context, contentTitle, contentText,
1190                PendingIntent.getActivity(context, 0, contentIntent, 0));
1191    }
1192
1193    /**
1194     * Constructs a Notification object with the information needed to
1195     * have a status bar icon without the standard expanded view.
1196     *
1197     * @param icon          The resource id of the icon to put in the status bar.
1198     * @param tickerText    The text that flows by in the status bar when the notification first
1199     *                      activates.
1200     * @param when          The time to show in the time field.  In the System.currentTimeMillis
1201     *                      timebase.
1202     *
1203     * @deprecated Use {@link Builder} instead.
1204     */
1205    @Deprecated
1206    public Notification(int icon, CharSequence tickerText, long when)
1207    {
1208        this.icon = icon;
1209        this.tickerText = tickerText;
1210        this.when = when;
1211    }
1212
1213    /**
1214     * Unflatten the notification from a parcel.
1215     */
1216    public Notification(Parcel parcel)
1217    {
1218        int version = parcel.readInt();
1219
1220        when = parcel.readLong();
1221        icon = parcel.readInt();
1222        number = parcel.readInt();
1223        if (parcel.readInt() != 0) {
1224            contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1225        }
1226        if (parcel.readInt() != 0) {
1227            deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1228        }
1229        if (parcel.readInt() != 0) {
1230            tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1231        }
1232        if (parcel.readInt() != 0) {
1233            tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
1234        }
1235        if (parcel.readInt() != 0) {
1236            contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1237        }
1238        if (parcel.readInt() != 0) {
1239            largeIcon = Bitmap.CREATOR.createFromParcel(parcel);
1240        }
1241        defaults = parcel.readInt();
1242        flags = parcel.readInt();
1243        if (parcel.readInt() != 0) {
1244            sound = Uri.CREATOR.createFromParcel(parcel);
1245        }
1246
1247        audioStreamType = parcel.readInt();
1248        if (parcel.readInt() != 0) {
1249            audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1250        }
1251        vibrate = parcel.createLongArray();
1252        ledARGB = parcel.readInt();
1253        ledOnMS = parcel.readInt();
1254        ledOffMS = parcel.readInt();
1255        iconLevel = parcel.readInt();
1256
1257        if (parcel.readInt() != 0) {
1258            fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1259        }
1260
1261        priority = parcel.readInt();
1262
1263        category = parcel.readString();
1264
1265        mGroupKey = parcel.readString();
1266
1267        mSortKey = parcel.readString();
1268
1269        extras = parcel.readBundle(); // may be null
1270
1271        actions = parcel.createTypedArray(Action.CREATOR); // may be null
1272
1273        if (parcel.readInt() != 0) {
1274            bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1275        }
1276
1277        if (parcel.readInt() != 0) {
1278            headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1279        }
1280
1281        visibility = parcel.readInt();
1282
1283        if (parcel.readInt() != 0) {
1284            publicVersion = Notification.CREATOR.createFromParcel(parcel);
1285        }
1286
1287        color = parcel.readInt();
1288    }
1289
1290    @Override
1291    public Notification clone() {
1292        Notification that = new Notification();
1293        cloneInto(that, true);
1294        return that;
1295    }
1296
1297    /**
1298     * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1299     * of this into that.
1300     * @hide
1301     */
1302    public void cloneInto(Notification that, boolean heavy) {
1303        that.when = this.when;
1304        that.icon = this.icon;
1305        that.number = this.number;
1306
1307        // PendingIntents are global, so there's no reason (or way) to clone them.
1308        that.contentIntent = this.contentIntent;
1309        that.deleteIntent = this.deleteIntent;
1310        that.fullScreenIntent = this.fullScreenIntent;
1311
1312        if (this.tickerText != null) {
1313            that.tickerText = this.tickerText.toString();
1314        }
1315        if (heavy && this.tickerView != null) {
1316            that.tickerView = this.tickerView.clone();
1317        }
1318        if (heavy && this.contentView != null) {
1319            that.contentView = this.contentView.clone();
1320        }
1321        if (heavy && this.largeIcon != null) {
1322            that.largeIcon = Bitmap.createBitmap(this.largeIcon);
1323        }
1324        that.iconLevel = this.iconLevel;
1325        that.sound = this.sound; // android.net.Uri is immutable
1326        that.audioStreamType = this.audioStreamType;
1327        if (this.audioAttributes != null) {
1328            that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1329        }
1330
1331        final long[] vibrate = this.vibrate;
1332        if (vibrate != null) {
1333            final int N = vibrate.length;
1334            final long[] vib = that.vibrate = new long[N];
1335            System.arraycopy(vibrate, 0, vib, 0, N);
1336        }
1337
1338        that.ledARGB = this.ledARGB;
1339        that.ledOnMS = this.ledOnMS;
1340        that.ledOffMS = this.ledOffMS;
1341        that.defaults = this.defaults;
1342
1343        that.flags = this.flags;
1344
1345        that.priority = this.priority;
1346
1347        that.category = this.category;
1348
1349        that.mGroupKey = this.mGroupKey;
1350
1351        that.mSortKey = this.mSortKey;
1352
1353        if (this.extras != null) {
1354            try {
1355                that.extras = new Bundle(this.extras);
1356                // will unparcel
1357                that.extras.size();
1358            } catch (BadParcelableException e) {
1359                Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1360                that.extras = null;
1361            }
1362        }
1363
1364        if (this.actions != null) {
1365            that.actions = new Action[this.actions.length];
1366            for(int i=0; i<this.actions.length; i++) {
1367                that.actions[i] = this.actions[i].clone();
1368            }
1369        }
1370
1371        if (heavy && this.bigContentView != null) {
1372            that.bigContentView = this.bigContentView.clone();
1373        }
1374
1375        if (heavy && this.headsUpContentView != null) {
1376            that.headsUpContentView = this.headsUpContentView.clone();
1377        }
1378
1379        that.visibility = this.visibility;
1380
1381        if (this.publicVersion != null) {
1382            that.publicVersion = new Notification();
1383            this.publicVersion.cloneInto(that.publicVersion, heavy);
1384        }
1385
1386        that.color = this.color;
1387
1388        if (!heavy) {
1389            that.lightenPayload(); // will clean out extras
1390        }
1391    }
1392
1393    /**
1394     * Removes heavyweight parts of the Notification object for archival or for sending to
1395     * listeners when the full contents are not necessary.
1396     * @hide
1397     */
1398    public final void lightenPayload() {
1399        tickerView = null;
1400        contentView = null;
1401        bigContentView = null;
1402        headsUpContentView = null;
1403        largeIcon = null;
1404        if (extras != null) {
1405            extras.remove(Notification.EXTRA_LARGE_ICON);
1406            extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1407            extras.remove(Notification.EXTRA_PICTURE);
1408        }
1409    }
1410
1411    /**
1412     * Make sure this CharSequence is safe to put into a bundle, which basically
1413     * means it had better not be some custom Parcelable implementation.
1414     * @hide
1415     */
1416    public static CharSequence safeCharSequence(CharSequence cs) {
1417        if (cs instanceof Parcelable) {
1418            Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1419                    + " instance is a custom Parcelable and not allowed in Notification");
1420            return cs.toString();
1421        }
1422
1423        return cs;
1424    }
1425
1426    public int describeContents() {
1427        return 0;
1428    }
1429
1430    /**
1431     * Flatten this notification from a parcel.
1432     */
1433    public void writeToParcel(Parcel parcel, int flags)
1434    {
1435        parcel.writeInt(1);
1436
1437        parcel.writeLong(when);
1438        parcel.writeInt(icon);
1439        parcel.writeInt(number);
1440        if (contentIntent != null) {
1441            parcel.writeInt(1);
1442            contentIntent.writeToParcel(parcel, 0);
1443        } else {
1444            parcel.writeInt(0);
1445        }
1446        if (deleteIntent != null) {
1447            parcel.writeInt(1);
1448            deleteIntent.writeToParcel(parcel, 0);
1449        } else {
1450            parcel.writeInt(0);
1451        }
1452        if (tickerText != null) {
1453            parcel.writeInt(1);
1454            TextUtils.writeToParcel(tickerText, parcel, flags);
1455        } else {
1456            parcel.writeInt(0);
1457        }
1458        if (tickerView != null) {
1459            parcel.writeInt(1);
1460            tickerView.writeToParcel(parcel, 0);
1461        } else {
1462            parcel.writeInt(0);
1463        }
1464        if (contentView != null) {
1465            parcel.writeInt(1);
1466            contentView.writeToParcel(parcel, 0);
1467        } else {
1468            parcel.writeInt(0);
1469        }
1470        if (largeIcon != null) {
1471            parcel.writeInt(1);
1472            largeIcon.writeToParcel(parcel, 0);
1473        } else {
1474            parcel.writeInt(0);
1475        }
1476
1477        parcel.writeInt(defaults);
1478        parcel.writeInt(this.flags);
1479
1480        if (sound != null) {
1481            parcel.writeInt(1);
1482            sound.writeToParcel(parcel, 0);
1483        } else {
1484            parcel.writeInt(0);
1485        }
1486        parcel.writeInt(audioStreamType);
1487
1488        if (audioAttributes != null) {
1489            parcel.writeInt(1);
1490            audioAttributes.writeToParcel(parcel, 0);
1491        } else {
1492            parcel.writeInt(0);
1493        }
1494
1495        parcel.writeLongArray(vibrate);
1496        parcel.writeInt(ledARGB);
1497        parcel.writeInt(ledOnMS);
1498        parcel.writeInt(ledOffMS);
1499        parcel.writeInt(iconLevel);
1500
1501        if (fullScreenIntent != null) {
1502            parcel.writeInt(1);
1503            fullScreenIntent.writeToParcel(parcel, 0);
1504        } else {
1505            parcel.writeInt(0);
1506        }
1507
1508        parcel.writeInt(priority);
1509
1510        parcel.writeString(category);
1511
1512        parcel.writeString(mGroupKey);
1513
1514        parcel.writeString(mSortKey);
1515
1516        parcel.writeBundle(extras); // null ok
1517
1518        parcel.writeTypedArray(actions, 0); // null ok
1519
1520        if (bigContentView != null) {
1521            parcel.writeInt(1);
1522            bigContentView.writeToParcel(parcel, 0);
1523        } else {
1524            parcel.writeInt(0);
1525        }
1526
1527        if (headsUpContentView != null) {
1528            parcel.writeInt(1);
1529            headsUpContentView.writeToParcel(parcel, 0);
1530        } else {
1531            parcel.writeInt(0);
1532        }
1533
1534        parcel.writeInt(visibility);
1535
1536        if (publicVersion != null) {
1537            parcel.writeInt(1);
1538            publicVersion.writeToParcel(parcel, 0);
1539        } else {
1540            parcel.writeInt(0);
1541        }
1542
1543        parcel.writeInt(color);
1544    }
1545
1546    /**
1547     * Parcelable.Creator that instantiates Notification objects
1548     */
1549    public static final Parcelable.Creator<Notification> CREATOR
1550            = new Parcelable.Creator<Notification>()
1551    {
1552        public Notification createFromParcel(Parcel parcel)
1553        {
1554            return new Notification(parcel);
1555        }
1556
1557        public Notification[] newArray(int size)
1558        {
1559            return new Notification[size];
1560        }
1561    };
1562
1563    /**
1564     * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1565     * layout.
1566     *
1567     * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1568     * in the view.</p>
1569     * @param context       The context for your application / activity.
1570     * @param contentTitle The title that goes in the expanded entry.
1571     * @param contentText  The text that goes in the expanded entry.
1572     * @param contentIntent The intent to launch when the user clicks the expanded notification.
1573     * If this is an activity, it must include the
1574     * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
1575     * that you take care of task management as described in the
1576     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1577     * Stack</a> document.
1578     *
1579     * @deprecated Use {@link Builder} instead.
1580     */
1581    @Deprecated
1582    public void setLatestEventInfo(Context context,
1583            CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
1584        Notification.Builder builder = new Notification.Builder(context);
1585
1586        // First, ensure that key pieces of information that may have been set directly
1587        // are preserved
1588        builder.setWhen(this.when);
1589        builder.setSmallIcon(this.icon);
1590        builder.setPriority(this.priority);
1591        builder.setTicker(this.tickerText);
1592        builder.setNumber(this.number);
1593        builder.mFlags = this.flags;
1594        builder.setSound(this.sound, this.audioStreamType);
1595        builder.setDefaults(this.defaults);
1596        builder.setVibrate(this.vibrate);
1597
1598        // now apply the latestEventInfo fields
1599        if (contentTitle != null) {
1600            builder.setContentTitle(contentTitle);
1601        }
1602        if (contentText != null) {
1603            builder.setContentText(contentText);
1604        }
1605        builder.setContentIntent(contentIntent);
1606        builder.buildInto(this);
1607    }
1608
1609    @Override
1610    public String toString() {
1611        StringBuilder sb = new StringBuilder();
1612        sb.append("Notification(pri=");
1613        sb.append(priority);
1614        sb.append(" contentView=");
1615        if (contentView != null) {
1616            sb.append(contentView.getPackage());
1617            sb.append("/0x");
1618            sb.append(Integer.toHexString(contentView.getLayoutId()));
1619        } else {
1620            sb.append("null");
1621        }
1622        sb.append(" vibrate=");
1623        if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1624            sb.append("default");
1625        } else if (this.vibrate != null) {
1626            int N = this.vibrate.length-1;
1627            sb.append("[");
1628            for (int i=0; i<N; i++) {
1629                sb.append(this.vibrate[i]);
1630                sb.append(',');
1631            }
1632            if (N != -1) {
1633                sb.append(this.vibrate[N]);
1634            }
1635            sb.append("]");
1636        } else {
1637            sb.append("null");
1638        }
1639        sb.append(" sound=");
1640        if ((this.defaults & DEFAULT_SOUND) != 0) {
1641            sb.append("default");
1642        } else if (this.sound != null) {
1643            sb.append(this.sound.toString());
1644        } else {
1645            sb.append("null");
1646        }
1647        sb.append(" defaults=0x");
1648        sb.append(Integer.toHexString(this.defaults));
1649        sb.append(" flags=0x");
1650        sb.append(Integer.toHexString(this.flags));
1651        sb.append(String.format(" color=0x%08x", this.color));
1652        if (this.category != null) {
1653            sb.append(" category=");
1654            sb.append(this.category);
1655        }
1656        if (this.mGroupKey != null) {
1657            sb.append(" groupKey=");
1658            sb.append(this.mGroupKey);
1659        }
1660        if (this.mSortKey != null) {
1661            sb.append(" sortKey=");
1662            sb.append(this.mSortKey);
1663        }
1664        if (actions != null) {
1665            sb.append(" actions=");
1666            sb.append(actions.length);
1667        }
1668        sb.append(" vis=");
1669        sb.append(visibilityToString(this.visibility));
1670        if (this.publicVersion != null) {
1671            sb.append(" publicVersion=");
1672            sb.append(publicVersion.toString());
1673        }
1674        sb.append(")");
1675        return sb.toString();
1676    }
1677
1678    /**
1679     * {@hide}
1680     */
1681    public static String visibilityToString(int vis) {
1682        switch (vis) {
1683            case VISIBILITY_PRIVATE:
1684                return "PRIVATE";
1685            case VISIBILITY_PUBLIC:
1686                return "PUBLIC";
1687            case VISIBILITY_SECRET:
1688                return "SECRET";
1689            default:
1690                return "UNKNOWN(" + String.valueOf(vis) + ")";
1691        }
1692    }
1693
1694    /** {@hide} */
1695    public void setUser(UserHandle user) {
1696        if (user.getIdentifier() == UserHandle.USER_ALL) {
1697            user = UserHandle.OWNER;
1698        }
1699        if (tickerView != null) {
1700            tickerView.setUser(user);
1701        }
1702        if (contentView != null) {
1703            contentView.setUser(user);
1704        }
1705        if (bigContentView != null) {
1706            bigContentView.setUser(user);
1707        }
1708        if (headsUpContentView != null) {
1709            headsUpContentView.setUser(user);
1710        }
1711    }
1712
1713    /**
1714     * Builder class for {@link Notification} objects.
1715     *
1716     * Provides a convenient way to set the various fields of a {@link Notification} and generate
1717     * content views using the platform's notification layout template. If your app supports
1718     * versions of Android as old as API level 4, you can instead use
1719     * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
1720     * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
1721     * library</a>.
1722     *
1723     * <p>Example:
1724     *
1725     * <pre class="prettyprint">
1726     * Notification noti = new Notification.Builder(mContext)
1727     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
1728     *         .setContentText(subject)
1729     *         .setSmallIcon(R.drawable.new_mail)
1730     *         .setLargeIcon(aBitmap)
1731     *         .build();
1732     * </pre>
1733     */
1734    public static class Builder {
1735        private static final int MAX_ACTION_BUTTONS = 3;
1736
1737        private Context mContext;
1738
1739        private long mWhen;
1740        private int mSmallIcon;
1741        private int mSmallIconLevel;
1742        private int mNumber;
1743        private CharSequence mContentTitle;
1744        private CharSequence mContentText;
1745        private CharSequence mContentInfo;
1746        private CharSequence mSubText;
1747        private PendingIntent mContentIntent;
1748        private RemoteViews mContentView;
1749        private PendingIntent mDeleteIntent;
1750        private PendingIntent mFullScreenIntent;
1751        private CharSequence mTickerText;
1752        private RemoteViews mTickerView;
1753        private Bitmap mLargeIcon;
1754        private Uri mSound;
1755        private int mAudioStreamType;
1756        private AudioAttributes mAudioAttributes;
1757        private long[] mVibrate;
1758        private int mLedArgb;
1759        private int mLedOnMs;
1760        private int mLedOffMs;
1761        private int mDefaults;
1762        private int mFlags;
1763        private int mProgressMax;
1764        private int mProgress;
1765        private boolean mProgressIndeterminate;
1766        private String mCategory;
1767        private String mGroupKey;
1768        private String mSortKey;
1769        private Bundle mExtras;
1770        private int mPriority;
1771        private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
1772        private boolean mUseChronometer;
1773        private Style mStyle;
1774        private boolean mShowWhen = true;
1775        private int mVisibility = VISIBILITY_PRIVATE;
1776        private Notification mPublicVersion = null;
1777        private final NotificationColorUtil mColorUtil;
1778        private ArrayList<String> mPeople;
1779        private int mColor = COLOR_DEFAULT;
1780
1781        /**
1782         * Constructs a new Builder with the defaults:
1783         *
1784
1785         * <table>
1786         * <tr><th align=right>priority</th>
1787         *     <td>{@link #PRIORITY_DEFAULT}</td></tr>
1788         * <tr><th align=right>when</th>
1789         *     <td>now ({@link System#currentTimeMillis()})</td></tr>
1790         * <tr><th align=right>audio stream</th>
1791         *     <td>{@link #STREAM_DEFAULT}</td></tr>
1792         * </table>
1793         *
1794
1795         * @param context
1796         *            A {@link Context} that will be used by the Builder to construct the
1797         *            RemoteViews. The Context will not be held past the lifetime of this Builder
1798         *            object.
1799         */
1800        public Builder(Context context) {
1801            /*
1802             * Important compatibility note!
1803             * Some apps out in the wild create a Notification.Builder in their Activity subclass
1804             * constructor for later use. At this point Activities - themselves subclasses of
1805             * ContextWrapper - do not have their inner Context populated yet. This means that
1806             * any calls to Context methods from within this constructor can cause NPEs in existing
1807             * apps. Any data populated from mContext should therefore be populated lazily to
1808             * preserve compatibility.
1809             */
1810            mContext = context;
1811
1812            // Set defaults to match the defaults of a Notification
1813            mWhen = System.currentTimeMillis();
1814            mAudioStreamType = STREAM_DEFAULT;
1815            mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
1816            mPriority = PRIORITY_DEFAULT;
1817            mPeople = new ArrayList<String>();
1818
1819            mColorUtil = NotificationColorUtil.getInstance();
1820        }
1821
1822        /**
1823         * Add a timestamp pertaining to the notification (usually the time the event occurred).
1824         * It will be shown in the notification content view by default; use
1825         * {@link #setShowWhen(boolean) setShowWhen} to control this.
1826         *
1827         * @see Notification#when
1828         */
1829        public Builder setWhen(long when) {
1830            mWhen = when;
1831            return this;
1832        }
1833
1834        /**
1835         * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
1836         * in the content view.
1837         */
1838        public Builder setShowWhen(boolean show) {
1839            mShowWhen = show;
1840            return this;
1841        }
1842
1843        /**
1844         * Show the {@link Notification#when} field as a stopwatch.
1845         *
1846         * Instead of presenting <code>when</code> as a timestamp, the notification will show an
1847         * automatically updating display of the minutes and seconds since <code>when</code>.
1848         *
1849         * Useful when showing an elapsed time (like an ongoing phone call).
1850         *
1851         * @see android.widget.Chronometer
1852         * @see Notification#when
1853         */
1854        public Builder setUsesChronometer(boolean b) {
1855            mUseChronometer = b;
1856            return this;
1857        }
1858
1859        /**
1860         * Set the small icon resource, which will be used to represent the notification in the
1861         * status bar.
1862         *
1863
1864         * The platform template for the expanded view will draw this icon in the left, unless a
1865         * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
1866         * icon will be moved to the right-hand side.
1867         *
1868
1869         * @param icon
1870         *            A resource ID in the application's package of the drawable to use.
1871         * @see Notification#icon
1872         */
1873        public Builder setSmallIcon(int icon) {
1874            mSmallIcon = icon;
1875            return this;
1876        }
1877
1878        /**
1879         * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
1880         * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
1881         * LevelListDrawable}.
1882         *
1883         * @param icon A resource ID in the application's package of the drawable to use.
1884         * @param level The level to use for the icon.
1885         *
1886         * @see Notification#icon
1887         * @see Notification#iconLevel
1888         */
1889        public Builder setSmallIcon(int icon, int level) {
1890            mSmallIcon = icon;
1891            mSmallIconLevel = level;
1892            return this;
1893        }
1894
1895        /**
1896         * Set the first line of text in the platform notification template.
1897         */
1898        public Builder setContentTitle(CharSequence title) {
1899            mContentTitle = safeCharSequence(title);
1900            return this;
1901        }
1902
1903        /**
1904         * Set the second line of text in the platform notification template.
1905         */
1906        public Builder setContentText(CharSequence text) {
1907            mContentText = safeCharSequence(text);
1908            return this;
1909        }
1910
1911        /**
1912         * Set the third line of text in the platform notification template.
1913         * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
1914         * same location in the standard template.
1915         */
1916        public Builder setSubText(CharSequence text) {
1917            mSubText = safeCharSequence(text);
1918            return this;
1919        }
1920
1921        /**
1922         * Set the large number at the right-hand side of the notification.  This is
1923         * equivalent to setContentInfo, although it might show the number in a different
1924         * font size for readability.
1925         */
1926        public Builder setNumber(int number) {
1927            mNumber = number;
1928            return this;
1929        }
1930
1931        /**
1932         * A small piece of additional information pertaining to this notification.
1933         *
1934         * The platform template will draw this on the last line of the notification, at the far
1935         * right (to the right of a smallIcon if it has been placed there).
1936         */
1937        public Builder setContentInfo(CharSequence info) {
1938            mContentInfo = safeCharSequence(info);
1939            return this;
1940        }
1941
1942        /**
1943         * Set the progress this notification represents.
1944         *
1945         * The platform template will represent this using a {@link ProgressBar}.
1946         */
1947        public Builder setProgress(int max, int progress, boolean indeterminate) {
1948            mProgressMax = max;
1949            mProgress = progress;
1950            mProgressIndeterminate = indeterminate;
1951            return this;
1952        }
1953
1954        /**
1955         * Supply a custom RemoteViews to use instead of the platform template.
1956         *
1957         * @see Notification#contentView
1958         */
1959        public Builder setContent(RemoteViews views) {
1960            mContentView = views;
1961            return this;
1962        }
1963
1964        /**
1965         * Supply a {@link PendingIntent} to be sent when the notification is clicked.
1966         *
1967         * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
1968         * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
1969         * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
1970         * to assign PendingIntents to individual views in that custom layout (i.e., to create
1971         * clickable buttons inside the notification view).
1972         *
1973         * @see Notification#contentIntent Notification.contentIntent
1974         */
1975        public Builder setContentIntent(PendingIntent intent) {
1976            mContentIntent = intent;
1977            return this;
1978        }
1979
1980        /**
1981         * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
1982         *
1983         * @see Notification#deleteIntent
1984         */
1985        public Builder setDeleteIntent(PendingIntent intent) {
1986            mDeleteIntent = intent;
1987            return this;
1988        }
1989
1990        /**
1991         * An intent to launch instead of posting the notification to the status bar.
1992         * Only for use with extremely high-priority notifications demanding the user's
1993         * <strong>immediate</strong> attention, such as an incoming phone call or
1994         * alarm clock that the user has explicitly set to a particular time.
1995         * If this facility is used for something else, please give the user an option
1996         * to turn it off and use a normal notification, as this can be extremely
1997         * disruptive.
1998         *
1999         * <p>
2000         * The system UI may choose to display a heads-up notification, instead of
2001         * launching this intent, while the user is using the device.
2002         * </p>
2003         *
2004         * @param intent The pending intent to launch.
2005         * @param highPriority Passing true will cause this notification to be sent
2006         *          even if other notifications are suppressed.
2007         *
2008         * @see Notification#fullScreenIntent
2009         */
2010        public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
2011            mFullScreenIntent = intent;
2012            setFlag(FLAG_HIGH_PRIORITY, highPriority);
2013            return this;
2014        }
2015
2016        /**
2017         * Set the "ticker" text which is sent to accessibility services.
2018         *
2019         * @see Notification#tickerText
2020         */
2021        public Builder setTicker(CharSequence tickerText) {
2022            mTickerText = safeCharSequence(tickerText);
2023            return this;
2024        }
2025
2026        /**
2027         * Obsolete version of {@link #setTicker(CharSequence)}.
2028         *
2029         */
2030        @Deprecated
2031        public Builder setTicker(CharSequence tickerText, RemoteViews views) {
2032            mTickerText = safeCharSequence(tickerText);
2033            mTickerView = views; // we'll save it for you anyway
2034            return this;
2035        }
2036
2037        /**
2038         * Add a large icon to the notification (and the ticker on some devices).
2039         *
2040         * In the platform template, this image will be shown on the left of the notification view
2041         * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side).
2042         *
2043         * @see Notification#largeIcon
2044         */
2045        public Builder setLargeIcon(Bitmap icon) {
2046            mLargeIcon = icon;
2047            return this;
2048        }
2049
2050        /**
2051         * Set the sound to play.
2052         *
2053         * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2054         * for notifications.
2055         *
2056         * <p>
2057         * A notification that is noisy is more likely to be presented as a heads-up notification.
2058         * </p>
2059         *
2060         * @see Notification#sound
2061         */
2062        public Builder setSound(Uri sound) {
2063            mSound = sound;
2064            mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
2065            return this;
2066        }
2067
2068        /**
2069         * Set the sound to play, along with a specific stream on which to play it.
2070         *
2071         * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2072         *
2073         * <p>
2074         * A notification that is noisy is more likely to be presented as a heads-up notification.
2075         * </p>
2076         * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
2077         * @see Notification#sound
2078         */
2079        public Builder setSound(Uri sound, int streamType) {
2080            mSound = sound;
2081            mAudioStreamType = streamType;
2082            return this;
2083        }
2084
2085        /**
2086         * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2087         * use during playback.
2088         *
2089         * <p>
2090         * A notification that is noisy is more likely to be presented as a heads-up notification.
2091         * </p>
2092         *
2093         * @see Notification#sound
2094         */
2095        public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
2096            mSound = sound;
2097            mAudioAttributes = audioAttributes;
2098            return this;
2099        }
2100
2101        /**
2102         * Set the vibration pattern to use.
2103         *
2104         * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2105         * <code>pattern</code> parameter.
2106         *
2107         * <p>
2108         * A notification that vibrates is more likely to be presented as a heads-up notification.
2109         * </p>
2110         *
2111         * @see Notification#vibrate
2112         */
2113        public Builder setVibrate(long[] pattern) {
2114            mVibrate = pattern;
2115            return this;
2116        }
2117
2118        /**
2119         * Set the desired color for the indicator LED on the device, as well as the
2120         * blink duty cycle (specified in milliseconds).
2121         *
2122
2123         * Not all devices will honor all (or even any) of these values.
2124         *
2125
2126         * @see Notification#ledARGB
2127         * @see Notification#ledOnMS
2128         * @see Notification#ledOffMS
2129         */
2130        public Builder setLights(int argb, int onMs, int offMs) {
2131            mLedArgb = argb;
2132            mLedOnMs = onMs;
2133            mLedOffMs = offMs;
2134            return this;
2135        }
2136
2137        /**
2138         * Set whether this is an "ongoing" notification.
2139         *
2140
2141         * Ongoing notifications cannot be dismissed by the user, so your application or service
2142         * must take care of canceling them.
2143         *
2144
2145         * They are typically used to indicate a background task that the user is actively engaged
2146         * with (e.g., playing music) or is pending in some way and therefore occupying the device
2147         * (e.g., a file download, sync operation, active network connection).
2148         *
2149
2150         * @see Notification#FLAG_ONGOING_EVENT
2151         * @see Service#setForeground(boolean)
2152         */
2153        public Builder setOngoing(boolean ongoing) {
2154            setFlag(FLAG_ONGOING_EVENT, ongoing);
2155            return this;
2156        }
2157
2158        /**
2159         * Set this flag if you would only like the sound, vibrate
2160         * and ticker to be played if the notification is not already showing.
2161         *
2162         * @see Notification#FLAG_ONLY_ALERT_ONCE
2163         */
2164        public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2165            setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2166            return this;
2167        }
2168
2169        /**
2170         * Make this notification automatically dismissed when the user touches it. The
2171         * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2172         *
2173         * @see Notification#FLAG_AUTO_CANCEL
2174         */
2175        public Builder setAutoCancel(boolean autoCancel) {
2176            setFlag(FLAG_AUTO_CANCEL, autoCancel);
2177            return this;
2178        }
2179
2180        /**
2181         * Set whether or not this notification should not bridge to other devices.
2182         *
2183         * <p>Some notifications can be bridged to other devices for remote display.
2184         * This hint can be set to recommend this notification not be bridged.
2185         */
2186        public Builder setLocalOnly(boolean localOnly) {
2187            setFlag(FLAG_LOCAL_ONLY, localOnly);
2188            return this;
2189        }
2190
2191        /**
2192         * Set which notification properties will be inherited from system defaults.
2193         * <p>
2194         * The value should be one or more of the following fields combined with
2195         * bitwise-or:
2196         * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2197         * <p>
2198         * For all default values, use {@link #DEFAULT_ALL}.
2199         */
2200        public Builder setDefaults(int defaults) {
2201            mDefaults = defaults;
2202            return this;
2203        }
2204
2205        /**
2206         * Set the priority of this notification.
2207         *
2208         * @see Notification#priority
2209         */
2210        public Builder setPriority(@Priority int pri) {
2211            mPriority = pri;
2212            return this;
2213        }
2214
2215        /**
2216         * Set the notification category.
2217         *
2218         * @see Notification#category
2219         */
2220        public Builder setCategory(String category) {
2221            mCategory = category;
2222            return this;
2223        }
2224
2225        /**
2226         * Add a person that is relevant to this notification.
2227         *
2228         * @see Notification#EXTRA_PEOPLE
2229         */
2230        public Builder addPerson(String handle) {
2231            mPeople.add(handle);
2232            return this;
2233        }
2234
2235        /**
2236         * Set this notification to be part of a group of notifications sharing the same key.
2237         * Grouped notifications may display in a cluster or stack on devices which
2238         * support such rendering.
2239         *
2240         * <p>To make this notification the summary for its group, also call
2241         * {@link #setGroupSummary}. A sort order can be specified for group members by using
2242         * {@link #setSortKey}.
2243         * @param groupKey The group key of the group.
2244         * @return this object for method chaining
2245         */
2246        public Builder setGroup(String groupKey) {
2247            mGroupKey = groupKey;
2248            return this;
2249        }
2250
2251        /**
2252         * Set this notification to be the group summary for a group of notifications.
2253         * Grouped notifications may display in a cluster or stack on devices which
2254         * support such rendering. Requires a group key also be set using {@link #setGroup}.
2255         * @param isGroupSummary Whether this notification should be a group summary.
2256         * @return this object for method chaining
2257         */
2258        public Builder setGroupSummary(boolean isGroupSummary) {
2259            setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2260            return this;
2261        }
2262
2263        /**
2264         * Set a sort key that orders this notification among other notifications from the
2265         * same package. This can be useful if an external sort was already applied and an app
2266         * would like to preserve this. Notifications will be sorted lexicographically using this
2267         * value, although providing different priorities in addition to providing sort key may
2268         * cause this value to be ignored.
2269         *
2270         * <p>This sort key can also be used to order members of a notification group. See
2271         * {@link #setGroup}.
2272         *
2273         * @see String#compareTo(String)
2274         */
2275        public Builder setSortKey(String sortKey) {
2276            mSortKey = sortKey;
2277            return this;
2278        }
2279
2280        /**
2281         * Merge additional metadata into this notification.
2282         *
2283         * <p>Values within the Bundle will replace existing extras values in this Builder.
2284         *
2285         * @see Notification#extras
2286         */
2287        public Builder addExtras(Bundle extras) {
2288            if (extras != null) {
2289                if (mExtras == null) {
2290                    mExtras = new Bundle(extras);
2291                } else {
2292                    mExtras.putAll(extras);
2293                }
2294            }
2295            return this;
2296        }
2297
2298        /**
2299         * Set metadata for this notification.
2300         *
2301         * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
2302         * current contents are copied into the Notification each time {@link #build()} is
2303         * called.
2304         *
2305         * <p>Replaces any existing extras values with those from the provided Bundle.
2306         * Use {@link #addExtras} to merge in metadata instead.
2307         *
2308         * @see Notification#extras
2309         */
2310        public Builder setExtras(Bundle extras) {
2311            mExtras = extras;
2312            return this;
2313        }
2314
2315        /**
2316         * Get the current metadata Bundle used by this notification Builder.
2317         *
2318         * <p>The returned Bundle is shared with this Builder.
2319         *
2320         * <p>The current contents of this Bundle are copied into the Notification each time
2321         * {@link #build()} is called.
2322         *
2323         * @see Notification#extras
2324         */
2325        public Bundle getExtras() {
2326            if (mExtras == null) {
2327                mExtras = new Bundle();
2328            }
2329            return mExtras;
2330        }
2331
2332        /**
2333         * Add an action to this notification. Actions are typically displayed by
2334         * the system as a button adjacent to the notification content.
2335         * <p>
2336         * Every action must have an icon (32dp square and matching the
2337         * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2338         * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2339         * <p>
2340         * A notification in its expanded form can display up to 3 actions, from left to right in
2341         * the order they were added. Actions will not be displayed when the notification is
2342         * collapsed, however, so be sure that any essential functions may be accessed by the user
2343         * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2344         *
2345         * @param icon Resource ID of a drawable that represents the action.
2346         * @param title Text describing the action.
2347         * @param intent PendingIntent to be fired when the action is invoked.
2348         */
2349        public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
2350            mActions.add(new Action(icon, safeCharSequence(title), intent));
2351            return this;
2352        }
2353
2354        /**
2355         * Add an action to this notification. Actions are typically displayed by
2356         * the system as a button adjacent to the notification content.
2357         * <p>
2358         * Every action must have an icon (32dp square and matching the
2359         * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2360         * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2361         * <p>
2362         * A notification in its expanded form can display up to 3 actions, from left to right in
2363         * the order they were added. Actions will not be displayed when the notification is
2364         * collapsed, however, so be sure that any essential functions may be accessed by the user
2365         * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2366         *
2367         * @param action The action to add.
2368         */
2369        public Builder addAction(Action action) {
2370            mActions.add(action);
2371            return this;
2372        }
2373
2374        /**
2375         * Add a rich notification style to be applied at build time.
2376         *
2377         * @param style Object responsible for modifying the notification style.
2378         */
2379        public Builder setStyle(Style style) {
2380            if (mStyle != style) {
2381                mStyle = style;
2382                if (mStyle != null) {
2383                    mStyle.setBuilder(this);
2384                }
2385            }
2386            return this;
2387        }
2388
2389        /**
2390         * Specify the value of {@link #visibility}.
2391         *
2392         * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2393         * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2394         *
2395         * @return The same Builder.
2396         */
2397        public Builder setVisibility(int visibility) {
2398            mVisibility = visibility;
2399            return this;
2400        }
2401
2402        /**
2403         * Supply a replacement Notification whose contents should be shown in insecure contexts
2404         * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2405         * @param n A replacement notification, presumably with some or all info redacted.
2406         * @return The same Builder.
2407         */
2408        public Builder setPublicVersion(Notification n) {
2409            mPublicVersion = n;
2410            return this;
2411        }
2412
2413        /**
2414         * Apply an extender to this notification builder. Extenders may be used to add
2415         * metadata or change options on this builder.
2416         */
2417        public Builder extend(Extender extender) {
2418            extender.extend(this);
2419            return this;
2420        }
2421
2422        private void setFlag(int mask, boolean value) {
2423            if (value) {
2424                mFlags |= mask;
2425            } else {
2426                mFlags &= ~mask;
2427            }
2428        }
2429
2430        /**
2431         * Sets {@link Notification#color}.
2432         *
2433         * @param argb The accent color to use
2434         *
2435         * @return The same Builder.
2436         */
2437        public Builder setColor(int argb) {
2438            mColor = argb;
2439            return this;
2440        }
2441
2442        private Bitmap getProfileBadge() {
2443            UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
2444            Drawable badge = userManager.getBadgeForUser(android.os.Process.myUserHandle());
2445            if (badge == null) {
2446                return null;
2447            }
2448            final int width = badge.getIntrinsicWidth();
2449            final int height = badge.getIntrinsicHeight();
2450            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
2451            Canvas canvas = new Canvas(bitmap);
2452            badge.setBounds(0, 0, width, height);
2453            badge.draw(canvas);
2454            return bitmap;
2455        }
2456
2457        private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) {
2458            Bitmap profileIcon = getProfileBadge();
2459            RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
2460            boolean showLine3 = false;
2461            boolean showLine2 = false;
2462
2463            if (mPriority < PRIORITY_LOW) {
2464                // TODO: Low priority presentation
2465            }
2466            if (profileIcon != null) {
2467                contentView.setImageViewBitmap(R.id.profile_icon, profileIcon);
2468                contentView.setViewVisibility(R.id.profile_icon, View.VISIBLE);
2469            } else {
2470                contentView.setViewVisibility(R.id.profile_icon, View.GONE);
2471            }
2472            if (mLargeIcon != null) {
2473                contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
2474                processLargeIcon(mLargeIcon, contentView);
2475                contentView.setImageViewResource(R.id.right_icon, mSmallIcon);
2476                contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
2477                processSmallRightIcon(mSmallIcon, contentView);
2478            } else { // small icon at left
2479                contentView.setImageViewResource(R.id.icon, mSmallIcon);
2480                contentView.setViewVisibility(R.id.icon, View.VISIBLE);
2481                processSmallIconAsLarge(mSmallIcon, contentView);
2482            }
2483            if (mContentTitle != null) {
2484                contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle));
2485            }
2486            if (mContentText != null) {
2487                contentView.setTextViewText(R.id.text, processLegacyText(mContentText));
2488                showLine3 = true;
2489            }
2490            if (mContentInfo != null) {
2491                contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo));
2492                contentView.setViewVisibility(R.id.info, View.VISIBLE);
2493                showLine3 = true;
2494            } else if (mNumber > 0) {
2495                final int tooBig = mContext.getResources().getInteger(
2496                        R.integer.status_bar_notification_info_maxnum);
2497                if (mNumber > tooBig) {
2498                    contentView.setTextViewText(R.id.info, processLegacyText(
2499                            mContext.getResources().getString(
2500                                    R.string.status_bar_notification_info_overflow)));
2501                } else {
2502                    NumberFormat f = NumberFormat.getIntegerInstance();
2503                    contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber)));
2504                }
2505                contentView.setViewVisibility(R.id.info, View.VISIBLE);
2506                showLine3 = true;
2507            } else {
2508                contentView.setViewVisibility(R.id.info, View.GONE);
2509            }
2510
2511            // Need to show three lines?
2512            if (mSubText != null) {
2513                contentView.setTextViewText(R.id.text, processLegacyText(mSubText));
2514                if (mContentText != null) {
2515                    contentView.setTextViewText(R.id.text2, processLegacyText(mContentText));
2516                    contentView.setViewVisibility(R.id.text2, View.VISIBLE);
2517                    showLine2 = true;
2518                } else {
2519                    contentView.setViewVisibility(R.id.text2, View.GONE);
2520                }
2521            } else {
2522                contentView.setViewVisibility(R.id.text2, View.GONE);
2523                if (mProgressMax != 0 || mProgressIndeterminate) {
2524                    contentView.setProgressBar(
2525                            R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
2526                    contentView.setViewVisibility(R.id.progress, View.VISIBLE);
2527                    showLine2 = true;
2528                } else {
2529                    contentView.setViewVisibility(R.id.progress, View.GONE);
2530                }
2531            }
2532            if (showLine2) {
2533                if (fitIn1U) {
2534                    // need to shrink all the type to make sure everything fits
2535                    final Resources res = mContext.getResources();
2536                    final float subTextSize = res.getDimensionPixelSize(
2537                            R.dimen.notification_subtext_size);
2538                    contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
2539                }
2540                // vertical centering
2541                contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
2542            }
2543
2544            if (mWhen != 0 && mShowWhen) {
2545                if (mUseChronometer) {
2546                    contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
2547                    contentView.setLong(R.id.chronometer, "setBase",
2548                            mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
2549                    contentView.setBoolean(R.id.chronometer, "setStarted", true);
2550                } else {
2551                    contentView.setViewVisibility(R.id.time, View.VISIBLE);
2552                    contentView.setLong(R.id.time, "setTime", mWhen);
2553                }
2554            } else {
2555                contentView.setViewVisibility(R.id.time, View.GONE);
2556            }
2557
2558            contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
2559            contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE);
2560            return contentView;
2561        }
2562
2563        private RemoteViews applyStandardTemplateWithActions(int layoutId) {
2564            RemoteViews big = applyStandardTemplate(layoutId, false);
2565
2566            int N = mActions.size();
2567            if (N > 0) {
2568                big.setViewVisibility(R.id.actions, View.VISIBLE);
2569                big.setViewVisibility(R.id.action_divider, View.VISIBLE);
2570                if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
2571                big.removeAllViews(R.id.actions);
2572                for (int i=0; i<N; i++) {
2573                    final RemoteViews button = generateActionButton(mActions.get(i));
2574                    big.addView(R.id.actions, button);
2575                }
2576            }
2577            return big;
2578        }
2579
2580        private RemoteViews makeContentView() {
2581            if (mContentView != null) {
2582                return mContentView;
2583            } else {
2584                return applyStandardTemplate(getBaseLayoutResource(), true); // no more special large_icon flavor
2585            }
2586        }
2587
2588        private RemoteViews makeTickerView() {
2589            if (mTickerView != null) {
2590                return mTickerView;
2591            }
2592            return null; // tickers are not created by default anymore
2593        }
2594
2595        private RemoteViews makeBigContentView() {
2596            if (mActions.size() == 0) return null;
2597
2598            return applyStandardTemplateWithActions(getBigBaseLayoutResource());
2599        }
2600
2601        private RemoteViews makeHeadsUpContentView() {
2602            if (mActions.size() == 0) return null;
2603
2604            return applyStandardTemplateWithActions(getBigBaseLayoutResource());
2605        }
2606
2607
2608        private RemoteViews generateActionButton(Action action) {
2609            final boolean tombstone = (action.actionIntent == null);
2610            RemoteViews button = new RemoteViews(mContext.getPackageName(),
2611                    tombstone ? getActionTombstoneLayoutResource()
2612                              : getActionLayoutResource());
2613            button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0);
2614            button.setTextViewText(R.id.action0, processLegacyText(action.title));
2615            if (!tombstone) {
2616                button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
2617            }
2618            button.setContentDescription(R.id.action0, action.title);
2619            processLegacyAction(action, button);
2620            return button;
2621        }
2622
2623        /**
2624         * @return Whether we are currently building a notification from a legacy (an app that
2625         *         doesn't create material notifications by itself) app.
2626         */
2627        private boolean isLegacy() {
2628            return mColorUtil != null;
2629        }
2630
2631        private void processLegacyAction(Action action, RemoteViews button) {
2632            if (isLegacy()) {
2633                if (mColorUtil.isGrayscale(mContext, action.icon)) {
2634                    button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
2635                            mContext.getResources().getColor(
2636                                    R.color.notification_action_legacy_color_filter),
2637                            PorterDuff.Mode.MULTIPLY);
2638                }
2639            }
2640        }
2641
2642        private CharSequence processLegacyText(CharSequence charSequence) {
2643            if (isLegacy()) {
2644                return mColorUtil.invertCharSequenceColors(charSequence);
2645            } else {
2646                return charSequence;
2647            }
2648        }
2649
2650        /**
2651         * Apply any necessary background to smallIcons being used in the largeIcon spot.
2652         */
2653        private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) {
2654            if (!isLegacy() || mColorUtil.isGrayscale(mContext, largeIconId)) {
2655                applyLargeIconBackground(contentView);
2656            }
2657        }
2658
2659        /**
2660         * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
2661         * if it's grayscale).
2662         */
2663        // TODO: also check bounds, transparency, that sort of thing.
2664        private void processLargeIcon(Bitmap largeIcon, RemoteViews contentView) {
2665            if (!isLegacy() || mColorUtil.isGrayscale(largeIcon)) {
2666                applyLargeIconBackground(contentView);
2667            } else {
2668                removeLargeIconBackground(contentView);
2669            }
2670        }
2671
2672        /**
2673         * Add a colored circle behind the largeIcon slot.
2674         */
2675        private void applyLargeIconBackground(RemoteViews contentView) {
2676            contentView.setInt(R.id.icon, "setBackgroundResource",
2677                    R.drawable.notification_icon_legacy_bg_inset);
2678
2679            contentView.setDrawableParameters(
2680                    R.id.icon,
2681                    true,
2682                    -1,
2683                    resolveColor(),
2684                    PorterDuff.Mode.SRC_ATOP,
2685                    -1);
2686        }
2687
2688        private void removeLargeIconBackground(RemoteViews contentView) {
2689            contentView.setInt(R.id.icon, "setBackgroundResource", 0);
2690        }
2691
2692        /**
2693         * Recolor small icons when used in the R.id.right_icon slot.
2694         */
2695        private void processSmallRightIcon(int smallIconDrawableId,
2696                RemoteViews contentView) {
2697            if (!isLegacy() || mColorUtil.isGrayscale(mContext, smallIconDrawableId)) {
2698                contentView.setDrawableParameters(R.id.right_icon, false, -1,
2699                        0xFFFFFFFF,
2700                        PorterDuff.Mode.SRC_ATOP, -1);
2701
2702                contentView.setInt(R.id.right_icon,
2703                        "setBackgroundResource",
2704                        R.drawable.notification_icon_legacy_bg);
2705
2706                contentView.setDrawableParameters(
2707                        R.id.right_icon,
2708                        true,
2709                        -1,
2710                        resolveColor(),
2711                        PorterDuff.Mode.SRC_ATOP,
2712                        -1);
2713            }
2714        }
2715
2716        private int sanitizeColor() {
2717            if (mColor != COLOR_DEFAULT) {
2718                mColor |= 0xFF000000; // no alpha for custom colors
2719            }
2720            return mColor;
2721        }
2722
2723        private int resolveColor() {
2724            if (mColor == COLOR_DEFAULT) {
2725                return mContext.getResources().getColor(R.color.notification_icon_bg_color);
2726            }
2727            return mColor;
2728        }
2729
2730        /**
2731         * Apply the unstyled operations and return a new {@link Notification} object.
2732         * @hide
2733         */
2734        public Notification buildUnstyled() {
2735            Notification n = new Notification();
2736            n.when = mWhen;
2737            n.icon = mSmallIcon;
2738            n.iconLevel = mSmallIconLevel;
2739            n.number = mNumber;
2740
2741            n.color = sanitizeColor();
2742
2743            n.contentView = makeContentView();
2744            n.contentIntent = mContentIntent;
2745            n.deleteIntent = mDeleteIntent;
2746            n.fullScreenIntent = mFullScreenIntent;
2747            n.tickerText = mTickerText;
2748            n.tickerView = makeTickerView();
2749            n.largeIcon = mLargeIcon;
2750            n.sound = mSound;
2751            n.audioStreamType = mAudioStreamType;
2752            n.audioAttributes = mAudioAttributes;
2753            n.vibrate = mVibrate;
2754            n.ledARGB = mLedArgb;
2755            n.ledOnMS = mLedOnMs;
2756            n.ledOffMS = mLedOffMs;
2757            n.defaults = mDefaults;
2758            n.flags = mFlags;
2759            n.bigContentView = makeBigContentView();
2760            n.headsUpContentView = makeHeadsUpContentView();
2761            if (mLedOnMs != 0 || mLedOffMs != 0) {
2762                n.flags |= FLAG_SHOW_LIGHTS;
2763            }
2764            if ((mDefaults & DEFAULT_LIGHTS) != 0) {
2765                n.flags |= FLAG_SHOW_LIGHTS;
2766            }
2767            n.category = mCategory;
2768            n.mGroupKey = mGroupKey;
2769            n.mSortKey = mSortKey;
2770            n.priority = mPriority;
2771            if (mActions.size() > 0) {
2772                n.actions = new Action[mActions.size()];
2773                mActions.toArray(n.actions);
2774            }
2775            n.visibility = mVisibility;
2776
2777            if (mPublicVersion != null) {
2778                n.publicVersion = new Notification();
2779                mPublicVersion.cloneInto(n.publicVersion, true);
2780            }
2781
2782            return n;
2783        }
2784
2785        /**
2786         * Capture, in the provided bundle, semantic information used in the construction of
2787         * this Notification object.
2788         * @hide
2789         */
2790        public void populateExtras(Bundle extras) {
2791            // Store original information used in the construction of this object
2792            extras.putCharSequence(EXTRA_TITLE, mContentTitle);
2793            extras.putCharSequence(EXTRA_TEXT, mContentText);
2794            extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
2795            extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo);
2796            extras.putInt(EXTRA_SMALL_ICON, mSmallIcon);
2797            extras.putInt(EXTRA_PROGRESS, mProgress);
2798            extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax);
2799            extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate);
2800            extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer);
2801            extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen);
2802            if (mLargeIcon != null) {
2803                extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
2804            }
2805            if (!mPeople.isEmpty()) {
2806                extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
2807            }
2808        }
2809
2810        /**
2811         * @deprecated Use {@link #build()} instead.
2812         */
2813        @Deprecated
2814        public Notification getNotification() {
2815            return build();
2816        }
2817
2818        /**
2819         * Combine all of the options that have been set and return a new {@link Notification}
2820         * object.
2821         */
2822        public Notification build() {
2823            Notification n = buildUnstyled();
2824
2825            if (mStyle != null) {
2826                n = mStyle.buildStyled(n);
2827            }
2828
2829            n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle();
2830
2831            populateExtras(n.extras);
2832            if (mStyle != null) {
2833                mStyle.addExtras(n.extras);
2834            }
2835
2836            return n;
2837        }
2838
2839        /**
2840         * Apply this Builder to an existing {@link Notification} object.
2841         *
2842         * @hide
2843         */
2844        public Notification buildInto(Notification n) {
2845            build().cloneInto(n, true);
2846            return n;
2847        }
2848
2849
2850        private int getBaseLayoutResource() {
2851            return R.layout.notification_template_material_base;
2852        }
2853
2854        private int getBigBaseLayoutResource() {
2855            return R.layout.notification_template_material_big_base;
2856        }
2857
2858        private int getBigPictureLayoutResource() {
2859            return R.layout.notification_template_material_big_picture;
2860        }
2861
2862        private int getBigTextLayoutResource() {
2863            return R.layout.notification_template_material_big_text;
2864        }
2865
2866        private int getInboxLayoutResource() {
2867            return R.layout.notification_template_material_inbox;
2868        }
2869
2870        private int getActionLayoutResource() {
2871            return R.layout.notification_material_action;
2872        }
2873
2874        private int getActionTombstoneLayoutResource() {
2875            return R.layout.notification_material_action_tombstone;
2876        }
2877    }
2878
2879    /**
2880     * An object that can apply a rich notification style to a {@link Notification.Builder}
2881     * object.
2882     */
2883    public static abstract class Style {
2884        private CharSequence mBigContentTitle;
2885        private CharSequence mSummaryText = null;
2886        private boolean mSummaryTextSet = false;
2887
2888        protected Builder mBuilder;
2889
2890        /**
2891         * Overrides ContentTitle in the big form of the template.
2892         * This defaults to the value passed to setContentTitle().
2893         */
2894        protected void internalSetBigContentTitle(CharSequence title) {
2895            mBigContentTitle = title;
2896        }
2897
2898        /**
2899         * Set the first line of text after the detail section in the big form of the template.
2900         */
2901        protected void internalSetSummaryText(CharSequence cs) {
2902            mSummaryText = cs;
2903            mSummaryTextSet = true;
2904        }
2905
2906        public void setBuilder(Builder builder) {
2907            if (mBuilder != builder) {
2908                mBuilder = builder;
2909                if (mBuilder != null) {
2910                    mBuilder.setStyle(this);
2911                }
2912            }
2913        }
2914
2915        protected void checkBuilder() {
2916            if (mBuilder == null) {
2917                throw new IllegalArgumentException("Style requires a valid Builder object");
2918            }
2919        }
2920
2921        protected RemoteViews getStandardView(int layoutId) {
2922            checkBuilder();
2923
2924            if (mBigContentTitle != null) {
2925                mBuilder.setContentTitle(mBigContentTitle);
2926            }
2927
2928            RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
2929
2930            if (mBigContentTitle != null && mBigContentTitle.equals("")) {
2931                contentView.setViewVisibility(R.id.line1, View.GONE);
2932            } else {
2933                contentView.setViewVisibility(R.id.line1, View.VISIBLE);
2934            }
2935
2936            // The last line defaults to the subtext, but can be replaced by mSummaryText
2937            final CharSequence overflowText =
2938                    mSummaryTextSet ? mSummaryText
2939                                    : mBuilder.mSubText;
2940            if (overflowText != null) {
2941                contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
2942                contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
2943                contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2944            } else {
2945                contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
2946                contentView.setViewVisibility(R.id.line3, View.GONE);
2947            }
2948
2949            return contentView;
2950        }
2951
2952        /**
2953         * @hide
2954         */
2955        public void addExtras(Bundle extras) {
2956            if (mSummaryTextSet) {
2957                extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
2958            }
2959            if (mBigContentTitle != null) {
2960                extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
2961            }
2962            extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
2963        }
2964
2965        /**
2966         * @hide
2967         */
2968        public abstract Notification buildStyled(Notification wip);
2969
2970        /**
2971         * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
2972         * attached to.
2973         *
2974         * @return the fully constructed Notification.
2975         */
2976        public Notification build() {
2977            checkBuilder();
2978            return mBuilder.build();
2979        }
2980    }
2981
2982    /**
2983     * Helper class for generating large-format notifications that include a large image attachment.
2984     *
2985     * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
2986     * <pre class="prettyprint">
2987     * Notification notif = new Notification.Builder(mContext)
2988     *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
2989     *     .setContentText(subject)
2990     *     .setSmallIcon(R.drawable.new_post)
2991     *     .setLargeIcon(aBitmap)
2992     *     .setStyle(new Notification.BigPictureStyle()
2993     *         .bigPicture(aBigBitmap))
2994     *     .build();
2995     * </pre>
2996     *
2997     * @see Notification#bigContentView
2998     */
2999    public static class BigPictureStyle extends Style {
3000        private Bitmap mPicture;
3001        private Bitmap mBigLargeIcon;
3002        private boolean mBigLargeIconSet = false;
3003
3004        public BigPictureStyle() {
3005        }
3006
3007        public BigPictureStyle(Builder builder) {
3008            setBuilder(builder);
3009        }
3010
3011        /**
3012         * Overrides ContentTitle in the big form of the template.
3013         * This defaults to the value passed to setContentTitle().
3014         */
3015        public BigPictureStyle setBigContentTitle(CharSequence title) {
3016            internalSetBigContentTitle(safeCharSequence(title));
3017            return this;
3018        }
3019
3020        /**
3021         * Set the first line of text after the detail section in the big form of the template.
3022         */
3023        public BigPictureStyle setSummaryText(CharSequence cs) {
3024            internalSetSummaryText(safeCharSequence(cs));
3025            return this;
3026        }
3027
3028        /**
3029         * Provide the bitmap to be used as the payload for the BigPicture notification.
3030         */
3031        public BigPictureStyle bigPicture(Bitmap b) {
3032            mPicture = b;
3033            return this;
3034        }
3035
3036        /**
3037         * Override the large icon when the big notification is shown.
3038         */
3039        public BigPictureStyle bigLargeIcon(Bitmap b) {
3040            mBigLargeIconSet = true;
3041            mBigLargeIcon = b;
3042            return this;
3043        }
3044
3045        private RemoteViews makeBigContentView() {
3046            RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
3047
3048            contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3049
3050            return contentView;
3051        }
3052
3053        /**
3054         * @hide
3055         */
3056        public void addExtras(Bundle extras) {
3057            super.addExtras(extras);
3058
3059            if (mBigLargeIconSet) {
3060                extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3061            }
3062            extras.putParcelable(EXTRA_PICTURE, mPicture);
3063        }
3064
3065        /**
3066         * @hide
3067         */
3068        @Override
3069        public Notification buildStyled(Notification wip) {
3070            if (mBigLargeIconSet ) {
3071                mBuilder.mLargeIcon = mBigLargeIcon;
3072            }
3073            wip.bigContentView = makeBigContentView();
3074            return wip;
3075        }
3076    }
3077
3078    /**
3079     * Helper class for generating large-format notifications that include a lot of text.
3080     *
3081     * Here's how you'd set the <code>BigTextStyle</code> on a notification:
3082     * <pre class="prettyprint">
3083     * Notification notif = new Notification.Builder(mContext)
3084     *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
3085     *     .setContentText(subject)
3086     *     .setSmallIcon(R.drawable.new_mail)
3087     *     .setLargeIcon(aBitmap)
3088     *     .setStyle(new Notification.BigTextStyle()
3089     *         .bigText(aVeryLongString))
3090     *     .build();
3091     * </pre>
3092     *
3093     * @see Notification#bigContentView
3094     */
3095    public static class BigTextStyle extends Style {
3096        private CharSequence mBigText;
3097
3098        public BigTextStyle() {
3099        }
3100
3101        public BigTextStyle(Builder builder) {
3102            setBuilder(builder);
3103        }
3104
3105        /**
3106         * Overrides ContentTitle in the big form of the template.
3107         * This defaults to the value passed to setContentTitle().
3108         */
3109        public BigTextStyle setBigContentTitle(CharSequence title) {
3110            internalSetBigContentTitle(safeCharSequence(title));
3111            return this;
3112        }
3113
3114        /**
3115         * Set the first line of text after the detail section in the big form of the template.
3116         */
3117        public BigTextStyle setSummaryText(CharSequence cs) {
3118            internalSetSummaryText(safeCharSequence(cs));
3119            return this;
3120        }
3121
3122        /**
3123         * Provide the longer text to be displayed in the big form of the
3124         * template in place of the content text.
3125         */
3126        public BigTextStyle bigText(CharSequence cs) {
3127            mBigText = safeCharSequence(cs);
3128            return this;
3129        }
3130
3131        /**
3132         * @hide
3133         */
3134        public void addExtras(Bundle extras) {
3135            super.addExtras(extras);
3136
3137            extras.putCharSequence(EXTRA_TEXT, mBigText);
3138        }
3139
3140        private RemoteViews makeBigContentView() {
3141            // Remove the content text so line3 only shows if you have a summary
3142            final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null);
3143            mBuilder.mContentText = null;
3144
3145            RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
3146
3147            if (hadThreeLines) {
3148                // vertical centering
3149                contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
3150            }
3151
3152            contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
3153            contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
3154            contentView.setViewVisibility(R.id.text2, View.GONE);
3155
3156            return contentView;
3157        }
3158
3159        /**
3160         * @hide
3161         */
3162        @Override
3163        public Notification buildStyled(Notification wip) {
3164            wip.bigContentView = makeBigContentView();
3165
3166            wip.extras.putCharSequence(EXTRA_TEXT, mBigText);
3167
3168            return wip;
3169        }
3170    }
3171
3172    /**
3173     * Helper class for generating large-format notifications that include a list of (up to 5) strings.
3174     *
3175     * Here's how you'd set the <code>InboxStyle</code> on a notification:
3176     * <pre class="prettyprint">
3177     * Notification notif = new Notification.Builder(mContext)
3178     *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
3179     *     .setContentText(subject)
3180     *     .setSmallIcon(R.drawable.new_mail)
3181     *     .setLargeIcon(aBitmap)
3182     *     .setStyle(new Notification.InboxStyle()
3183     *         .addLine(str1)
3184     *         .addLine(str2)
3185     *         .setContentTitle(&quot;&quot;)
3186     *         .setSummaryText(&quot;+3 more&quot;))
3187     *     .build();
3188     * </pre>
3189     *
3190     * @see Notification#bigContentView
3191     */
3192    public static class InboxStyle extends Style {
3193        private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
3194
3195        public InboxStyle() {
3196        }
3197
3198        public InboxStyle(Builder builder) {
3199            setBuilder(builder);
3200        }
3201
3202        /**
3203         * Overrides ContentTitle in the big form of the template.
3204         * This defaults to the value passed to setContentTitle().
3205         */
3206        public InboxStyle setBigContentTitle(CharSequence title) {
3207            internalSetBigContentTitle(safeCharSequence(title));
3208            return this;
3209        }
3210
3211        /**
3212         * Set the first line of text after the detail section in the big form of the template.
3213         */
3214        public InboxStyle setSummaryText(CharSequence cs) {
3215            internalSetSummaryText(safeCharSequence(cs));
3216            return this;
3217        }
3218
3219        /**
3220         * Append a line to the digest section of the Inbox notification.
3221         */
3222        public InboxStyle addLine(CharSequence cs) {
3223            mTexts.add(safeCharSequence(cs));
3224            return this;
3225        }
3226
3227        /**
3228         * @hide
3229         */
3230        public void addExtras(Bundle extras) {
3231            super.addExtras(extras);
3232            CharSequence[] a = new CharSequence[mTexts.size()];
3233            extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
3234        }
3235
3236        private RemoteViews makeBigContentView() {
3237            // Remove the content text so line3 disappears unless you have a summary
3238            mBuilder.mContentText = null;
3239            RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
3240
3241            contentView.setViewVisibility(R.id.text2, View.GONE);
3242
3243            int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
3244                    R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
3245
3246            // Make sure all rows are gone in case we reuse a view.
3247            for (int rowId : rowIds) {
3248                contentView.setViewVisibility(rowId, View.GONE);
3249            }
3250
3251
3252            int i=0;
3253            while (i < mTexts.size() && i < rowIds.length) {
3254                CharSequence str = mTexts.get(i);
3255                if (str != null && !str.equals("")) {
3256                    contentView.setViewVisibility(rowIds[i], View.VISIBLE);
3257                    contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
3258                }
3259                i++;
3260            }
3261
3262            contentView.setViewVisibility(R.id.inbox_end_pad,
3263                    mTexts.size() > 0 ? View.VISIBLE : View.GONE);
3264
3265            contentView.setViewVisibility(R.id.inbox_more,
3266                    mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
3267
3268            return contentView;
3269        }
3270
3271        /**
3272         * @hide
3273         */
3274        @Override
3275        public Notification buildStyled(Notification wip) {
3276            wip.bigContentView = makeBigContentView();
3277
3278            return wip;
3279        }
3280    }
3281
3282    /**
3283     * Notification style for media playback notifications.
3284     *
3285     * In the expanded form, {@link Notification#bigContentView}, up to 5
3286     * {@link Notification.Action}s specified with
3287     * {@link Notification.Builder#addAction(int, CharSequence, PendingIntent) addAction} will be
3288     * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
3289     * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
3290     * treated as album artwork.
3291     *
3292     * Unlike the other styles provided here, MediaStyle can also modify the standard-size
3293     * {@link Notification#contentView}; by providing action indices to
3294     * {@link #setShowActionsInCompactView(int...)} you can promote up to 2 actions to be displayed
3295     * in the standard view alongside the usual content.
3296     *
3297     * Notifications created with MediaStyle will have their category set to
3298     * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
3299     * category using {@link Notification.Builder#setCategory(String) setCategory()}.
3300     *
3301     * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
3302     * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
3303     * the System UI can identify this as a notification representing an active media session
3304     * and respond accordingly (by showing album artwork in the lockscreen, for example).
3305     *
3306     * To use this style with your Notification, feed it to
3307     * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
3308     * <pre class="prettyprint">
3309     * Notification noti = new Notification.Builder()
3310     *     .setSmallIcon(R.drawable.ic_stat_player)
3311     *     .setContentTitle(&quot;Track title&quot;)     // these three lines are optional
3312     *     .setContentText(&quot;Artist - Album&quot;)   // if you use
3313     *     .setLargeIcon(albumArtBitmap))      // setMediaSession(token)
3314     *     .setStyle(<b>new Notification.MediaStyle()</b>
3315     *         .setMediaSession(mySession))
3316     *     .build();
3317     * </pre>
3318     *
3319     * @see Notification#bigContentView
3320     */
3321    public static class MediaStyle extends Style {
3322        static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 2;
3323        static final int MAX_MEDIA_BUTTONS = 5;
3324
3325        private int[] mActionsToShowInCompact = null;
3326        private MediaSession.Token mToken;
3327
3328        public MediaStyle() {
3329        }
3330
3331        public MediaStyle(Builder builder) {
3332            setBuilder(builder);
3333        }
3334
3335        /**
3336         * Request up to 2 actions (by index in the order of addition) to be shown in the compact
3337         * notification view.
3338         */
3339        public MediaStyle setShowActionsInCompactView(int...actions) {
3340            mActionsToShowInCompact = actions;
3341            return this;
3342        }
3343
3344        /**
3345         * Attach a {@link android.media.session.MediaSession.Token} to this Notification
3346         * to provide additional playback information and control to the SystemUI.
3347         */
3348        public MediaStyle setMediaSession(MediaSession.Token token) {
3349            mToken = token;
3350            return this;
3351        }
3352
3353        @Override
3354        public Notification buildStyled(Notification wip) {
3355            wip.contentView = makeMediaContentView();
3356            wip.bigContentView = makeMediaBigContentView();
3357            if (wip.category == null) {
3358                wip.category = Notification.CATEGORY_TRANSPORT;
3359            }
3360            return wip;
3361        }
3362
3363        /** @hide */
3364        @Override
3365        public void addExtras(Bundle extras) {
3366            super.addExtras(extras);
3367
3368            if (mToken != null) {
3369                extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
3370            }
3371            if (mActionsToShowInCompact != null) {
3372                extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
3373            }
3374        }
3375
3376        private RemoteViews generateMediaActionButton(Action action) {
3377            final boolean tombstone = (action.actionIntent == null);
3378            RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
3379                    R.layout.notification_material_media_action);
3380            button.setImageViewResource(R.id.action0, action.icon);
3381            if (!tombstone) {
3382                button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
3383            }
3384            button.setContentDescription(R.id.action0, action.title);
3385            return button;
3386        }
3387
3388        private RemoteViews makeMediaContentView() {
3389            RemoteViews view = mBuilder.applyStandardTemplate(
3390                    R.layout.notification_template_material_media, true /* 1U */);
3391
3392            final int numActions = mBuilder.mActions.size();
3393            final int N = mActionsToShowInCompact == null
3394                    ? 0
3395                    : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
3396            if (N > 0) {
3397                view.removeAllViews(R.id.actions);
3398                for (int i = 0; i < N; i++) {
3399                    if (i >= numActions) {
3400                        throw new IllegalArgumentException(String.format(
3401                                "setShowActionsInCompactView: action %d out of bounds (max %d)",
3402                                i, numActions - 1));
3403                    }
3404
3405                    final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
3406                    final RemoteViews button = generateMediaActionButton(action);
3407                    view.addView(R.id.actions, button);
3408                }
3409            }
3410            return view;
3411        }
3412
3413        private RemoteViews makeMediaBigContentView() {
3414            RemoteViews big = mBuilder.applyStandardTemplate(
3415                    R.layout.notification_template_material_big_media, false);
3416
3417            final int N = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
3418            if (N > 0) {
3419                big.removeAllViews(R.id.actions);
3420                for (int i=0; i<N; i++) {
3421                    final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
3422                    big.addView(R.id.actions, button);
3423                }
3424            }
3425            return big;
3426        }
3427    }
3428
3429    /**
3430     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
3431     * metadata or change options on a notification builder.
3432     */
3433    public interface Extender {
3434        /**
3435         * Apply this extender to a notification builder.
3436         * @param builder the builder to be modified.
3437         * @return the build object for chaining.
3438         */
3439        public Builder extend(Builder builder);
3440    }
3441
3442    /**
3443     * Helper class to add wearable extensions to notifications.
3444     * <p class="note"> See
3445     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
3446     * for Android Wear</a> for more information on how to use this class.
3447     * <p>
3448     * To create a notification with wearable extensions:
3449     * <ol>
3450     *   <li>Create a {@link android.app.Notification.Builder}, setting any desired
3451     *   properties.
3452     *   <li>Create a {@link android.app.Notification.WearableExtender}.
3453     *   <li>Set wearable-specific properties using the
3454     *   {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
3455     *   <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
3456     *   notification.
3457     *   <li>Post the notification to the notification system with the
3458     *   {@code NotificationManager.notify(...)} methods.
3459     * </ol>
3460     *
3461     * <pre class="prettyprint">
3462     * Notification notif = new Notification.Builder(mContext)
3463     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
3464     *         .setContentText(subject)
3465     *         .setSmallIcon(R.drawable.new_mail)
3466     *         .extend(new Notification.WearableExtender()
3467     *                 .setContentIcon(R.drawable.new_mail))
3468     *         .build();
3469     * NotificationManager notificationManger =
3470     *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
3471     * notificationManger.notify(0, notif);</pre>
3472     *
3473     * <p>Wearable extensions can be accessed on an existing notification by using the
3474     * {@code WearableExtender(Notification)} constructor,
3475     * and then using the {@code get} methods to access values.
3476     *
3477     * <pre class="prettyprint">
3478     * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
3479     *         notification);
3480     * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
3481     */
3482    public static final class WearableExtender implements Extender {
3483        /**
3484         * Sentinel value for an action index that is unset.
3485         */
3486        public static final int UNSET_ACTION_INDEX = -1;
3487
3488        /**
3489         * Size value for use with {@link #setCustomSizePreset} to show this notification with
3490         * default sizing.
3491         * <p>For custom display notifications created using {@link #setDisplayIntent},
3492         * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
3493         * on their content.
3494         */
3495        public static final int SIZE_DEFAULT = 0;
3496
3497        /**
3498         * Size value for use with {@link #setCustomSizePreset} to show this notification
3499         * with an extra small size.
3500         * <p>This value is only applicable for custom display notifications created using
3501         * {@link #setDisplayIntent}.
3502         */
3503        public static final int SIZE_XSMALL = 1;
3504
3505        /**
3506         * Size value for use with {@link #setCustomSizePreset} to show this notification
3507         * with a small size.
3508         * <p>This value is only applicable for custom display notifications created using
3509         * {@link #setDisplayIntent}.
3510         */
3511        public static final int SIZE_SMALL = 2;
3512
3513        /**
3514         * Size value for use with {@link #setCustomSizePreset} to show this notification
3515         * with a medium size.
3516         * <p>This value is only applicable for custom display notifications created using
3517         * {@link #setDisplayIntent}.
3518         */
3519        public static final int SIZE_MEDIUM = 3;
3520
3521        /**
3522         * Size value for use with {@link #setCustomSizePreset} to show this notification
3523         * with a large size.
3524         * <p>This value is only applicable for custom display notifications created using
3525         * {@link #setDisplayIntent}.
3526         */
3527        public static final int SIZE_LARGE = 4;
3528
3529        /**
3530         * Size value for use with {@link #setCustomSizePreset} to show this notification
3531         * full screen.
3532         * <p>This value is only applicable for custom display notifications created using
3533         * {@link #setDisplayIntent}.
3534         */
3535        public static final int SIZE_FULL_SCREEN = 5;
3536
3537        /** Notification extra which contains wearable extensions */
3538        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
3539
3540        // Keys within EXTRA_WEARABLE_OPTIONS for wearable options.
3541        private static final String KEY_ACTIONS = "actions";
3542        private static final String KEY_FLAGS = "flags";
3543        private static final String KEY_DISPLAY_INTENT = "displayIntent";
3544        private static final String KEY_PAGES = "pages";
3545        private static final String KEY_BACKGROUND = "background";
3546        private static final String KEY_CONTENT_ICON = "contentIcon";
3547        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
3548        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
3549        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
3550        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
3551        private static final String KEY_GRAVITY = "gravity";
3552
3553        // Flags bitwise-ored to mFlags
3554        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
3555        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
3556        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
3557        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
3558
3559        // Default value for flags integer
3560        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
3561
3562        private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
3563        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
3564
3565        private ArrayList<Action> mActions = new ArrayList<Action>();
3566        private int mFlags = DEFAULT_FLAGS;
3567        private PendingIntent mDisplayIntent;
3568        private ArrayList<Notification> mPages = new ArrayList<Notification>();
3569        private Bitmap mBackground;
3570        private int mContentIcon;
3571        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
3572        private int mContentActionIndex = UNSET_ACTION_INDEX;
3573        private int mCustomSizePreset = SIZE_DEFAULT;
3574        private int mCustomContentHeight;
3575        private int mGravity = DEFAULT_GRAVITY;
3576
3577        /**
3578         * Create a {@link android.app.Notification.WearableExtender} with default
3579         * options.
3580         */
3581        public WearableExtender() {
3582        }
3583
3584        public WearableExtender(Notification notif) {
3585            Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
3586            if (wearableBundle != null) {
3587                List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
3588                if (actions != null) {
3589                    mActions.addAll(actions);
3590                }
3591
3592                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
3593                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
3594
3595                Notification[] pages = getNotificationArrayFromBundle(
3596                        wearableBundle, KEY_PAGES);
3597                if (pages != null) {
3598                    Collections.addAll(mPages, pages);
3599                }
3600
3601                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
3602                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
3603                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
3604                        DEFAULT_CONTENT_ICON_GRAVITY);
3605                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
3606                        UNSET_ACTION_INDEX);
3607                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
3608                        SIZE_DEFAULT);
3609                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
3610                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
3611            }
3612        }
3613
3614        /**
3615         * Apply wearable extensions to a notification that is being built. This is typically
3616         * called by the {@link android.app.Notification.Builder#extend} method of
3617         * {@link android.app.Notification.Builder}.
3618         */
3619        @Override
3620        public Notification.Builder extend(Notification.Builder builder) {
3621            Bundle wearableBundle = new Bundle();
3622
3623            if (!mActions.isEmpty()) {
3624                wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
3625            }
3626            if (mFlags != DEFAULT_FLAGS) {
3627                wearableBundle.putInt(KEY_FLAGS, mFlags);
3628            }
3629            if (mDisplayIntent != null) {
3630                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
3631            }
3632            if (!mPages.isEmpty()) {
3633                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
3634                        new Notification[mPages.size()]));
3635            }
3636            if (mBackground != null) {
3637                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
3638            }
3639            if (mContentIcon != 0) {
3640                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
3641            }
3642            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
3643                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
3644            }
3645            if (mContentActionIndex != UNSET_ACTION_INDEX) {
3646                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
3647                        mContentActionIndex);
3648            }
3649            if (mCustomSizePreset != SIZE_DEFAULT) {
3650                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
3651            }
3652            if (mCustomContentHeight != 0) {
3653                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
3654            }
3655            if (mGravity != DEFAULT_GRAVITY) {
3656                wearableBundle.putInt(KEY_GRAVITY, mGravity);
3657            }
3658
3659            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
3660            return builder;
3661        }
3662
3663        @Override
3664        public WearableExtender clone() {
3665            WearableExtender that = new WearableExtender();
3666            that.mActions = new ArrayList<Action>(this.mActions);
3667            that.mFlags = this.mFlags;
3668            that.mDisplayIntent = this.mDisplayIntent;
3669            that.mPages = new ArrayList<Notification>(this.mPages);
3670            that.mBackground = this.mBackground;
3671            that.mContentIcon = this.mContentIcon;
3672            that.mContentIconGravity = this.mContentIconGravity;
3673            that.mContentActionIndex = this.mContentActionIndex;
3674            that.mCustomSizePreset = this.mCustomSizePreset;
3675            that.mCustomContentHeight = this.mCustomContentHeight;
3676            that.mGravity = this.mGravity;
3677            return that;
3678        }
3679
3680        /**
3681         * Add a wearable action to this notification.
3682         *
3683         * <p>When wearable actions are added using this method, the set of actions that
3684         * show on a wearable device splits from devices that only show actions added
3685         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
3686         * of which actions display on different devices.
3687         *
3688         * @param action the action to add to this notification
3689         * @return this object for method chaining
3690         * @see android.app.Notification.Action
3691         */
3692        public WearableExtender addAction(Action action) {
3693            mActions.add(action);
3694            return this;
3695        }
3696
3697        /**
3698         * Adds wearable actions to this notification.
3699         *
3700         * <p>When wearable actions are added using this method, the set of actions that
3701         * show on a wearable device splits from devices that only show actions added
3702         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
3703         * of which actions display on different devices.
3704         *
3705         * @param actions the actions to add to this notification
3706         * @return this object for method chaining
3707         * @see android.app.Notification.Action
3708         */
3709        public WearableExtender addActions(List<Action> actions) {
3710            mActions.addAll(actions);
3711            return this;
3712        }
3713
3714        /**
3715         * Clear all wearable actions present on this builder.
3716         * @return this object for method chaining.
3717         * @see #addAction
3718         */
3719        public WearableExtender clearActions() {
3720            mActions.clear();
3721            return this;
3722        }
3723
3724        /**
3725         * Get the wearable actions present on this notification.
3726         */
3727        public List<Action> getActions() {
3728            return mActions;
3729        }
3730
3731        /**
3732         * Set an intent to launch inside of an activity view when displaying
3733         * this notification. The {@link PendingIntent} provided should be for an activity.
3734         *
3735         * <pre class="prettyprint">
3736         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
3737         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
3738         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
3739         * Notification notif = new Notification.Builder(context)
3740         *         .extend(new Notification.WearableExtender()
3741         *                 .setDisplayIntent(displayPendingIntent)
3742         *                 .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
3743         *         .build();</pre>
3744         *
3745         * <p>The activity to launch needs to allow embedding, must be exported, and
3746         * should have an empty task affinity. It is also recommended to use the device
3747         * default light theme.
3748         *
3749         * <p>Example AndroidManifest.xml entry:
3750         * <pre class="prettyprint">
3751         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
3752         *     android:exported=&quot;true&quot;
3753         *     android:allowEmbedded=&quot;true&quot;
3754         *     android:taskAffinity=&quot;&quot;
3755         *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
3756         *
3757         * @param intent the {@link PendingIntent} for an activity
3758         * @return this object for method chaining
3759         * @see android.app.Notification.WearableExtender#getDisplayIntent
3760         */
3761        public WearableExtender setDisplayIntent(PendingIntent intent) {
3762            mDisplayIntent = intent;
3763            return this;
3764        }
3765
3766        /**
3767         * Get the intent to launch inside of an activity view when displaying this
3768         * notification. This {@code PendingIntent} should be for an activity.
3769         */
3770        public PendingIntent getDisplayIntent() {
3771            return mDisplayIntent;
3772        }
3773
3774        /**
3775         * Add an additional page of content to display with this notification. The current
3776         * notification forms the first page, and pages added using this function form
3777         * subsequent pages. This field can be used to separate a notification into multiple
3778         * sections.
3779         *
3780         * @param page the notification to add as another page
3781         * @return this object for method chaining
3782         * @see android.app.Notification.WearableExtender#getPages
3783         */
3784        public WearableExtender addPage(Notification page) {
3785            mPages.add(page);
3786            return this;
3787        }
3788
3789        /**
3790         * Add additional pages of content to display with this notification. The current
3791         * notification forms the first page, and pages added using this function form
3792         * subsequent pages. This field can be used to separate a notification into multiple
3793         * sections.
3794         *
3795         * @param pages a list of notifications
3796         * @return this object for method chaining
3797         * @see android.app.Notification.WearableExtender#getPages
3798         */
3799        public WearableExtender addPages(List<Notification> pages) {
3800            mPages.addAll(pages);
3801            return this;
3802        }
3803
3804        /**
3805         * Clear all additional pages present on this builder.
3806         * @return this object for method chaining.
3807         * @see #addPage
3808         */
3809        public WearableExtender clearPages() {
3810            mPages.clear();
3811            return this;
3812        }
3813
3814        /**
3815         * Get the array of additional pages of content for displaying this notification. The
3816         * current notification forms the first page, and elements within this array form
3817         * subsequent pages. This field can be used to separate a notification into multiple
3818         * sections.
3819         * @return the pages for this notification
3820         */
3821        public List<Notification> getPages() {
3822            return mPages;
3823        }
3824
3825        /**
3826         * Set a background image to be displayed behind the notification content.
3827         * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
3828         * will work with any notification style.
3829         *
3830         * @param background the background bitmap
3831         * @return this object for method chaining
3832         * @see android.app.Notification.WearableExtender#getBackground
3833         */
3834        public WearableExtender setBackground(Bitmap background) {
3835            mBackground = background;
3836            return this;
3837        }
3838
3839        /**
3840         * Get a background image to be displayed behind the notification content.
3841         * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
3842         * will work with any notification style.
3843         *
3844         * @return the background image
3845         * @see android.app.Notification.WearableExtender#setBackground
3846         */
3847        public Bitmap getBackground() {
3848            return mBackground;
3849        }
3850
3851        /**
3852         * Set an icon that goes with the content of this notification.
3853         */
3854        public WearableExtender setContentIcon(int icon) {
3855            mContentIcon = icon;
3856            return this;
3857        }
3858
3859        /**
3860         * Get an icon that goes with the content of this notification.
3861         */
3862        public int getContentIcon() {
3863            return mContentIcon;
3864        }
3865
3866        /**
3867         * Set the gravity that the content icon should have within the notification display.
3868         * Supported values include {@link android.view.Gravity#START} and
3869         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
3870         * @see #setContentIcon
3871         */
3872        public WearableExtender setContentIconGravity(int contentIconGravity) {
3873            mContentIconGravity = contentIconGravity;
3874            return this;
3875        }
3876
3877        /**
3878         * Get the gravity that the content icon should have within the notification display.
3879         * Supported values include {@link android.view.Gravity#START} and
3880         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
3881         * @see #getContentIcon
3882         */
3883        public int getContentIconGravity() {
3884            return mContentIconGravity;
3885        }
3886
3887        /**
3888         * Set an action from this notification's actions to be clickable with the content of
3889         * this notification. This action will no longer display separately from the
3890         * notification's content.
3891         *
3892         * <p>For notifications with multiple pages, child pages can also have content actions
3893         * set, although the list of available actions comes from the main notification and not
3894         * from the child page's notification.
3895         *
3896         * @param actionIndex The index of the action to hoist onto the current notification page.
3897         *                    If wearable actions were added to the main notification, this index
3898         *                    will apply to that list, otherwise it will apply to the regular
3899         *                    actions list.
3900         */
3901        public WearableExtender setContentAction(int actionIndex) {
3902            mContentActionIndex = actionIndex;
3903            return this;
3904        }
3905
3906        /**
3907         * Get the index of the notification action, if any, that was specified as being clickable
3908         * with the content of this notification. This action will no longer display separately
3909         * from the notification's content.
3910         *
3911         * <p>For notifications with multiple pages, child pages can also have content actions
3912         * set, although the list of available actions comes from the main notification and not
3913         * from the child page's notification.
3914         *
3915         * <p>If wearable specific actions were added to the main notification, this index will
3916         * apply to that list, otherwise it will apply to the regular actions list.
3917         *
3918         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
3919         */
3920        public int getContentAction() {
3921            return mContentActionIndex;
3922        }
3923
3924        /**
3925         * Set the gravity that this notification should have within the available viewport space.
3926         * Supported values include {@link android.view.Gravity#TOP},
3927         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
3928         * The default value is {@link android.view.Gravity#BOTTOM}.
3929         */
3930        public WearableExtender setGravity(int gravity) {
3931            mGravity = gravity;
3932            return this;
3933        }
3934
3935        /**
3936         * Get the gravity that this notification should have within the available viewport space.
3937         * Supported values include {@link android.view.Gravity#TOP},
3938         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
3939         * The default value is {@link android.view.Gravity#BOTTOM}.
3940         */
3941        public int getGravity() {
3942            return mGravity;
3943        }
3944
3945        /**
3946         * Set the custom size preset for the display of this notification out of the available
3947         * presets found in {@link android.app.Notification.WearableExtender}, e.g.
3948         * {@link #SIZE_LARGE}.
3949         * <p>Some custom size presets are only applicable for custom display notifications created
3950         * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
3951         * documentation for the preset in question. See also
3952         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
3953         */
3954        public WearableExtender setCustomSizePreset(int sizePreset) {
3955            mCustomSizePreset = sizePreset;
3956            return this;
3957        }
3958
3959        /**
3960         * Get the custom size preset for the display of this notification out of the available
3961         * presets found in {@link android.app.Notification.WearableExtender}, e.g.
3962         * {@link #SIZE_LARGE}.
3963         * <p>Some custom size presets are only applicable for custom display notifications created
3964         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
3965         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
3966         */
3967        public int getCustomSizePreset() {
3968            return mCustomSizePreset;
3969        }
3970
3971        /**
3972         * Set the custom height in pixels for the display of this notification's content.
3973         * <p>This option is only available for custom display notifications created
3974         * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
3975         * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
3976         * {@link #getCustomContentHeight}.
3977         */
3978        public WearableExtender setCustomContentHeight(int height) {
3979            mCustomContentHeight = height;
3980            return this;
3981        }
3982
3983        /**
3984         * Get the custom height in pixels for the display of this notification's content.
3985         * <p>This option is only available for custom display notifications created
3986         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
3987         * {@link #setCustomContentHeight}.
3988         */
3989        public int getCustomContentHeight() {
3990            return mCustomContentHeight;
3991        }
3992
3993        /**
3994         * Set whether the scrolling position for the contents of this notification should start
3995         * at the bottom of the contents instead of the top when the contents are too long to
3996         * display within the screen.  Default is false (start scroll at the top).
3997         */
3998        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
3999            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4000            return this;
4001        }
4002
4003        /**
4004         * Get whether the scrolling position for the contents of this notification should start
4005         * at the bottom of the contents instead of the top when the contents are too long to
4006         * display within the screen. Default is false (start scroll at the top).
4007         */
4008        public boolean getStartScrollBottom() {
4009            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4010        }
4011
4012        /**
4013         * Set whether the content intent is available when the wearable device is not connected
4014         * to a companion device.  The user can still trigger this intent when the wearable device
4015         * is offline, but a visual hint will indicate that the content intent may not be available.
4016         * Defaults to true.
4017         */
4018        public WearableExtender setContentIntentAvailableOffline(
4019                boolean contentIntentAvailableOffline) {
4020            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4021            return this;
4022        }
4023
4024        /**
4025         * Get whether the content intent is available when the wearable device is not connected
4026         * to a companion device.  The user can still trigger this intent when the wearable device
4027         * is offline, but a visual hint will indicate that the content intent may not be available.
4028         * Defaults to true.
4029         */
4030        public boolean getContentIntentAvailableOffline() {
4031            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
4032        }
4033
4034        /**
4035         * Set a hint that this notification's icon should not be displayed.
4036         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
4037         * @return this object for method chaining
4038         */
4039        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
4040            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
4041            return this;
4042        }
4043
4044        /**
4045         * Get a hint that this notification's icon should not be displayed.
4046         * @return {@code true} if this icon should not be displayed, false otherwise.
4047         * The default value is {@code false} if this was never set.
4048         */
4049        public boolean getHintHideIcon() {
4050            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
4051        }
4052
4053        /**
4054         * Set a visual hint that only the background image of this notification should be
4055         * displayed, and other semantic content should be hidden. This hint is only applicable
4056         * to sub-pages added using {@link #addPage}.
4057         */
4058        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
4059            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
4060            return this;
4061        }
4062
4063        /**
4064         * Get a visual hint that only the background image of this notification should be
4065         * displayed, and other semantic content should be hidden. This hint is only applicable
4066         * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
4067         */
4068        public boolean getHintShowBackgroundOnly() {
4069            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
4070        }
4071
4072        private void setFlag(int mask, boolean value) {
4073            if (value) {
4074                mFlags |= mask;
4075            } else {
4076                mFlags &= ~mask;
4077            }
4078        }
4079    }
4080
4081    /**
4082     * Get an array of Notification objects from a parcelable array bundle field.
4083     * Update the bundle to have a typed array so fetches in the future don't need
4084     * to do an array copy.
4085     */
4086    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
4087        Parcelable[] array = bundle.getParcelableArray(key);
4088        if (array instanceof Notification[] || array == null) {
4089            return (Notification[]) array;
4090        }
4091        Notification[] typedArray = Arrays.copyOf(array, array.length,
4092                Notification[].class);
4093        bundle.putParcelableArray(key, typedArray);
4094        return typedArray;
4095    }
4096}
4097