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