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