NotificationCompat.java revision 26d3baddb1cc012cedb65e2d481f1d4fe8e9e987
1/*
2 * Copyright (C) 2012 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.support.v4.app;
18
19import android.app.Notification;
20import android.app.PendingIntent;
21import android.content.Context;
22import android.graphics.Bitmap;
23import android.graphics.Color;
24import android.media.AudioManager;
25import android.net.Uri;
26import android.os.Build;
27import android.os.Bundle;
28import android.os.Parcelable;
29import android.support.v4.view.GravityCompat;
30import android.view.Gravity;
31import android.widget.RemoteViews;
32
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.List;
36
37/**
38 * Helper for accessing features in {@link android.app.Notification}
39 * introduced after API level 4 in a backwards compatible fashion.
40 */
41public class NotificationCompat {
42
43    /**
44     * Use all default values (where applicable).
45     */
46    public static final int DEFAULT_ALL = ~0;
47
48    /**
49     * Use the default notification sound. This will ignore any sound set using
50     * {@link Builder#setSound}
51     *
52     * <p>
53     * A notification that is noisy is more likely to be presented as a heads-up notification,
54     * on some platforms.
55     * </p>
56     *
57     * @see Builder#setDefaults
58     */
59    public static final int DEFAULT_SOUND = 1;
60
61    /**
62     * Use the default notification vibrate. This will ignore any vibrate set using
63     * {@link Builder#setVibrate}. Using phone vibration requires the
64     * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
65     *
66     * <p>
67     * A notification that vibrates is more likely to be presented as a heads-up notification,
68     * on some platforms.
69     * </p>
70     *
71     * @see Builder#setDefaults
72     */
73    public static final int DEFAULT_VIBRATE = 2;
74
75    /**
76     * Use the default notification lights. This will ignore the
77     * {@link #FLAG_SHOW_LIGHTS} bit, and values set with {@link Builder#setLights}.
78     *
79     * @see Builder#setDefaults
80     */
81    public static final int DEFAULT_LIGHTS = 4;
82
83    /**
84     * Use this constant as the value for audioStreamType to request that
85     * the default stream type for notifications be used.  Currently the
86     * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
87     */
88    public static final int STREAM_DEFAULT = -1;
89
90    /**
91     * Bit set in the Notification flags field when LEDs should be turned on
92     * for this notification.
93     */
94    public static final int FLAG_SHOW_LIGHTS        = 0x00000001;
95
96    /**
97     * Bit set in the Notification flags field if this notification is in
98     * reference to something that is ongoing, like a phone call.  It should
99     * not be set if this notification is in reference to something that
100     * happened at a particular point in time, like a missed phone call.
101     */
102    public static final int FLAG_ONGOING_EVENT      = 0x00000002;
103
104    /**
105     * Bit set in the Notification flags field if
106     * the audio will be repeated until the notification is
107     * cancelled or the notification window is opened.
108     */
109    public static final int FLAG_INSISTENT          = 0x00000004;
110
111    /**
112     * Bit set in the Notification flags field if the notification's sound,
113     * vibrate and ticker should only be played if the notification is not already showing.
114     */
115    public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;
116
117    /**
118     * Bit set in the Notification flags field if the notification should be canceled when
119     * it is clicked by the user.
120     */
121    public static final int FLAG_AUTO_CANCEL        = 0x00000010;
122
123    /**
124     * Bit set in the Notification flags field if the notification should not be canceled
125     * when the user clicks the Clear all button.
126     */
127    public static final int FLAG_NO_CLEAR           = 0x00000020;
128
129    /**
130     * Bit set in the Notification flags field if this notification represents a currently
131     * running service.  This will normally be set for you by
132     * {@link android.app.Service#startForeground}.
133     */
134    public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
135
136    /**
137     * Obsolete flag indicating high-priority notifications; use the priority field instead.
138     *
139     * @deprecated Use {@link NotificationCompat.Builder#setPriority(int)} with a positive value.
140     */
141    public static final int FLAG_HIGH_PRIORITY      = 0x00000080;
142
143    /**
144     * Bit set in the Notification flags field if this notification is relevant to the current
145     * device only and it is not recommended that it bridge to other devices.
146     */
147    public static final int FLAG_LOCAL_ONLY         = 0x00000100;
148
149    /**
150     * Bit set in the Notification flags field if this notification is the group summary for a
151     * group of notifications. Grouped notifications may display in a cluster or stack on devices
152     * which support such rendering. Requires a group key also be set using
153     * {@link Builder#setGroup}.
154     */
155    public static final int FLAG_GROUP_SUMMARY      = 0x00000200;
156
157    /**
158     * Default notification priority for {@link NotificationCompat.Builder#setPriority(int)}.
159     * If your application does not prioritize its own notifications,
160     * use this value for all notifications.
161     */
162    public static final int PRIORITY_DEFAULT = 0;
163
164    /**
165     * Lower notification priority for {@link NotificationCompat.Builder#setPriority(int)},
166     * for items that are less important. The UI may choose to show
167     * these items smaller, or at a different position in the list,
168     * compared with your app's {@link #PRIORITY_DEFAULT} items.
169     */
170    public static final int PRIORITY_LOW = -1;
171
172    /**
173     * Lowest notification priority for {@link NotificationCompat.Builder#setPriority(int)};
174     * these items might not be shown to the user except under
175     * special circumstances, such as detailed notification logs.
176     */
177    public static final int PRIORITY_MIN = -2;
178
179    /**
180     * Higher notification priority for {@link NotificationCompat.Builder#setPriority(int)},
181     * for more important notifications or alerts. The UI may choose
182     * to show these items larger, or at a different position in
183     * notification lists, compared with your app's {@link #PRIORITY_DEFAULT} items.
184     */
185    public static final int PRIORITY_HIGH = 1;
186
187    /**
188     * Highest notification priority for {@link NotificationCompat.Builder#setPriority(int)},
189     * for your application's most important items that require the user's
190     * prompt attention or input.
191     */
192    public static final int PRIORITY_MAX = 2;
193
194    /**
195     * Notification extras key: this is the title of the notification,
196     * as supplied to {@link Builder#setContentTitle(CharSequence)}.
197     */
198    public static final String EXTRA_TITLE = "android.title";
199
200    /**
201     * Notification extras key: this is the title of the notification when shown in expanded form,
202     * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
203     */
204    public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
205
206    /**
207     * Notification extras key: this is the main text payload, as supplied to
208     * {@link Builder#setContentText(CharSequence)}.
209     */
210    public static final String EXTRA_TEXT = "android.text";
211
212    /**
213     * Notification extras key: this is a third line of text, as supplied to
214     * {@link Builder#setSubText(CharSequence)}.
215     */
216    public static final String EXTRA_SUB_TEXT = "android.subText";
217
218    /**
219     * Notification extras key: this is a small piece of additional text as supplied to
220     * {@link Builder#setContentInfo(CharSequence)}.
221     */
222    public static final String EXTRA_INFO_TEXT = "android.infoText";
223
224    /**
225     * Notification extras key: this is a line of summary information intended to be shown
226     * alongside expanded notifications, as supplied to (e.g.)
227     * {@link BigTextStyle#setSummaryText(CharSequence)}.
228     */
229    public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
230
231    /**
232     * Notification extras key: this is the resource ID of the notification's main small icon, as
233     * supplied to {@link Builder#setSmallIcon(int)}.
234     */
235    public static final String EXTRA_SMALL_ICON = "android.icon";
236
237    /**
238     * Notification extras key: this is a bitmap to be used instead of the small icon when showing the
239     * notification payload, as
240     * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
241     */
242    public static final String EXTRA_LARGE_ICON = "android.largeIcon";
243
244    /**
245     * Notification extras key: this is a bitmap to be used instead of the one from
246     * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
247     * shown in its expanded form, as supplied to
248     * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
249     */
250    public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
251
252    /**
253     * Notification extras key: this is the progress value supplied to
254     * {@link Builder#setProgress(int, int, boolean)}.
255     */
256    public static final String EXTRA_PROGRESS = "android.progress";
257
258    /**
259     * Notification extras key: this is the maximum value supplied to
260     * {@link Builder#setProgress(int, int, boolean)}.
261     */
262    public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
263
264    /**
265     * Notification extras key: whether the progress bar is indeterminate, supplied to
266     * {@link Builder#setProgress(int, int, boolean)}.
267     */
268    public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
269
270    /**
271     * Notification extras key: whether the when field set using {@link Builder#setWhen} should
272     * be shown as a count-up timer (specifically a {@link android.widget.Chronometer}) instead
273     * of a timestamp, as supplied to {@link Builder#setUsesChronometer(boolean)}.
274     */
275    public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
276
277    /**
278     * Notification extras key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
279     * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
280     */
281    public static final String EXTRA_PICTURE = "android.picture";
282
283    /**
284     * Notification extras key: An array of CharSequences to show in {@link InboxStyle} expanded
285     * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
286     */
287    public static final String EXTRA_TEXT_LINES = "android.textLines";
288
289    /**
290     * Notification extras key: An array of people that this notification relates to, specified
291     * by contacts provider contact URI.
292     */
293    public static final String EXTRA_PEOPLE = "android.people";
294
295    /**
296     * Value of {@link Notification#color} equal to 0 (also known as
297     * {@link android.graphics.Color#TRANSPARENT Color.TRANSPARENT}),
298     * telling the system not to decorate this notification with any special color but instead use
299     * default colors when presenting this notification.
300     */
301    public static final int COLOR_DEFAULT = Color.TRANSPARENT;
302
303    /**
304     * Notification visibility: Show this notification in its entirety on all lockscreens.
305     *
306     * {@see android.app.Notification#visibility}
307     */
308    public static final int VISIBILITY_PUBLIC = 1;
309
310    /**
311     * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
312     * private information on secure lockscreens.
313     *
314     * {@see android.app.Notification#visibility}
315     */
316    public static final int VISIBILITY_PRIVATE = 0;
317
318    /**
319     * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
320     *
321     * {@see android.app.Notification#visibility}
322     */
323    public static final int VISIBILITY_SECRET = -1;
324
325    private static final NotificationCompatImpl IMPL;
326
327    interface NotificationCompatImpl {
328        public Notification build(Builder b);
329        public Bundle getExtras(Notification n);
330        public int getActionCount(Notification n);
331        public Action getAction(Notification n, int actionIndex);
332        public Action[] getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables);
333        public ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions);
334        public boolean getLocalOnly(Notification n);
335        public String getGroup(Notification n);
336        public boolean isGroupSummary(Notification n);
337        public String getSortKey(Notification n);
338    }
339
340    static class NotificationCompatImplBase implements NotificationCompatImpl {
341        @Override
342        public Notification build(Builder b) {
343            Notification result = b.mNotification;
344            result.setLatestEventInfo(b.mContext, b.mContentTitle,
345                    b.mContentText, b.mContentIntent);
346            // translate high priority requests into legacy flag
347            if (b.mPriority > PRIORITY_DEFAULT) {
348                result.flags |= FLAG_HIGH_PRIORITY;
349            }
350            return result;
351        }
352
353        @Override
354        public Bundle getExtras(Notification n) {
355            return null;
356        }
357
358        @Override
359        public int getActionCount(Notification n) {
360            return 0;
361        }
362
363        @Override
364        public Action getAction(Notification n, int actionIndex) {
365            return null;
366        }
367
368        @Override
369        public Action[] getActionsFromParcelableArrayList(
370                ArrayList<Parcelable> parcelables) {
371            return null;
372        }
373
374        @Override
375        public ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions) {
376            return null;
377        }
378
379        @Override
380        public boolean getLocalOnly(Notification n) {
381            return false;
382        }
383
384        @Override
385        public String getGroup(Notification n) {
386            return null;
387        }
388
389        @Override
390        public boolean isGroupSummary(Notification n) {
391            return false;
392        }
393
394        @Override
395        public String getSortKey(Notification n) {
396            return null;
397        }
398    }
399
400    static class NotificationCompatImplGingerbread extends NotificationCompatImplBase {
401        @Override
402        public Notification build(Builder b) {
403            Notification result = b.mNotification;
404            result.setLatestEventInfo(b.mContext, b.mContentTitle,
405                    b.mContentText, b.mContentIntent);
406            result = NotificationCompatGingerbread.add(result, b.mContext,
407                    b.mContentTitle, b.mContentText, b.mContentIntent, b.mFullScreenIntent);
408            // translate high priority requests into legacy flag
409            if (b.mPriority > PRIORITY_DEFAULT) {
410                result.flags |= FLAG_HIGH_PRIORITY;
411            }
412            return result;
413        }
414    }
415
416    static class NotificationCompatImplHoneycomb extends NotificationCompatImplBase {
417        @Override
418        public Notification build(Builder b) {
419            return NotificationCompatHoneycomb.add(b.mContext, b.mNotification,
420                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
421                    b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon);
422        }
423    }
424
425    static class NotificationCompatImplIceCreamSandwich extends NotificationCompatImplBase {
426        @Override
427        public Notification build(Builder b) {
428            return NotificationCompatIceCreamSandwich.add(b.mContext, b.mNotification,
429                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
430                    b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
431                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate);
432        }
433    }
434
435    static class NotificationCompatImplJellybean extends NotificationCompatImplBase {
436        @Override
437        public Notification build(Builder b) {
438            NotificationCompatJellybean.Builder builder = new NotificationCompatJellybean.Builder(
439                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
440                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
441                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate,
442                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mExtras,
443                    b.mGroupKey, b.mGroupSummary, b.mSortKey);
444            addActionsToBuilder(builder, b.mActions);
445            addStyleToBuilderJellybean(builder, b.mStyle);
446            return builder.build();
447        }
448
449        @Override
450        public Bundle getExtras(Notification n) {
451            return NotificationCompatJellybean.getExtras(n);
452        }
453
454        @Override
455        public int getActionCount(Notification n) {
456            return NotificationCompatJellybean.getActionCount(n);
457        }
458
459        @Override
460        public Action getAction(Notification n, int actionIndex) {
461            return (Action) NotificationCompatJellybean.getAction(n, actionIndex, Action.FACTORY,
462                    RemoteInput.FACTORY);
463        }
464
465        @Override
466        public Action[] getActionsFromParcelableArrayList(
467                ArrayList<Parcelable> parcelables) {
468            return (Action[]) NotificationCompatJellybean.getActionsFromParcelableArrayList(
469                    parcelables, Action.FACTORY, RemoteInput.FACTORY);
470        }
471
472        @Override
473        public ArrayList<Parcelable> getParcelableArrayListForActions(
474                Action[] actions) {
475            return NotificationCompatJellybean.getParcelableArrayListForActions(actions);
476        }
477
478        @Override
479        public boolean getLocalOnly(Notification n) {
480            return NotificationCompatJellybean.getLocalOnly(n);
481        }
482
483        @Override
484        public String getGroup(Notification n) {
485            return NotificationCompatJellybean.getGroup(n);
486        }
487
488        @Override
489        public boolean isGroupSummary(Notification n) {
490            return NotificationCompatJellybean.isGroupSummary(n);
491        }
492
493        @Override
494        public String getSortKey(Notification n) {
495            return NotificationCompatJellybean.getSortKey(n);
496        }
497    }
498
499    static class NotificationCompatImplKitKat extends NotificationCompatImplJellybean {
500        @Override
501        public Notification build(Builder b) {
502            NotificationCompatKitKat.Builder builder = new NotificationCompatKitKat.Builder(
503                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
504                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
505                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate,
506                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mPeople, b.mExtras,
507                    b.mGroupKey, b.mGroupSummary, b.mSortKey);
508            addActionsToBuilder(builder, b.mActions);
509            addStyleToBuilderJellybean(builder, b.mStyle);
510            return builder.build();
511        }
512
513        @Override
514        public Bundle getExtras(Notification n) {
515            return NotificationCompatKitKat.getExtras(n);
516        }
517
518        @Override
519        public int getActionCount(Notification n) {
520            return NotificationCompatKitKat.getActionCount(n);
521        }
522
523        @Override
524        public Action getAction(Notification n, int actionIndex) {
525            return (Action) NotificationCompatKitKat.getAction(n, actionIndex, Action.FACTORY,
526                    RemoteInput.FACTORY);
527        }
528
529        @Override
530        public boolean getLocalOnly(Notification n) {
531            return NotificationCompatKitKat.getLocalOnly(n);
532        }
533
534        @Override
535        public String getGroup(Notification n) {
536            return NotificationCompatKitKat.getGroup(n);
537        }
538
539        @Override
540        public boolean isGroupSummary(Notification n) {
541            return NotificationCompatKitKat.isGroupSummary(n);
542        }
543
544        @Override
545        public String getSortKey(Notification n) {
546            return NotificationCompatKitKat.getSortKey(n);
547        }
548    }
549
550    static class NotificationCompatImplApi20 extends NotificationCompatImplKitKat {
551        @Override
552        public Notification build(Builder b) {
553            NotificationCompatApi20.Builder builder = new NotificationCompatApi20.Builder(
554                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
555                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
556                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate,
557                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory,
558                    b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion,
559                    b.mGroupKey, b.mGroupSummary, b.mSortKey);
560            addActionsToBuilder(builder, b.mActions);
561            addStyleToBuilderJellybean(builder, b.mStyle);
562            return builder.build();
563        }
564
565        @Override
566        public Action getAction(Notification n, int actionIndex) {
567            return (Action) NotificationCompatApi20.getAction(n, actionIndex, Action.FACTORY,
568                    RemoteInput.FACTORY);
569        }
570
571        @Override
572        public Action[] getActionsFromParcelableArrayList(
573                ArrayList<Parcelable> parcelables) {
574            return (Action[]) NotificationCompatApi20.getActionsFromParcelableArrayList(
575                    parcelables, Action.FACTORY, RemoteInput.FACTORY);
576        }
577
578        @Override
579        public ArrayList<Parcelable> getParcelableArrayListForActions(
580                Action[] actions) {
581            return NotificationCompatApi20.getParcelableArrayListForActions(actions);
582        }
583
584        @Override
585        public boolean getLocalOnly(Notification n) {
586            return NotificationCompatApi20.getLocalOnly(n);
587        }
588
589        @Override
590        public String getGroup(Notification n) {
591            return NotificationCompatApi20.getGroup(n);
592        }
593
594        @Override
595        public boolean isGroupSummary(Notification n) {
596            return NotificationCompatApi20.isGroupSummary(n);
597        }
598
599        @Override
600        public String getSortKey(Notification n) {
601            return NotificationCompatApi20.getSortKey(n);
602        }
603    }
604
605    private static void addActionsToBuilder(NotificationBuilderWithActions builder,
606            ArrayList<Action> actions) {
607        for (Action action : actions) {
608            builder.addAction(action);
609        }
610    }
611
612    private static void addStyleToBuilderJellybean(NotificationBuilderWithBuilderAccessor builder,
613            Style style) {
614        if (style != null) {
615            if (style instanceof BigTextStyle) {
616                BigTextStyle bigTextStyle = (BigTextStyle) style;
617                NotificationCompatJellybean.addBigTextStyle(builder,
618                        bigTextStyle.mBigContentTitle,
619                        bigTextStyle.mSummaryTextSet,
620                        bigTextStyle.mSummaryText,
621                        bigTextStyle.mBigText);
622            } else if (style instanceof InboxStyle) {
623                InboxStyle inboxStyle = (InboxStyle) style;
624                NotificationCompatJellybean.addInboxStyle(builder,
625                        inboxStyle.mBigContentTitle,
626                        inboxStyle.mSummaryTextSet,
627                        inboxStyle.mSummaryText,
628                        inboxStyle.mTexts);
629            } else if (style instanceof BigPictureStyle) {
630                BigPictureStyle bigPictureStyle = (BigPictureStyle) style;
631                NotificationCompatJellybean.addBigPictureStyle(builder,
632                        bigPictureStyle.mBigContentTitle,
633                        bigPictureStyle.mSummaryTextSet,
634                        bigPictureStyle.mSummaryText,
635                        bigPictureStyle.mPicture,
636                        bigPictureStyle.mBigLargeIcon,
637                        bigPictureStyle.mBigLargeIconSet);
638            }
639        }
640    }
641
642    static {
643        // TODO: Replace this if clause when SDK_INT is incremented to 20.
644        if (Build.VERSION.RELEASE.equals("L")) {
645            IMPL = new NotificationCompatImplApi20();
646        } else if (Build.VERSION.SDK_INT >= 19) {
647            IMPL = new NotificationCompatImplKitKat();
648        } else if (Build.VERSION.SDK_INT >= 16) {
649            IMPL = new NotificationCompatImplJellybean();
650        } else if (Build.VERSION.SDK_INT >= 14) {
651            IMPL = new NotificationCompatImplIceCreamSandwich();
652        } else if (Build.VERSION.SDK_INT >= 11) {
653            IMPL = new NotificationCompatImplHoneycomb();
654        } else if (Build.VERSION.SDK_INT >= 9) {
655            IMPL = new NotificationCompatImplGingerbread();
656        } else {
657            IMPL = new NotificationCompatImplBase();
658        }
659    }
660
661    /**
662     * Builder class for {@link NotificationCompat} objects.  Allows easier control over
663     * all the flags, as well as help constructing the typical notification layouts.
664     * <p>
665     * On platform versions that don't offer expanded notifications, methods that depend on
666     * expanded notifications have no effect.
667     * </p>
668     * <p>
669     * For example, action buttons won't appear on platforms prior to Android 4.1. Action
670     * buttons depend on expanded notifications, which are only available in Android 4.1
671     * and later.
672     * <p>
673     * For this reason, you should always ensure that UI controls in a notification are also
674     * available in an {@link android.app.Activity} in your app, and you should always start that
675     * {@link android.app.Activity} when users click the notification. To do this, use the
676     * {@link NotificationCompat.Builder#setContentIntent setContentIntent()}
677     * method.
678     * </p>
679     *
680     */
681    public static class Builder {
682        Context mContext;
683
684        CharSequence mContentTitle;
685        CharSequence mContentText;
686        PendingIntent mContentIntent;
687        PendingIntent mFullScreenIntent;
688        RemoteViews mTickerView;
689        Bitmap mLargeIcon;
690        CharSequence mContentInfo;
691        int mNumber;
692        int mPriority;
693        boolean mUseChronometer;
694        Style mStyle;
695        CharSequence mSubText;
696        int mProgressMax;
697        int mProgress;
698        boolean mProgressIndeterminate;
699        String mGroupKey;
700        boolean mGroupSummary;
701        String mSortKey;
702        ArrayList<Action> mActions = new ArrayList<Action>();
703        boolean mLocalOnly = false;
704        String mCategory;
705        Bundle mExtras;
706        int mColor = COLOR_DEFAULT;
707        int mVisibility = VISIBILITY_PRIVATE;
708        Notification mPublicVersion;
709
710        Notification mNotification = new Notification();
711        public ArrayList<String> mPeople;
712
713        /**
714         * Constructor.
715         *
716         * Automatically sets the when field to {@link System#currentTimeMillis()
717         * System.currentTimeMillis()} and the audio stream to the
718         * {@link Notification#STREAM_DEFAULT}.
719         *
720         * @param context A {@link Context} that will be used to construct the
721         *      RemoteViews. The Context will not be held past the lifetime of this
722         *      Builder object.
723         */
724        public Builder(Context context) {
725            mContext = context;
726
727            // Set defaults to match the defaults of a Notification
728            mNotification.when = System.currentTimeMillis();
729            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
730            mPriority = PRIORITY_DEFAULT;
731            mPeople = new ArrayList<String>();
732        }
733
734        /**
735         * Set the time that the event occurred.  Notifications in the panel are
736         * sorted by this time.
737         */
738        public Builder setWhen(long when) {
739            mNotification.when = when;
740            return this;
741        }
742
743        /**
744         * Show the {@link Notification#when} field as a stopwatch.
745         *
746         * Instead of presenting <code>when</code> as a timestamp, the notification will show an
747         * automatically updating display of the minutes and seconds since <code>when</code>.
748         *
749         * Useful when showing an elapsed time (like an ongoing phone call).
750         *
751         * @see android.widget.Chronometer
752         * @see Notification#when
753         */
754        public Builder setUsesChronometer(boolean b) {
755            mUseChronometer = b;
756            return this;
757        }
758
759        /**
760         * Set the small icon to use in the notification layouts.  Different classes of devices
761         * may return different sizes.  See the UX guidelines for more information on how to
762         * design these icons.
763         *
764         * @param icon A resource ID in the application's package of the drawble to use.
765         */
766        public Builder setSmallIcon(int icon) {
767            mNotification.icon = icon;
768            return this;
769        }
770
771        /**
772         * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
773         * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
774         * LevelListDrawable}.
775         *
776         * @param icon A resource ID in the application's package of the drawble to use.
777         * @param level The level to use for the icon.
778         *
779         * @see android.graphics.drawable.LevelListDrawable
780         */
781        public Builder setSmallIcon(int icon, int level) {
782            mNotification.icon = icon;
783            mNotification.iconLevel = level;
784            return this;
785        }
786
787        /**
788         * Set the title (first row) of the notification, in a standard notification.
789         */
790        public Builder setContentTitle(CharSequence title) {
791            mContentTitle = title;
792            return this;
793        }
794
795        /**
796         * Set the text (second row) of the notification, in a standard notification.
797         */
798        public Builder setContentText(CharSequence text) {
799            mContentText = text;
800            return this;
801        }
802
803        /**
804         * Set the third line of text in the platform notification template.
805         * Don't use if you're also using {@link #setProgress(int, int, boolean)};
806         * they occupy the same location in the standard template.
807         * <br>
808         * If the platform does not provide large-format notifications, this method has no effect.
809         * The third line of text only appears in expanded view.
810         * <br>
811         */
812        public Builder setSubText(CharSequence text) {
813            mSubText = text;
814            return this;
815        }
816
817        /**
818         * Set the large number at the right-hand side of the notification.  This is
819         * equivalent to setContentInfo, although it might show the number in a different
820         * font size for readability.
821         */
822        public Builder setNumber(int number) {
823            mNumber = number;
824            return this;
825        }
826
827        /**
828         * Set the large text at the right-hand side of the notification.
829         */
830        public Builder setContentInfo(CharSequence info) {
831            mContentInfo = info;
832            return this;
833        }
834
835        /**
836         * Set the progress this notification represents, which may be
837         * represented as a {@link android.widget.ProgressBar}.
838         */
839        public Builder setProgress(int max, int progress, boolean indeterminate) {
840            mProgressMax = max;
841            mProgress = progress;
842            mProgressIndeterminate = indeterminate;
843            return this;
844        }
845
846        /**
847         * Supply a custom RemoteViews to use instead of the standard one.
848         */
849        public Builder setContent(RemoteViews views) {
850            mNotification.contentView = views;
851            return this;
852        }
853
854        /**
855         * Supply a {@link PendingIntent} to send when the notification is clicked.
856         * If you do not supply an intent, you can now add PendingIntents to individual
857         * views to be launched when clicked by calling {@link RemoteViews#setOnClickPendingIntent
858         * RemoteViews.setOnClickPendingIntent(int,PendingIntent)}.  Be sure to
859         * read {@link Notification#contentIntent Notification.contentIntent} for
860         * how to correctly use this.
861         */
862        public Builder setContentIntent(PendingIntent intent) {
863            mContentIntent = intent;
864            return this;
865        }
866
867        /**
868         * Supply a {@link PendingIntent} to send when the notification is cleared by the user
869         * directly from the notification panel.  For example, this intent is sent when the user
870         * clicks the "Clear all" button, or the individual "X" buttons on notifications.  This
871         * intent is not sent when the application calls
872         * {@link android.app.NotificationManager#cancel NotificationManager.cancel(int)}.
873         */
874        public Builder setDeleteIntent(PendingIntent intent) {
875            mNotification.deleteIntent = intent;
876            return this;
877        }
878
879        /**
880         * An intent to launch instead of posting the notification to the status bar.
881         * Only for use with extremely high-priority notifications demanding the user's
882         * <strong>immediate</strong> attention, such as an incoming phone call or
883         * alarm clock that the user has explicitly set to a particular time.
884         * If this facility is used for something else, please give the user an option
885         * to turn it off and use a normal notification, as this can be extremely
886         * disruptive.
887         *
888         * <p>
889         * On some platforms, the system UI may choose to display a heads-up notification,
890         * instead of launching this intent, while the user is using the device.
891         * </p>
892         *
893         * @param intent The pending intent to launch.
894         * @param highPriority Passing true will cause this notification to be sent
895         *          even if other notifications are suppressed.
896         */
897        public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
898            mFullScreenIntent = intent;
899            setFlag(FLAG_HIGH_PRIORITY, highPriority);
900            return this;
901        }
902
903        /**
904         * Set the text that is displayed in the status bar when the notification first
905         * arrives.
906         */
907        public Builder setTicker(CharSequence tickerText) {
908            mNotification.tickerText = tickerText;
909            return this;
910        }
911
912        /**
913         * Set the text that is displayed in the status bar when the notification first
914         * arrives, and also a RemoteViews object that may be displayed instead on some
915         * devices.
916         */
917        public Builder setTicker(CharSequence tickerText, RemoteViews views) {
918            mNotification.tickerText = tickerText;
919            mTickerView = views;
920            return this;
921        }
922
923        /**
924         * Set the large icon that is shown in the ticker and notification.
925         */
926        public Builder setLargeIcon(Bitmap icon) {
927            mLargeIcon = icon;
928            return this;
929        }
930
931        /**
932         * Set the sound to play.  It will play on the default stream.
933         *
934         * <p>
935         * On some platforms, a notification that is noisy is more likely to be presented
936         * as a heads-up notification.
937         * </p>
938         */
939        public Builder setSound(Uri sound) {
940            mNotification.sound = sound;
941            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
942            return this;
943        }
944
945        /**
946         * Set the sound to play.  It will play on the stream you supply.
947         *
948         * <p>
949         * On some platforms, a notification that is noisy is more likely to be presented
950         * as a heads-up notification.
951         * </p>
952         *
953         * @see Notification#STREAM_DEFAULT
954         * @see AudioManager for the <code>STREAM_</code> constants.
955         */
956        public Builder setSound(Uri sound, int streamType) {
957            mNotification.sound = sound;
958            mNotification.audioStreamType = streamType;
959            return this;
960        }
961
962        /**
963         * Set the vibration pattern to use.
964         *
965         * <p>
966         * On some platforms, a notification that vibrates is more likely to be presented
967         * as a heads-up notification.
968         * </p>
969         *
970         * @see android.os.Vibrator for a discussion of the <code>pattern</code>
971         * parameter.
972         */
973        public Builder setVibrate(long[] pattern) {
974            mNotification.vibrate = pattern;
975            return this;
976        }
977
978        /**
979         * Set the argb value that you would like the LED on the device to blnk, as well as the
980         * rate.  The rate is specified in terms of the number of milliseconds to be on
981         * and then the number of milliseconds to be off.
982         */
983        public Builder setLights(int argb, int onMs, int offMs) {
984            mNotification.ledARGB = argb;
985            mNotification.ledOnMS = onMs;
986            mNotification.ledOffMS = offMs;
987            boolean showLights = mNotification.ledOnMS != 0 && mNotification.ledOffMS != 0;
988            mNotification.flags = (mNotification.flags & ~Notification.FLAG_SHOW_LIGHTS) |
989                    (showLights ? Notification.FLAG_SHOW_LIGHTS : 0);
990            return this;
991        }
992
993        /**
994         * Set whether this is an ongoing notification.
995         *
996         * <p>Ongoing notifications differ from regular notifications in the following ways:
997         * <ul>
998         *   <li>Ongoing notifications are sorted above the regular notifications in the
999         *   notification panel.</li>
1000         *   <li>Ongoing notifications do not have an 'X' close button, and are not affected
1001         *   by the "Clear all" button.
1002         * </ul>
1003         */
1004        public Builder setOngoing(boolean ongoing) {
1005            setFlag(Notification.FLAG_ONGOING_EVENT, ongoing);
1006            return this;
1007        }
1008
1009        /**
1010         * Set this flag if you would only like the sound, vibrate
1011         * and ticker to be played if the notification is not already showing.
1012         */
1013        public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
1014            setFlag(Notification.FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
1015            return this;
1016        }
1017
1018        /**
1019         * Setting this flag will make it so the notification is automatically
1020         * canceled when the user clicks it in the panel.  The PendingIntent
1021         * set with {@link #setDeleteIntent} will be broadcast when the notification
1022         * is canceled.
1023         */
1024        public Builder setAutoCancel(boolean autoCancel) {
1025            setFlag(Notification.FLAG_AUTO_CANCEL, autoCancel);
1026            return this;
1027        }
1028
1029        /**
1030         * Set whether or not this notification is only relevant to the current device.
1031         *
1032         * <p>Some notifications can be bridged to other devices for remote display.
1033         * This hint can be set to recommend this notification not be bridged.
1034         */
1035        public Builder setLocalOnly(boolean b) {
1036            mLocalOnly = b;
1037            return this;
1038        }
1039
1040        /**
1041         * Set the notification category.
1042         *
1043         * <p>Must be one of the predefined notification categories (see the <code>CATEGORY_*</code>
1044         * constants in {@link Notification}) that best describes this notification.
1045         * May be used by the system for ranking and filtering.
1046         */
1047        public Builder setCategory(String category) {
1048            mCategory = category;
1049            return this;
1050        }
1051
1052        /**
1053         * Set the default notification options that will be used.
1054         * <p>
1055         * The value should be one or more of the following fields combined with
1056         * bitwise-or:
1057         * {@link Notification#DEFAULT_SOUND}, {@link Notification#DEFAULT_VIBRATE},
1058         * {@link Notification#DEFAULT_LIGHTS}.
1059         * <p>
1060         * For all default values, use {@link Notification#DEFAULT_ALL}.
1061         */
1062        public Builder setDefaults(int defaults) {
1063            mNotification.defaults = defaults;
1064            if ((defaults & Notification.DEFAULT_LIGHTS) != 0) {
1065                mNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
1066            }
1067            return this;
1068        }
1069
1070        private void setFlag(int mask, boolean value) {
1071            if (value) {
1072                mNotification.flags |= mask;
1073            } else {
1074                mNotification.flags &= ~mask;
1075            }
1076        }
1077
1078        /**
1079         * Set the relative priority for this notification.
1080         *
1081         * Priority is an indication of how much of the user's
1082         * valuable attention should be consumed by this
1083         * notification. Low-priority notifications may be hidden from
1084         * the user in certain situations, while the user might be
1085         * interrupted for a higher-priority notification.
1086         * The system sets a notification's priority based on various factors including the
1087         * setPriority value. The effect may differ slightly on different platforms.
1088         *
1089         * @param pri Relative priority for this notification. Must be one of
1090         *     the priority constants defined by {@link NotificationCompat}.
1091         *     Acceptable values range from {@link
1092         *     NotificationCompat#PRIORITY_MIN} (-2) to {@link
1093         *     NotificationCompat#PRIORITY_MAX} (2).
1094         */
1095        public Builder setPriority(int pri) {
1096            mPriority = pri;
1097            return this;
1098        }
1099
1100        /**
1101         * Add a person that is relevant to this notification.
1102         *
1103         * @see Notification#EXTRA_PEOPLE
1104         */
1105        public Builder addPerson(String handle) {
1106            mPeople.add(handle);
1107            return this;
1108        }
1109
1110        /**
1111         * Set this notification to be part of a group of notifications sharing the same key.
1112         * Grouped notifications may display in a cluster or stack on devices which
1113         * support such rendering.
1114         *
1115         * <p>To make this notification the summary for its group, also call
1116         * {@link #setGroupSummary}. A sort order can be specified for group members by using
1117         * {@link #setSortKey}.
1118         * @param groupKey The group key of the group.
1119         * @return this object for method chaining
1120         */
1121        public Builder setGroup(String groupKey) {
1122            mGroupKey = groupKey;
1123            return this;
1124        }
1125
1126        /**
1127         * Set this notification to be the group summary for a group of notifications.
1128         * Grouped notifications may display in a cluster or stack on devices which
1129         * support such rendering. Requires a group key also be set using {@link #setGroup}.
1130         * @param isGroupSummary Whether this notification should be a group summary.
1131         * @return this object for method chaining
1132         */
1133        public Builder setGroupSummary(boolean isGroupSummary) {
1134            mGroupSummary = isGroupSummary;
1135            return this;
1136        }
1137
1138        /**
1139         * Set a sort key that orders this notification among other notifications from the
1140         * same package. This can be useful if an external sort was already applied and an app
1141         * would like to preserve this. Notifications will be sorted lexicographically using this
1142         * value, although providing different priorities in addition to providing sort key may
1143         * cause this value to be ignored.
1144         *
1145         * <p>This sort key can also be used to order members of a notification group. See
1146         * {@link Builder#setGroup}.
1147         *
1148         * @see String#compareTo(String)
1149         */
1150        public Builder setSortKey(String sortKey) {
1151            mSortKey = sortKey;
1152            return this;
1153        }
1154
1155        /**
1156         * Merge additional metadata into this notification.
1157         *
1158         * <p>Values within the Bundle will replace existing extras values in this Builder.
1159         *
1160         * @see Notification#extras
1161         */
1162        public Builder addExtras(Bundle extras) {
1163            if (extras != null) {
1164                if (mExtras == null) {
1165                    mExtras = new Bundle(extras);
1166                } else {
1167                    mExtras.putAll(extras);
1168                }
1169            }
1170            return this;
1171        }
1172
1173        /**
1174         * Set metadata for this notification.
1175         *
1176         * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
1177         * current contents are copied into the Notification each time {@link #build()} is
1178         * called.
1179         *
1180         * <p>Replaces any existing extras values with those from the provided Bundle.
1181         * Use {@link #addExtras} to merge in metadata instead.
1182         *
1183         * @see Notification#extras
1184         */
1185        public Builder setExtras(Bundle extras) {
1186            mExtras = extras;
1187            return this;
1188        }
1189
1190        /**
1191         * Get the current metadata Bundle used by this notification Builder.
1192         *
1193         * <p>The returned Bundle is shared with this Builder.
1194         *
1195         * <p>The current contents of this Bundle are copied into the Notification each time
1196         * {@link #build()} is called.
1197         *
1198         * @see Notification#extras
1199         */
1200        public Bundle getExtras() {
1201            if (mExtras == null) {
1202                mExtras = new Bundle();
1203            }
1204            return mExtras;
1205        }
1206
1207        /**
1208         * Add an action to this notification. Actions are typically displayed by
1209         * the system as a button adjacent to the notification content.
1210         * <br>
1211         * Action buttons won't appear on platforms prior to Android 4.1. Action
1212         * buttons depend on expanded notifications, which are only available in Android 4.1
1213         * and later. To ensure that an action button's functionality is always available, first
1214         * implement the functionality in the {@link android.app.Activity} that starts when a user
1215         * clicks the  notification (see {@link #setContentIntent setContentIntent()}), and then
1216         * enhance the notification by implementing the same functionality with
1217         * {@link #addAction addAction()}.
1218         *
1219         * @param icon Resource ID of a drawable that represents the action.
1220         * @param title Text describing the action.
1221         * @param intent {@link android.app.PendingIntent} to be fired when the action is invoked.
1222         */
1223        public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
1224            mActions.add(new Action(icon, title, intent));
1225            return this;
1226        }
1227
1228        /**
1229         * Add an action to this notification. Actions are typically displayed by
1230         * the system as a button adjacent to the notification content.
1231         * <br>
1232         * Action buttons won't appear on platforms prior to Android 4.1. Action
1233         * buttons depend on expanded notifications, which are only available in Android 4.1
1234         * and later. To ensure that an action button's functionality is always available, first
1235         * implement the functionality in the {@link android.app.Activity} that starts when a user
1236         * clicks the  notification (see {@link #setContentIntent setContentIntent()}), and then
1237         * enhance the notification by implementing the same functionality with
1238         * {@link #addAction addAction()}.
1239         *
1240         * @param action The action to add.
1241         */
1242        public Builder addAction(Action action) {
1243            mActions.add(action);
1244            return this;
1245        }
1246
1247        /**
1248         * Add a rich notification style to be applied at build time.
1249         * <br>
1250         * If the platform does not provide rich notification styles, this method has no effect. The
1251         * user will always see the normal notification style.
1252         *
1253         * @param style Object responsible for modifying the notification style.
1254         */
1255        public Builder setStyle(Style style) {
1256            if (mStyle != style) {
1257                mStyle = style;
1258                if (mStyle != null) {
1259                    mStyle.setBuilder(this);
1260                }
1261            }
1262            return this;
1263        }
1264
1265        /**
1266         * Sets {@link Notification#color}.
1267         *
1268         * @param argb The accent color to use
1269         *
1270         * @return The same Builder.
1271         */
1272        public Builder setColor(int argb) {
1273            mColor = argb;
1274            return this;
1275        }
1276
1277        /**
1278         * Sets {@link Notification#visibility}.
1279         *
1280         * @param visibility One of {@link Notification#VISIBILITY_PRIVATE} (the default),
1281         *                   {@link Notification#VISIBILITY_PUBLIC}, or
1282         *                   {@link Notification#VISIBILITY_SECRET}.
1283         */
1284        public Builder setVisibility(int visibility) {
1285            mVisibility = visibility;
1286            return this;
1287        }
1288
1289        /**
1290         * Supply a replacement Notification whose contents should be shown in insecure contexts
1291         * (i.e. atop the secure lockscreen). See {@link Notification#visibility} and
1292         * {@link #VISIBILITY_PUBLIC}.
1293         *
1294         * @param n A replacement notification, presumably with some or all info redacted.
1295         * @return The same Builder.
1296         */
1297        public Builder setPublicVersion(Notification n) {
1298            mPublicVersion = n;
1299            return this;
1300        }
1301
1302        /**
1303         * Apply an extender to this notification builder. Extenders may be used to add
1304         * metadata or change options on this builder.
1305         */
1306        public Builder extend(Extender extender) {
1307            extender.extend(this);
1308            return this;
1309        }
1310
1311        /**
1312         * @deprecated Use {@link #build()} instead.
1313         */
1314        @Deprecated
1315        public Notification getNotification() {
1316            return IMPL.build(this);
1317        }
1318
1319        /**
1320         * Combine all of the options that have been set and return a new {@link Notification}
1321         * object.
1322         */
1323        public Notification build() {
1324            return IMPL.build(this);
1325        }
1326    }
1327
1328    /**
1329     * An object that can apply a rich notification style to a {@link Notification.Builder}
1330     * object.
1331     * <br>
1332     * If the platform does not provide rich notification styles, methods in this class have no
1333     * effect.
1334     */
1335    public static abstract class Style {
1336        Builder mBuilder;
1337        CharSequence mBigContentTitle;
1338        CharSequence mSummaryText;
1339        boolean mSummaryTextSet = false;
1340
1341        public void setBuilder(Builder builder) {
1342            if (mBuilder != builder) {
1343                mBuilder = builder;
1344                if (mBuilder != null) {
1345                    mBuilder.setStyle(this);
1346                }
1347            }
1348        }
1349
1350        public Notification build() {
1351            Notification notification = null;
1352            if (mBuilder != null) {
1353                notification = mBuilder.build();
1354            }
1355            return notification;
1356        }
1357    }
1358
1359    /**
1360     * Helper class for generating large-format notifications that include a large image attachment.
1361     * <br>
1362     * If the platform does not provide large-format notifications, this method has no effect. The
1363     * user will always see the normal notification view.
1364     * <br>
1365     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1366     * <pre class="prettyprint">
1367     * Notification notif = new Notification.Builder(mContext)
1368     *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
1369     *     .setContentText(subject)
1370     *     .setSmallIcon(R.drawable.new_post)
1371     *     .setLargeIcon(aBitmap)
1372     *     .setStyle(new Notification.BigPictureStyle()
1373     *         .bigPicture(aBigBitmap))
1374     *     .build();
1375     * </pre>
1376     *
1377     * @see Notification#bigContentView
1378     */
1379    public static class BigPictureStyle extends Style {
1380        Bitmap mPicture;
1381        Bitmap mBigLargeIcon;
1382        boolean mBigLargeIconSet;
1383
1384        public BigPictureStyle() {
1385        }
1386
1387        public BigPictureStyle(Builder builder) {
1388            setBuilder(builder);
1389        }
1390
1391        /**
1392         * Overrides ContentTitle in the big form of the template.
1393         * This defaults to the value passed to setContentTitle().
1394         */
1395        public BigPictureStyle setBigContentTitle(CharSequence title) {
1396            mBigContentTitle = title;
1397            return this;
1398        }
1399
1400        /**
1401         * Set the first line of text after the detail section in the big form of the template.
1402         */
1403        public BigPictureStyle setSummaryText(CharSequence cs) {
1404            mSummaryText = cs;
1405            mSummaryTextSet = true;
1406            return this;
1407        }
1408
1409        /**
1410         * Provide the bitmap to be used as the payload for the BigPicture notification.
1411         */
1412        public BigPictureStyle bigPicture(Bitmap b) {
1413            mPicture = b;
1414            return this;
1415        }
1416
1417        /**
1418         * Override the large icon when the big notification is shown.
1419         */
1420        public BigPictureStyle bigLargeIcon(Bitmap b) {
1421            mBigLargeIcon = b;
1422            mBigLargeIconSet = true;
1423            return this;
1424        }
1425    }
1426
1427    /**
1428     * Helper class for generating large-format notifications that include a lot of text.
1429     *
1430     * <br>
1431     * If the platform does not provide large-format notifications, this method has no effect. The
1432     * user will always see the normal notification view.
1433     * <br>
1434     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1435     * <pre class="prettyprint">
1436     * Notification notif = new Notification.Builder(mContext)
1437     *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
1438     *     .setContentText(subject)
1439     *     .setSmallIcon(R.drawable.new_mail)
1440     *     .setLargeIcon(aBitmap)
1441     *     .setStyle(new Notification.BigTextStyle()
1442     *         .bigText(aVeryLongString))
1443     *     .build();
1444     * </pre>
1445     *
1446     * @see Notification#bigContentView
1447     */
1448    public static class BigTextStyle extends Style {
1449        CharSequence mBigText;
1450
1451        public BigTextStyle() {
1452        }
1453
1454        public BigTextStyle(Builder builder) {
1455            setBuilder(builder);
1456        }
1457
1458        /**
1459         * Overrides ContentTitle in the big form of the template.
1460         * This defaults to the value passed to setContentTitle().
1461         */
1462        public BigTextStyle setBigContentTitle(CharSequence title) {
1463            mBigContentTitle = title;
1464            return this;
1465        }
1466
1467        /**
1468         * Set the first line of text after the detail section in the big form of the template.
1469         */
1470        public BigTextStyle setSummaryText(CharSequence cs) {
1471            mSummaryText = cs;
1472            mSummaryTextSet = true;
1473            return this;
1474        }
1475
1476        /**
1477         * Provide the longer text to be displayed in the big form of the
1478         * template in place of the content text.
1479         */
1480        public BigTextStyle bigText(CharSequence cs) {
1481            mBigText = cs;
1482            return this;
1483        }
1484    }
1485
1486    /**
1487     * Helper class for generating large-format notifications that include a list of (up to 5) strings.
1488     *
1489     * <br>
1490     * If the platform does not provide large-format notifications, this method has no effect. The
1491     * user will always see the normal notification view.
1492     * <br>
1493     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1494     * <pre class="prettyprint">
1495     * Notification noti = new Notification.Builder()
1496     *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
1497     *     .setContentText(subject)
1498     *     .setSmallIcon(R.drawable.new_mail)
1499     *     .setLargeIcon(aBitmap)
1500     *     .setStyle(new Notification.InboxStyle()
1501     *         .addLine(str1)
1502     *         .addLine(str2)
1503     *         .setContentTitle(&quot;&quot;)
1504     *         .setSummaryText(&quot;+3 more&quot;))
1505     *     .build();
1506     * </pre>
1507     *
1508     * @see Notification#bigContentView
1509     */
1510    public static class InboxStyle extends Style {
1511        ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
1512
1513        public InboxStyle() {
1514        }
1515
1516        public InboxStyle(Builder builder) {
1517            setBuilder(builder);
1518        }
1519
1520        /**
1521         * Overrides ContentTitle in the big form of the template.
1522         * This defaults to the value passed to setContentTitle().
1523         */
1524        public InboxStyle setBigContentTitle(CharSequence title) {
1525            mBigContentTitle = title;
1526            return this;
1527        }
1528
1529        /**
1530         * Set the first line of text after the detail section in the big form of the template.
1531         */
1532        public InboxStyle setSummaryText(CharSequence cs) {
1533            mSummaryText = cs;
1534            mSummaryTextSet = true;
1535            return this;
1536        }
1537
1538        /**
1539         * Append a line to the digest section of the Inbox notification.
1540         */
1541        public InboxStyle addLine(CharSequence cs) {
1542            mTexts.add(cs);
1543            return this;
1544        }
1545    }
1546
1547    /**
1548     * Structure to encapsulate a named action that can be shown as part of this notification.
1549     * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1550     * selected by the user. Action buttons won't appear on platforms prior to Android 4.1.
1551     * <p>
1552     * Apps should use {@link NotificationCompat.Builder#addAction(int, CharSequence, PendingIntent)}
1553     * or {@link NotificationCompat.Builder#addAction(NotificationCompat.Action)}
1554     * to attach actions.
1555     */
1556    public static class Action extends NotificationCompatBase.Action {
1557        private final Bundle mExtras;
1558        private final RemoteInput[] mRemoteInputs;
1559
1560        /**
1561         * Small icon representing the action.
1562         */
1563        public int icon;
1564        /**
1565         * Title of the action.
1566         */
1567        public CharSequence title;
1568        /**
1569         * Intent to send when the user invokes this action. May be null, in which case the action
1570         * may be rendered in a disabled presentation.
1571         */
1572        public PendingIntent actionIntent;
1573
1574        public Action(int icon, CharSequence title, PendingIntent intent) {
1575            this(icon, title, intent, new Bundle(), null);
1576        }
1577
1578        private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
1579                RemoteInput[] remoteInputs) {
1580            this.icon = icon;
1581            this.title = title;
1582            this.actionIntent = intent;
1583            this.mExtras = extras != null ? extras : new Bundle();
1584            this.mRemoteInputs = remoteInputs;
1585        }
1586
1587        @Override
1588        protected int getIcon() {
1589            return icon;
1590        }
1591
1592        @Override
1593        protected CharSequence getTitle() {
1594            return title;
1595        }
1596
1597        @Override
1598        protected PendingIntent getActionIntent() {
1599            return actionIntent;
1600        }
1601
1602        /**
1603         * Get additional metadata carried around with this Action.
1604         */
1605        public Bundle getExtras() {
1606            return mExtras;
1607        }
1608
1609        /**
1610         * Get the list of inputs to be collected from the user when this action is sent.
1611         * May return null if no remote inputs were added.
1612         */
1613        public RemoteInput[] getRemoteInputs() {
1614            return mRemoteInputs;
1615        }
1616
1617        /**
1618         * Builder class for {@link Action} objects.
1619         */
1620        public static final class Builder {
1621            private final int mIcon;
1622            private final CharSequence mTitle;
1623            private final PendingIntent mIntent;
1624            private final Bundle mExtras;
1625            private ArrayList<RemoteInput> mRemoteInputs;
1626
1627            /**
1628             * Construct a new builder for {@link Action} object.
1629             * @param icon icon to show for this action
1630             * @param title the title of the action
1631             * @param intent the {@link PendingIntent} to fire when users trigger this action
1632             */
1633            public Builder(int icon, CharSequence title, PendingIntent intent) {
1634                this(icon, title, intent, new Bundle());
1635            }
1636
1637            /**
1638             * Construct a new builder for {@link Action} object using the fields from an
1639             * {@link Action}.
1640             * @param action the action to read fields from.
1641             */
1642            public Builder(Action action) {
1643                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras));
1644            }
1645
1646            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
1647                mIcon = icon;
1648                mTitle = title;
1649                mIntent = intent;
1650                mExtras = extras;
1651            }
1652
1653            /**
1654             * Merge additional metadata into this builder.
1655             *
1656             * <p>Values within the Bundle will replace existing extras values in this Builder.
1657             *
1658             * @see NotificationCompat.Action#getExtras
1659             */
1660            public Builder addExtras(Bundle extras) {
1661                if (extras != null) {
1662                    mExtras.putAll(extras);
1663                }
1664                return this;
1665            }
1666
1667            /**
1668             * Get the metadata Bundle used by this Builder.
1669             *
1670             * <p>The returned Bundle is shared with this Builder.
1671             */
1672            public Bundle getExtras() {
1673                return mExtras;
1674            }
1675
1676            /**
1677             * Add an input to be collected from the user when this action is sent.
1678             * Response values can be retrieved from the fired intent by using the
1679             * {@link RemoteInput#getResultsFromIntent} function.
1680             * @param remoteInput a {@link RemoteInput} to add to the action
1681             * @return this object for method chaining
1682             */
1683            public Builder addRemoteInput(RemoteInput remoteInput) {
1684                if (mRemoteInputs == null) {
1685                    mRemoteInputs = new ArrayList<RemoteInput>();
1686                }
1687                mRemoteInputs.add(remoteInput);
1688                return this;
1689            }
1690
1691            /**
1692             * Apply an extender to this action builder. Extenders may be used to add
1693             * metadata or change options on this builder.
1694             */
1695            public Builder extend(Extender extender) {
1696                extender.extend(this);
1697                return this;
1698            }
1699
1700            /**
1701             * Combine all of the options that have been set and return a new {@link Action}
1702             * object.
1703             * @return the built action
1704             */
1705            public Action build() {
1706                RemoteInput[] remoteInputs = mRemoteInputs != null
1707                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1708                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
1709            }
1710        }
1711
1712
1713        /**
1714         * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1715         * metadata or change options on an action builder.
1716         */
1717        public interface Extender {
1718            /**
1719             * Apply this extender to a notification action builder.
1720             * @param builder the builder to be modified.
1721             * @return the build object for chaining.
1722             */
1723            public Builder extend(Builder builder);
1724        }
1725
1726        /**
1727         * Wearable extender for notification actions. To add extensions to an action,
1728         * create a new {@link NotificationCompat.Action.WearableExtender} object using
1729         * the {@code WearableExtender()} constructor and apply it to a
1730         * {@link NotificationCompat.Action.Builder} using
1731         * {@link NotificationCompat.Action.Builder#extend}.
1732         *
1733         * <pre class="prettyprint">
1734         * NotificationCompat.Action action = new NotificationCompat.Action.Builder(
1735         *         R.drawable.archive_all, "Archive all", actionIntent)
1736         *         .extend(new NotificationCompat.Action.WearableExtender()
1737         *                 .setAvailableOffline(false))
1738         *         .build();</pre>
1739         */
1740        public static final class WearableExtender implements Extender {
1741            /** Notification action extra which contains wearable extensions */
1742            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1743
1744            private static final String KEY_FLAGS = "flags";
1745
1746            // Flags bitwise-ored to mFlags
1747            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1748
1749            // Default value for flags integer
1750            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1751
1752            private int mFlags = DEFAULT_FLAGS;
1753
1754            /**
1755             * Create a {@link NotificationCompat.Action.WearableExtender} with default
1756             * options.
1757             */
1758            public WearableExtender() {
1759            }
1760
1761            /**
1762             * Create a {@link NotificationCompat.Action.WearableExtender} by reading
1763             * wearable options present in an existing notification action.
1764             * @param action the notification action to inspect.
1765             */
1766            public WearableExtender(Action action) {
1767                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1768                if (wearableBundle != null) {
1769                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1770                }
1771            }
1772
1773            /**
1774             * Apply wearable extensions to a notification action that is being built. This is
1775             * typically called by the {@link NotificationCompat.Action.Builder#extend}
1776             * method of {@link NotificationCompat.Action.Builder}.
1777             */
1778            @Override
1779            public Action.Builder extend(Action.Builder builder) {
1780                Bundle wearableBundle = new Bundle();
1781
1782                if (mFlags != DEFAULT_FLAGS) {
1783                    wearableBundle.putInt(KEY_FLAGS, mFlags);
1784                }
1785
1786                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1787                return builder;
1788            }
1789
1790            @Override
1791            public WearableExtender clone() {
1792                WearableExtender that = new WearableExtender();
1793                that.mFlags = this.mFlags;
1794                return that;
1795            }
1796
1797            /**
1798             * Set whether this action is available when the wearable device is not connected to
1799             * a companion device. The user can still trigger this action when the wearable device
1800             * is offline, but a visual hint will indicate that the action may not be available.
1801             * Defaults to true.
1802             */
1803            public WearableExtender setAvailableOffline(boolean availableOffline) {
1804                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1805                return this;
1806            }
1807
1808            /**
1809             * Get whether this action is available when the wearable device is not connected to
1810             * a companion device. The user can still trigger this action when the wearable device
1811             * is offline, but a visual hint will indicate that the action may not be available.
1812             * Defaults to true.
1813             */
1814            public boolean isAvailableOffline() {
1815                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1816            }
1817
1818            private void setFlag(int mask, boolean value) {
1819                if (value) {
1820                    mFlags |= mask;
1821                } else {
1822                    mFlags &= ~mask;
1823                }
1824            }
1825        }
1826
1827        /** @hide */
1828        public static final Factory FACTORY = new Factory() {
1829            @Override
1830            public Action build(int icon, CharSequence title,
1831                    PendingIntent actionIntent, Bundle extras,
1832                    RemoteInputCompatBase.RemoteInput[] remoteInputs) {
1833                return new Action(icon, title, actionIntent, extras,
1834                        (RemoteInput[]) remoteInputs);
1835            }
1836
1837            @Override
1838            public Action[] newArray(int length) {
1839                return new Action[length];
1840            }
1841        };
1842    }
1843
1844
1845    /**
1846     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1847     * metadata or change options on a notification builder.
1848     */
1849    public interface Extender {
1850        /**
1851         * Apply this extender to a notification builder.
1852         * @param builder the builder to be modified.
1853         * @return the build object for chaining.
1854         */
1855        public Builder extend(Builder builder);
1856    }
1857
1858    /**
1859     * Helper class to add wearable extensions to notifications.
1860     * <p class="note"> See
1861     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
1862     * for Android Wear</a> for more information on how to use this class.
1863     * <p>
1864     * To create a notification with wearable extensions:
1865     * <ol>
1866     *   <li>Create a {@link NotificationCompat.Builder}, setting any desired
1867     *   properties.
1868     *   <li>Create a {@link NotificationCompat.WearableExtender}.
1869     *   <li>Set wearable-specific properties using the
1870     *   {@code add} and {@code set} methods of {@link NotificationCompat.WearableExtender}.
1871     *   <li>Call {@link NotificationCompat.Builder#extend} to apply the extensions to a
1872     *   notification.
1873     *   <li>Post the notification to the notification
1874     *   system with the {@code NotificationManagerCompat.notify(...)} methods
1875     *   and not the {@code NotificationManager.notify(...)} methods.
1876     * </ol>
1877     *
1878     * <pre class="prettyprint">
1879     * Notification notif = new NotificationCompat.Builder(mContext)
1880     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
1881     *         .setContentText(subject)
1882     *         .setSmallIcon(R.drawable.new_mail)
1883     *         .extend(new NotificationCompat.WearableExtender()
1884     *                 .setContentIcon(R.drawable.new_mail))
1885     *         .build();
1886     * NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
1887     *
1888     * <p>Wearable extensions can be accessed on an existing notification by using the
1889     * {@code WearableExtender(Notification)} constructor,
1890     * and then using the {@code get} methods to access values.
1891     *
1892     * <pre class="prettyprint">
1893     * NotificationCompat.WearableExtender wearableExtender =
1894     *         new NotificationCompat.WearableExtender(notification);
1895     * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
1896     */
1897    public static final class WearableExtender implements Extender {
1898        /**
1899         * Sentinel value for an action index that is unset.
1900         */
1901        public static final int UNSET_ACTION_INDEX = -1;
1902
1903        /**
1904         * Size value for use with {@link #setCustomSizePreset} to show this notification with
1905         * default sizing.
1906         * <p>For custom display notifications created using {@link #setDisplayIntent},
1907         * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
1908         * on their content.
1909         */
1910        public static final int SIZE_DEFAULT = 0;
1911
1912        /**
1913         * Size value for use with {@link #setCustomSizePreset} to show this notification
1914         * with an extra small size.
1915         * <p>This value is only applicable for custom display notifications created using
1916         * {@link #setDisplayIntent}.
1917         */
1918        public static final int SIZE_XSMALL = 1;
1919
1920        /**
1921         * Size value for use with {@link #setCustomSizePreset} to show this notification
1922         * with a small size.
1923         * <p>This value is only applicable for custom display notifications created using
1924         * {@link #setDisplayIntent}.
1925         */
1926        public static final int SIZE_SMALL = 2;
1927
1928        /**
1929         * Size value for use with {@link #setCustomSizePreset} to show this notification
1930         * with a medium size.
1931         * <p>This value is only applicable for custom display notifications created using
1932         * {@link #setDisplayIntent}.
1933         */
1934        public static final int SIZE_MEDIUM = 3;
1935
1936        /**
1937         * Size value for use with {@link #setCustomSizePreset} to show this notification
1938         * with a large size.
1939         * <p>This value is only applicable for custom display notifications created using
1940         * {@link #setDisplayIntent}.
1941         */
1942        public static final int SIZE_LARGE = 4;
1943
1944        /**
1945         * Size value for use with {@link #setCustomSizePreset} to show this notification
1946         * full screen.
1947         * <p>This value is only applicable for custom display notifications created using
1948         * {@link #setDisplayIntent}.
1949         */
1950        public static final int SIZE_FULL_SCREEN = 5;
1951
1952        /** Notification extra which contains wearable extensions */
1953        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1954
1955        // Keys within EXTRA_WEARABLE_OPTIONS for wearable options.
1956        private static final String KEY_ACTIONS = "actions";
1957        private static final String KEY_FLAGS = "flags";
1958        private static final String KEY_DISPLAY_INTENT = "displayIntent";
1959        private static final String KEY_PAGES = "pages";
1960        private static final String KEY_BACKGROUND = "background";
1961        private static final String KEY_CONTENT_ICON = "contentIcon";
1962        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
1963        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
1964        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
1965        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
1966        private static final String KEY_GRAVITY = "gravity";
1967
1968        // Flags bitwise-ored to mFlags
1969        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
1970        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
1971        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
1972        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
1973
1974        // Default value for flags integer
1975        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
1976
1977        private static final int DEFAULT_CONTENT_ICON_GRAVITY = GravityCompat.END;
1978        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
1979
1980        private ArrayList<Action> mActions = new ArrayList<Action>();
1981        private int mFlags = DEFAULT_FLAGS;
1982        private PendingIntent mDisplayIntent;
1983        private ArrayList<Notification> mPages = new ArrayList<Notification>();
1984        private Bitmap mBackground;
1985        private int mContentIcon;
1986        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
1987        private int mContentActionIndex = UNSET_ACTION_INDEX;
1988        private int mCustomSizePreset = SIZE_DEFAULT;
1989        private int mCustomContentHeight;
1990        private int mGravity = DEFAULT_GRAVITY;
1991
1992        /**
1993         * Create a {@link NotificationCompat.WearableExtender} with default
1994         * options.
1995         */
1996        public WearableExtender() {
1997        }
1998
1999        public WearableExtender(Notification notif) {
2000            Bundle extras = getExtras(notif);
2001            Bundle wearableBundle = extras != null ? extras.getBundle(EXTRA_WEARABLE_EXTENSIONS)
2002                    : null;
2003            if (wearableBundle != null) {
2004                Action[] actions = IMPL.getActionsFromParcelableArrayList(
2005                        wearableBundle.getParcelableArrayList(KEY_ACTIONS));
2006                if (actions != null) {
2007                    Collections.addAll(mActions, actions);
2008                }
2009
2010                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
2011                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
2012
2013                Notification[] pages = getNotificationArrayFromBundle(
2014                        wearableBundle, KEY_PAGES);
2015                if (pages != null) {
2016                    Collections.addAll(mPages, pages);
2017                }
2018
2019                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
2020                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
2021                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
2022                        DEFAULT_CONTENT_ICON_GRAVITY);
2023                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
2024                        UNSET_ACTION_INDEX);
2025                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
2026                        SIZE_DEFAULT);
2027                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
2028                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
2029            }
2030        }
2031
2032        /**
2033         * Apply wearable extensions to a notification that is being built. This is typically
2034         * called by the {@link NotificationCompat.Builder#extend} method of
2035         * {@link NotificationCompat.Builder}.
2036         */
2037        @Override
2038        public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
2039            Bundle wearableBundle = new Bundle();
2040
2041            if (!mActions.isEmpty()) {
2042                wearableBundle.putParcelableArrayList(KEY_ACTIONS,
2043                        IMPL.getParcelableArrayListForActions(mActions.toArray(
2044                                new Action[mActions.size()])));
2045            }
2046            if (mFlags != DEFAULT_FLAGS) {
2047                wearableBundle.putInt(KEY_FLAGS, mFlags);
2048            }
2049            if (mDisplayIntent != null) {
2050                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
2051            }
2052            if (!mPages.isEmpty()) {
2053                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
2054                        new Notification[mPages.size()]));
2055            }
2056            if (mBackground != null) {
2057                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
2058            }
2059            if (mContentIcon != 0) {
2060                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
2061            }
2062            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
2063                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
2064            }
2065            if (mContentActionIndex != UNSET_ACTION_INDEX) {
2066                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
2067                        mContentActionIndex);
2068            }
2069            if (mCustomSizePreset != SIZE_DEFAULT) {
2070                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
2071            }
2072            if (mCustomContentHeight != 0) {
2073                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
2074            }
2075            if (mGravity != DEFAULT_GRAVITY) {
2076                wearableBundle.putInt(KEY_GRAVITY, mGravity);
2077            }
2078
2079            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
2080            return builder;
2081        }
2082
2083        @Override
2084        public WearableExtender clone() {
2085            WearableExtender that = new WearableExtender();
2086            that.mActions = new ArrayList<Action>(this.mActions);
2087            that.mFlags = this.mFlags;
2088            that.mDisplayIntent = this.mDisplayIntent;
2089            that.mPages = new ArrayList<Notification>(this.mPages);
2090            that.mBackground = this.mBackground;
2091            that.mContentIcon = this.mContentIcon;
2092            that.mContentIconGravity = this.mContentIconGravity;
2093            that.mContentActionIndex = this.mContentActionIndex;
2094            that.mCustomSizePreset = this.mCustomSizePreset;
2095            that.mCustomContentHeight = this.mCustomContentHeight;
2096            that.mGravity = this.mGravity;
2097            return that;
2098        }
2099
2100        /**
2101         * Add a wearable action to this notification.
2102         *
2103         * <p>When wearable actions are added using this method, the set of actions that
2104         * show on a wearable device splits from devices that only show actions added
2105         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
2106         * of which actions display on different devices.
2107         *
2108         * @param action the action to add to this notification
2109         * @return this object for method chaining
2110         * @see NotificationCompat.Action
2111         */
2112        public WearableExtender addAction(Action action) {
2113            mActions.add(action);
2114            return this;
2115        }
2116
2117        /**
2118         * Adds wearable actions to this notification.
2119         *
2120         * <p>When wearable actions are added using this method, the set of actions that
2121         * show on a wearable device splits from devices that only show actions added
2122         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
2123         * of which actions display on different devices.
2124         *
2125         * @param actions the actions to add to this notification
2126         * @return this object for method chaining
2127         * @see NotificationCompat.Action
2128         */
2129        public WearableExtender addActions(List<Action> actions) {
2130            mActions.addAll(actions);
2131            return this;
2132        }
2133
2134        /**
2135         * Clear all wearable actions present on this builder.
2136         * @return this object for method chaining.
2137         * @see #addAction
2138         */
2139        public WearableExtender clearActions() {
2140            mActions.clear();
2141            return this;
2142        }
2143
2144        /**
2145         * Get the wearable actions present on this notification.
2146         */
2147        public List<Action> getActions() {
2148            return mActions;
2149        }
2150
2151        /**
2152         * Set an intent to launch inside of an activity view when displaying
2153         * this notification. The {@link PendingIntent} provided should be for an activity.
2154         *
2155         * <pre class="prettyprint">
2156         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
2157         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
2158         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
2159         * Notification notif = new NotificationCompat.Builder(context)
2160         *         .extend(new NotificationCompat.WearableExtender()
2161         *                 .setDisplayIntent(displayPendingIntent)
2162         *                 .setCustomSizePreset(NotificationCompat.WearableExtender.SIZE_MEDIUM))
2163         *         .build();</pre>
2164         *
2165         * <p>The activity to launch needs to allow embedding, must be exported, and
2166         * should have an empty task affinity. It is also recommended to use the device
2167         * default light theme.
2168         *
2169         * <p>Example AndroidManifest.xml entry:
2170         * <pre class="prettyprint">
2171         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
2172         *     android:exported=&quot;true&quot;
2173         *     android:allowEmbedded=&quot;true&quot;
2174         *     android:taskAffinity=&quot;&quot;
2175         *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
2176         *
2177         * @param intent the {@link PendingIntent} for an activity
2178         * @return this object for method chaining
2179         * @see NotificationCompat.WearableExtender#getDisplayIntent
2180         */
2181        public WearableExtender setDisplayIntent(PendingIntent intent) {
2182            mDisplayIntent = intent;
2183            return this;
2184        }
2185
2186        /**
2187         * Get the intent to launch inside of an activity view when displaying this
2188         * notification. This {@code PendingIntent} should be for an activity.
2189         */
2190        public PendingIntent getDisplayIntent() {
2191            return mDisplayIntent;
2192        }
2193
2194        /**
2195         * Add an additional page of content to display with this notification. The current
2196         * notification forms the first page, and pages added using this function form
2197         * subsequent pages. This field can be used to separate a notification into multiple
2198         * sections.
2199         *
2200         * @param page the notification to add as another page
2201         * @return this object for method chaining
2202         * @see NotificationCompat.WearableExtender#getPages
2203         */
2204        public WearableExtender addPage(Notification page) {
2205            mPages.add(page);
2206            return this;
2207        }
2208
2209        /**
2210         * Add additional pages of content to display with this notification. The current
2211         * notification forms the first page, and pages added using this function form
2212         * subsequent pages. This field can be used to separate a notification into multiple
2213         * sections.
2214         *
2215         * @param pages a list of notifications
2216         * @return this object for method chaining
2217         * @see NotificationCompat.WearableExtender#getPages
2218         */
2219        public WearableExtender addPages(List<Notification> pages) {
2220            mPages.addAll(pages);
2221            return this;
2222        }
2223
2224        /**
2225         * Clear all additional pages present on this builder.
2226         * @return this object for method chaining.
2227         * @see #addPage
2228         */
2229        public WearableExtender clearPages() {
2230            mPages.clear();
2231            return this;
2232        }
2233
2234        /**
2235         * Get the array of additional pages of content for displaying this notification. The
2236         * current notification forms the first page, and elements within this array form
2237         * subsequent pages. This field can be used to separate a notification into multiple
2238         * sections.
2239         * @return the pages for this notification
2240         */
2241        public List<Notification> getPages() {
2242            return mPages;
2243        }
2244
2245        /**
2246         * Set a background image to be displayed behind the notification content.
2247         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
2248         * will work with any notification style.
2249         *
2250         * @param background the background bitmap
2251         * @return this object for method chaining
2252         * @see NotificationCompat.WearableExtender#getBackground
2253         */
2254        public WearableExtender setBackground(Bitmap background) {
2255            mBackground = background;
2256            return this;
2257        }
2258
2259        /**
2260         * Get a background image to be displayed behind the notification content.
2261         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
2262         * will work with any notification style.
2263         *
2264         * @return the background image
2265         * @see NotificationCompat.WearableExtender#setBackground
2266         */
2267        public Bitmap getBackground() {
2268            return mBackground;
2269        }
2270
2271        /**
2272         * Set an icon that goes with the content of this notification.
2273         */
2274        public WearableExtender setContentIcon(int icon) {
2275            mContentIcon = icon;
2276            return this;
2277        }
2278
2279        /**
2280         * Get an icon that goes with the content of this notification.
2281         */
2282        public int getContentIcon() {
2283            return mContentIcon;
2284        }
2285
2286        /**
2287         * Set the gravity that the content icon should have within the notification display.
2288         * Supported values include {@link android.view.Gravity#START} and
2289         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
2290         * @see #setContentIcon
2291         */
2292        public WearableExtender setContentIconGravity(int contentIconGravity) {
2293            mContentIconGravity = contentIconGravity;
2294            return this;
2295        }
2296
2297        /**
2298         * Get the gravity that the content icon should have within the notification display.
2299         * Supported values include {@link android.view.Gravity#START} and
2300         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
2301         * @see #getContentIcon
2302         */
2303        public int getContentIconGravity() {
2304            return mContentIconGravity;
2305        }
2306
2307        /**
2308         * Set an action from this notification's actions to be clickable with the content of
2309         * this notification. This action will no longer display separately from the
2310         * notification's content.
2311         *
2312         * <p>For notifications with multiple pages, child pages can also have content actions
2313         * set, although the list of available actions comes from the main notification and not
2314         * from the child page's notification.
2315         *
2316         * @param actionIndex The index of the action to hoist onto the current notification page.
2317         *                    If wearable actions were added to the main notification, this index
2318         *                    will apply to that list, otherwise it will apply to the regular
2319         *                    actions list.
2320         */
2321        public WearableExtender setContentAction(int actionIndex) {
2322            mContentActionIndex = actionIndex;
2323            return this;
2324        }
2325
2326        /**
2327         * Get the index of the notification action, if any, that was specified as being clickable
2328         * with the content of this notification. This action will no longer display separately
2329         * from the notification's content.
2330         *
2331         * <p>For notifications with multiple pages, child pages can also have content actions
2332         * set, although the list of available actions comes from the main notification and not
2333         * from the child page's notification.
2334         *
2335         * <p>If wearable specific actions were added to the main notification, this index will
2336         * apply to that list, otherwise it will apply to the regular actions list.
2337         *
2338         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
2339         */
2340        public int getContentAction() {
2341            return mContentActionIndex;
2342        }
2343
2344        /**
2345         * Set the gravity that this notification should have within the available viewport space.
2346         * Supported values include {@link android.view.Gravity#TOP},
2347         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
2348         * The default value is {@link android.view.Gravity#BOTTOM}.
2349         */
2350        public WearableExtender setGravity(int gravity) {
2351            mGravity = gravity;
2352            return this;
2353        }
2354
2355        /**
2356         * Get the gravity that this notification should have within the available viewport space.
2357         * Supported values include {@link android.view.Gravity#TOP},
2358         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
2359         * The default value is {@link android.view.Gravity#BOTTOM}.
2360         */
2361        public int getGravity() {
2362            return mGravity;
2363        }
2364
2365        /**
2366         * Set the custom size preset for the display of this notification out of the available
2367         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
2368         * {@link #SIZE_LARGE}.
2369         * <p>Some custom size presets are only applicable for custom display notifications created
2370         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. Check the
2371         * documentation for the preset in question. See also
2372         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
2373         */
2374        public WearableExtender setCustomSizePreset(int sizePreset) {
2375            mCustomSizePreset = sizePreset;
2376            return this;
2377        }
2378
2379        /**
2380         * Get the custom size preset for the display of this notification out of the available
2381         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
2382         * {@link #SIZE_LARGE}.
2383         * <p>Some custom size presets are only applicable for custom display notifications created
2384         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
2385         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
2386         */
2387        public int getCustomSizePreset() {
2388            return mCustomSizePreset;
2389        }
2390
2391        /**
2392         * Set the custom height in pixels for the display of this notification's content.
2393         * <p>This option is only available for custom display notifications created
2394         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. See also
2395         * {@link NotificationCompat.WearableExtender#setCustomSizePreset} and
2396         * {@link #getCustomContentHeight}.
2397         */
2398        public WearableExtender setCustomContentHeight(int height) {
2399            mCustomContentHeight = height;
2400            return this;
2401        }
2402
2403        /**
2404         * Get the custom height in pixels for the display of this notification's content.
2405         * <p>This option is only available for custom display notifications created
2406         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
2407         * {@link #setCustomContentHeight}.
2408         */
2409        public int getCustomContentHeight() {
2410            return mCustomContentHeight;
2411        }
2412
2413        /**
2414         * Set whether the scrolling position for the contents of this notification should start
2415         * at the bottom of the contents instead of the top when the contents are too long to
2416         * display within the screen.  Default is false (start scroll at the top).
2417         */
2418        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
2419            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
2420            return this;
2421        }
2422
2423        /**
2424         * Get whether the scrolling position for the contents of this notification should start
2425         * at the bottom of the contents instead of the top when the contents are too long to
2426         * display within the screen. Default is false (start scroll at the top).
2427         */
2428        public boolean getStartScrollBottom() {
2429            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
2430        }
2431
2432        /**
2433         * Set whether the content intent is available when the wearable device is not connected
2434         * to a companion device.  The user can still trigger this intent when the wearable device
2435         * is offline, but a visual hint will indicate that the content intent may not be available.
2436         * Defaults to true.
2437         */
2438        public WearableExtender setContentIntentAvailableOffline(
2439                boolean contentIntentAvailableOffline) {
2440            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
2441            return this;
2442        }
2443
2444        /**
2445         * Get whether the content intent is available when the wearable device is not connected
2446         * to a companion device.  The user can still trigger this intent when the wearable device
2447         * is offline, but a visual hint will indicate that the content intent may not be available.
2448         * Defaults to true.
2449         */
2450        public boolean getContentIntentAvailableOffline() {
2451            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
2452        }
2453
2454        /**
2455         * Set a hint that this notification's icon should not be displayed.
2456         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
2457         * @return this object for method chaining
2458         */
2459        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
2460            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
2461            return this;
2462        }
2463
2464        /**
2465         * Get a hint that this notification's icon should not be displayed.
2466         * @return {@code true} if this icon should not be displayed, false otherwise.
2467         * The default value is {@code false} if this was never set.
2468         */
2469        public boolean getHintHideIcon() {
2470            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
2471        }
2472
2473        /**
2474         * Set a visual hint that only the background image of this notification should be
2475         * displayed, and other semantic content should be hidden. This hint is only applicable
2476         * to sub-pages added using {@link #addPage}.
2477         */
2478        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
2479            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
2480            return this;
2481        }
2482
2483        /**
2484         * Get a visual hint that only the background image of this notification should be
2485         * displayed, and other semantic content should be hidden. This hint is only applicable
2486         * to sub-pages added using {@link NotificationCompat.WearableExtender#addPage}.
2487         */
2488        public boolean getHintShowBackgroundOnly() {
2489            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
2490        }
2491
2492        private void setFlag(int mask, boolean value) {
2493            if (value) {
2494                mFlags |= mask;
2495            } else {
2496                mFlags &= ~mask;
2497            }
2498        }
2499    }
2500
2501    /**
2502     * Get an array of Notification objects from a parcelable array bundle field.
2503     * Update the bundle to have a typed array so fetches in the future don't need
2504     * to do an array copy.
2505     */
2506    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
2507        Parcelable[] array = bundle.getParcelableArray(key);
2508        if (array instanceof Notification[] || array == null) {
2509            return (Notification[]) array;
2510        }
2511        Notification[] typedArray = new Notification[array.length];
2512        for (int i = 0; i < array.length; i++) {
2513            typedArray[i] = (Notification) array[i];
2514        }
2515        bundle.putParcelableArray(key, typedArray);
2516        return typedArray;
2517    }
2518
2519    /**
2520     * Gets the {@link Notification#extras} field from a notification in a backwards
2521     * compatible manner. Extras field was supported from JellyBean (Api level 16)
2522     * forwards. This function will return null on older api levels.
2523     */
2524    public static Bundle getExtras(Notification notif) {
2525        return IMPL.getExtras(notif);
2526    }
2527
2528    /**
2529     * Get the number of actions in this notification in a backwards compatible
2530     * manner. Actions were supported from JellyBean (Api level 16) forwards.
2531     */
2532    public static int getActionCount(Notification notif) {
2533        return IMPL.getActionCount(notif);
2534    }
2535
2536    /**
2537     * Get an action on this notification in a backwards compatible
2538     * manner. Actions were supported from JellyBean (Api level 16) forwards.
2539     * @param notif The notification to inspect.
2540     * @param actionIndex The index of the action to retrieve.
2541     */
2542    public static Action getAction(Notification notif, int actionIndex) {
2543        return IMPL.getAction(notif, actionIndex);
2544    }
2545
2546    /**
2547     * Get whether or not this notification is only relevant to the current device.
2548     *
2549     * <p>Some notifications can be bridged to other devices for remote display.
2550     * If this hint is set, it is recommend that this notification not be bridged.
2551     */
2552    public static boolean getLocalOnly(Notification notif) {
2553        return IMPL.getLocalOnly(notif);
2554    }
2555
2556    /**
2557     * Get the key used to group this notification into a cluster or stack
2558     * with other notifications on devices which support such rendering.
2559     */
2560    public static String getGroup(Notification notif) {
2561        return IMPL.getGroup(notif);
2562    }
2563
2564    /**
2565     * Get whether this notification to be the group summary for a group of notifications.
2566     * Grouped notifications may display in a cluster or stack on devices which
2567     * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
2568     * @return Whether this notification is a group summary.
2569     */
2570    public static boolean isGroupSummary(Notification notif) {
2571        return IMPL.isGroupSummary(notif);
2572    }
2573
2574    /**
2575     * Get a sort key that orders this notification among other notifications from the
2576     * same package. This can be useful if an external sort was already applied and an app
2577     * would like to preserve this. Notifications will be sorted lexicographically using this
2578     * value, although providing different priorities in addition to providing sort key may
2579     * cause this value to be ignored.
2580     *
2581     * <p>This sort key can also be used to order members of a notification group. See
2582     * {@link Builder#setGroup}.
2583     *
2584     * @see String#compareTo(String)
2585     */
2586    public static String getSortKey(Notification notif) {
2587        return IMPL.getSortKey(notif);
2588    }
2589}
2590