NotificationCompat.java revision d3ac32d9b20ef2f2c2630030957ff84489529dd3
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         *         .apply(new NotificationCompat.Action.WearableExtender()
1707         *                 .setAvailableOffline(false))
1708         *         .build();
1709         * </pre>
1710         */
1711        public static final class WearableExtender implements Extender {
1712            /** Notification action extra which contains wearable extensions */
1713            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1714
1715            private static final String KEY_FLAGS = "flags";
1716
1717            // Flags bitwise-ored to mFlags
1718            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1719
1720            // Default value for flags integer
1721            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1722
1723            private int mFlags = DEFAULT_FLAGS;
1724
1725            /**
1726             * Create a {@link NotificationCompat.Action.WearableExtender} with default
1727             * options.
1728             */
1729            public WearableExtender() {
1730            }
1731
1732            /**
1733             * Create a {@link NotificationCompat.Action.WearableExtender} by reading
1734             * wearable options present in an existing notification action.
1735             * @param action the notification action to inspect.
1736             */
1737            public WearableExtender(Action action) {
1738                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1739                if (wearableBundle != null) {
1740                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1741                }
1742            }
1743
1744            /**
1745             * Apply wearable extensions to a notification action that is being built. This is
1746             * typically called by the {@link NotificationCompat.Action.Builder#extend}
1747             * method of {@link NotificationCompat.Action.Builder}.
1748             */
1749            @Override
1750            public Action.Builder extend(Action.Builder builder) {
1751                Bundle wearableBundle = new Bundle();
1752
1753                if (mFlags != DEFAULT_FLAGS) {
1754                    wearableBundle.putInt(KEY_FLAGS, mFlags);
1755                }
1756
1757                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1758                return builder;
1759            }
1760
1761            @Override
1762            public WearableExtender clone() {
1763                WearableExtender that = new WearableExtender();
1764                that.mFlags = this.mFlags;
1765                return that;
1766            }
1767
1768            /**
1769             * Set whether this action is available when the wearable device is not connected to
1770             * a companion device. The user can still trigger this action when the wearable device
1771             * is offline, but a visual hint will indicate that the action may not be available.
1772             * Defaults to true.
1773             */
1774            public WearableExtender setAvailableOffline(boolean availableOffline) {
1775                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1776                return this;
1777            }
1778
1779            /**
1780             * Get whether this action is available when the wearable device is not connected to
1781             * a companion device. The user can still trigger this action when the wearable device
1782             * is offline, but a visual hint will indicate that the action may not be available.
1783             * Defaults to true.
1784             */
1785            public boolean isAvailableOffline() {
1786                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1787            }
1788
1789            private void setFlag(int mask, boolean value) {
1790                if (value) {
1791                    mFlags |= mask;
1792                } else {
1793                    mFlags &= ~mask;
1794                }
1795            }
1796        }
1797
1798        /** @hide */
1799        public static final Factory FACTORY = new Factory() {
1800            @Override
1801            public Action build(int icon, CharSequence title,
1802                    PendingIntent actionIntent, Bundle extras,
1803                    RemoteInputCompatBase.RemoteInput[] remoteInputs) {
1804                return new Action(icon, title, actionIntent, extras,
1805                        (RemoteInput[]) remoteInputs);
1806            }
1807
1808            @Override
1809            public Action[] newArray(int length) {
1810                return new Action[length];
1811            }
1812        };
1813    }
1814
1815
1816    /**
1817     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1818     * metadata or change options on a notification builder.
1819     */
1820    public interface Extender {
1821        /**
1822         * Apply this extender to a notification builder.
1823         * @param builder the builder to be modified.
1824         * @return the build object for chaining.
1825         */
1826        public Builder extend(Builder builder);
1827    }
1828
1829    /**
1830     * Helper class to add wearable extensions to notifications.
1831     * <p class="note"> See
1832     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
1833     * for Android Wear</a> for more information on how to use this class.
1834     * <p>
1835     * To create a notification with wearable extensions:
1836     * <ol>
1837     *   <li>Create a {@link NotificationCompat.Builder}, setting any desired
1838     *   properties.
1839     *   <li>Create a {@link NotificationCompat.WearableExtender}.
1840     *   <li>Set wearable-specific properties using the
1841     *   {@code add} and {@code set} methods of {@link NotificationCompat.WearableExtender}.
1842     *   <li>Call {@link NotificationCompat.Builder#extend} to apply the extensions to a
1843     *   notification.
1844     *   <li>Post the notification to the notification
1845     *   system with the {@code NotificationManagerCompat.notify(...)} methods
1846     *   and not the {@code NotificationManager.notify(...)} methods.
1847     * </ol>
1848     *
1849     * <pre class="prettyprint">
1850     * Notification notif = new NotificationCompat.Builder(mContext)
1851     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
1852     *         .setContentText(subject)
1853     *         .setSmallIcon(R.drawable.new_mail)
1854     *         .extend(new NotificationCompat.WearableExtender()
1855     *                 .setContentIcon(R.drawable.new_mail))
1856     *         .build();
1857     * NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
1858     *
1859     * <p>Wearable extensions can be accessed on an existing notification by using the
1860     * {@code WearableExtender(Notification)} constructor,
1861     * and then using the {@code get} methods to access values.
1862     *
1863     * <pre class="prettyprint">
1864     * NotificationCompat.WearableExtender wearableExtender =
1865     *         new NotificationCompat.WearableExtender(notification);
1866     * List&lt;Notification&gt; pages = wearableExtender.getPages();
1867     * </pre>
1868     */
1869    public static final class WearableExtender implements Extender {
1870        /**
1871         * Sentinel value for an action index that is unset.
1872         */
1873        public static final int UNSET_ACTION_INDEX = -1;
1874
1875        /**
1876         * Size value for use with {@link #setCustomSizePreset} to show this notification with
1877         * default sizing.
1878         * <p>For custom display notifications created using {@link #setDisplayIntent},
1879         * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
1880         * on their content.
1881         */
1882        public static final int SIZE_DEFAULT = 0;
1883
1884        /**
1885         * Size value for use with {@link #setCustomSizePreset} to show this notification
1886         * with an extra small size.
1887         * <p>This value is only applicable for custom display notifications created using
1888         * {@link #setDisplayIntent}.
1889         */
1890        public static final int SIZE_XSMALL = 1;
1891
1892        /**
1893         * Size value for use with {@link #setCustomSizePreset} to show this notification
1894         * with a small size.
1895         * <p>This value is only applicable for custom display notifications created using
1896         * {@link #setDisplayIntent}.
1897         */
1898        public static final int SIZE_SMALL = 2;
1899
1900        /**
1901         * Size value for use with {@link #setCustomSizePreset} to show this notification
1902         * with a medium size.
1903         * <p>This value is only applicable for custom display notifications created using
1904         * {@link #setDisplayIntent}.
1905         */
1906        public static final int SIZE_MEDIUM = 3;
1907
1908        /**
1909         * Size value for use with {@link #setCustomSizePreset} to show this notification
1910         * with a large size.
1911         * <p>This value is only applicable for custom display notifications created using
1912         * {@link #setDisplayIntent}.
1913         */
1914        public static final int SIZE_LARGE = 4;
1915
1916        /** Notification extra which contains wearable extensions */
1917        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1918
1919        // Keys within EXTRA_WEARABLE_OPTIONS for wearable options.
1920        private static final String KEY_ACTIONS = "actions";
1921        private static final String KEY_FLAGS = "flags";
1922        private static final String KEY_DISPLAY_INTENT = "displayIntent";
1923        private static final String KEY_PAGES = "pages";
1924        private static final String KEY_BACKGROUND = "background";
1925        private static final String KEY_CONTENT_ICON = "contentIcon";
1926        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
1927        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
1928        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
1929        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
1930        private static final String KEY_GRAVITY = "gravity";
1931
1932        // Flags bitwise-ored to mFlags
1933        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
1934        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
1935        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
1936        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
1937
1938        // Default value for flags integer
1939        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
1940
1941        private static final int DEFAULT_CONTENT_ICON_GRAVITY = GravityCompat.END;
1942        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
1943
1944        private ArrayList<Action> mActions = new ArrayList<Action>();
1945        private int mFlags = DEFAULT_FLAGS;
1946        private PendingIntent mDisplayIntent;
1947        private ArrayList<Notification> mPages = new ArrayList<Notification>();
1948        private Bitmap mBackground;
1949        private int mContentIcon;
1950        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
1951        private int mContentActionIndex = UNSET_ACTION_INDEX;
1952        private int mCustomSizePreset = SIZE_DEFAULT;
1953        private int mCustomContentHeight;
1954        private int mGravity = DEFAULT_GRAVITY;
1955
1956        /**
1957         * Create a {@link NotificationCompat.WearableExtender} with default
1958         * options.
1959         */
1960        public WearableExtender() {
1961        }
1962
1963        public WearableExtender(Notification notif) {
1964            Bundle extras = getExtras(notif);
1965            Bundle wearableBundle = extras != null ? extras.getBundle(EXTRA_WEARABLE_EXTENSIONS)
1966                    : null;
1967            if (wearableBundle != null) {
1968                Action[] actions = IMPL.getActionsFromParcelableArrayList(
1969                        wearableBundle.getParcelableArrayList(KEY_ACTIONS));
1970                if (actions != null) {
1971                    Collections.addAll(mActions, actions);
1972                }
1973
1974                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1975                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
1976
1977                Notification[] pages = getNotificationArrayFromBundle(
1978                        wearableBundle, KEY_PAGES);
1979                if (pages != null) {
1980                    Collections.addAll(mPages, pages);
1981                }
1982
1983                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
1984                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
1985                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
1986                        DEFAULT_CONTENT_ICON_GRAVITY);
1987                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
1988                        UNSET_ACTION_INDEX);
1989                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
1990                        SIZE_DEFAULT);
1991                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
1992                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
1993            }
1994        }
1995
1996        /**
1997         * Apply wearable extensions to a notification that is being built. This is typically
1998         * called by the {@link NotificationCompat.Builder#extend} method of
1999         * {@link NotificationCompat.Builder}.
2000         */
2001        @Override
2002        public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
2003            Bundle wearableBundle = new Bundle();
2004
2005            if (!mActions.isEmpty()) {
2006                wearableBundle.putParcelableArrayList(KEY_ACTIONS,
2007                        IMPL.getParcelableArrayListForActions(mActions.toArray(
2008                                new Action[mActions.size()])));
2009            }
2010            if (mFlags != DEFAULT_FLAGS) {
2011                wearableBundle.putInt(KEY_FLAGS, mFlags);
2012            }
2013            if (mDisplayIntent != null) {
2014                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
2015            }
2016            if (!mPages.isEmpty()) {
2017                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
2018                        new Notification[mPages.size()]));
2019            }
2020            if (mBackground != null) {
2021                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
2022            }
2023            if (mContentIcon != 0) {
2024                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
2025            }
2026            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
2027                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
2028            }
2029            if (mContentActionIndex != UNSET_ACTION_INDEX) {
2030                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
2031                        mContentActionIndex);
2032            }
2033            if (mCustomSizePreset != SIZE_DEFAULT) {
2034                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
2035            }
2036            if (mCustomContentHeight != 0) {
2037                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
2038            }
2039            if (mGravity != DEFAULT_GRAVITY) {
2040                wearableBundle.putInt(KEY_GRAVITY, mGravity);
2041            }
2042
2043            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
2044            return builder;
2045        }
2046
2047        @Override
2048        public WearableExtender clone() {
2049            WearableExtender that = new WearableExtender();
2050            that.mActions = new ArrayList<Action>(this.mActions);
2051            that.mFlags = this.mFlags;
2052            that.mDisplayIntent = this.mDisplayIntent;
2053            that.mPages = new ArrayList<Notification>(this.mPages);
2054            that.mBackground = this.mBackground;
2055            that.mContentIcon = this.mContentIcon;
2056            that.mContentIconGravity = this.mContentIconGravity;
2057            that.mContentActionIndex = this.mContentActionIndex;
2058            that.mCustomSizePreset = this.mCustomSizePreset;
2059            that.mCustomContentHeight = this.mCustomContentHeight;
2060            that.mGravity = this.mGravity;
2061            return that;
2062        }
2063
2064        /**
2065         * Add a wearable action to this notification.
2066         *
2067         * <p>When wearable actions are added using this method, the set of actions that
2068         * show on a wearable device splits from devices that only show actions added
2069         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
2070         * of which actions display on different devices.
2071         *
2072         * @param action the action to add to this notification
2073         * @return this object for method chaining
2074         * @see NotificationCompat.Action
2075         */
2076        public WearableExtender addAction(Action action) {
2077            mActions.add(action);
2078            return this;
2079        }
2080
2081        /**
2082         * Adds wearable actions to this notification.
2083         *
2084         * <p>When wearable actions are added using this method, the set of actions that
2085         * show on a wearable device splits from devices that only show actions added
2086         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
2087         * of which actions display on different devices.
2088         *
2089         * @param actions the actions to add to this notification
2090         * @return this object for method chaining
2091         * @see NotificationCompat.Action
2092         */
2093        public WearableExtender addActions(List<Action> actions) {
2094            mActions.addAll(actions);
2095            return this;
2096        }
2097
2098        /**
2099         * Clear all wearable actions present on this builder.
2100         * @return this object for method chaining.
2101         * @see #addAction
2102         */
2103        public WearableExtender clearActions() {
2104            mActions.clear();
2105            return this;
2106        }
2107
2108        /**
2109         * Get the wearable actions present on this notification.
2110         */
2111        public List<Action> getActions() {
2112            return mActions;
2113        }
2114
2115        /**
2116         * Set an intent to launch inside of an activity view when displaying
2117         * this notification. This {@link PendingIntent} should be for an activity.
2118         *
2119         * @param intent the {@link PendingIntent} for an activity
2120         * @return this object for method chaining
2121         * @see NotificationCompat.WearableExtender#getDisplayIntent
2122         */
2123        public WearableExtender setDisplayIntent(PendingIntent intent) {
2124            mDisplayIntent = intent;
2125            return this;
2126        }
2127
2128        /**
2129         * Get the intent to launch inside of an activity view when displaying this
2130         * notification. This {@code PendingIntent} should be for an activity.
2131         */
2132        public PendingIntent getDisplayIntent() {
2133            return mDisplayIntent;
2134        }
2135
2136        /**
2137         * Add an additional page of content to display with this notification. The current
2138         * notification forms the first page, and pages added using this function form
2139         * subsequent pages. This field can be used to separate a notification into multiple
2140         * sections.
2141         *
2142         * @param page the notification to add as another page
2143         * @return this object for method chaining
2144         * @see NotificationCompat.WearableExtender#getPages
2145         */
2146        public WearableExtender addPage(Notification page) {
2147            mPages.add(page);
2148            return this;
2149        }
2150
2151        /**
2152         * Add additional pages of content to display with this notification. The current
2153         * notification forms the first page, and pages added using this function form
2154         * subsequent pages. This field can be used to separate a notification into multiple
2155         * sections.
2156         *
2157         * @param pages a list of notifications
2158         * @return this object for method chaining
2159         * @see NotificationCompat.WearableExtender#getPages
2160         */
2161        public WearableExtender addPages(List<Notification> pages) {
2162            mPages.addAll(pages);
2163            return this;
2164        }
2165
2166        /**
2167         * Clear all additional pages present on this builder.
2168         * @return this object for method chaining.
2169         * @see #addPage
2170         */
2171        public WearableExtender clearPages() {
2172            mPages.clear();
2173            return this;
2174        }
2175
2176        /**
2177         * Get the array of additional pages of content for displaying this notification. The
2178         * current notification forms the first page, and elements within this array form
2179         * subsequent pages. This field can be used to separate a notification into multiple
2180         * sections.
2181         * @return the pages for this notification
2182         */
2183        public List<Notification> getPages() {
2184            return mPages;
2185        }
2186
2187        /**
2188         * Set a background image to be displayed behind the notification content.
2189         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
2190         * will work with any notification style.
2191         *
2192         * @param background the background bitmap
2193         * @return this object for method chaining
2194         * @see NotificationCompat.WearableExtender#getBackground
2195         */
2196        public WearableExtender setBackground(Bitmap background) {
2197            mBackground = background;
2198            return this;
2199        }
2200
2201        /**
2202         * Get a background image to be displayed behind the notification content.
2203         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
2204         * will work with any notification style.
2205         *
2206         * @return the background image
2207         * @see NotificationCompat.WearableExtender#setBackground
2208         */
2209        public Bitmap getBackground() {
2210            return mBackground;
2211        }
2212
2213        /**
2214         * Set an icon that goes with the content of this notification.
2215         */
2216        public WearableExtender setContentIcon(int icon) {
2217            mContentIcon = icon;
2218            return this;
2219        }
2220
2221        /**
2222         * Get an icon that goes with the content of this notification.
2223         */
2224        public int getContentIcon() {
2225            return mContentIcon;
2226        }
2227
2228        /**
2229         * Set the gravity that the content icon should have within the notification display.
2230         * Supported values include {@link android.view.Gravity#START} and
2231         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
2232         * @see #setContentIcon
2233         */
2234        public WearableExtender setContentIconGravity(int contentIconGravity) {
2235            mContentIconGravity = contentIconGravity;
2236            return this;
2237        }
2238
2239        /**
2240         * Get the gravity that the content icon should have within the notification display.
2241         * Supported values include {@link android.view.Gravity#START} and
2242         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
2243         * @see #getContentIcon
2244         */
2245        public int getContentIconGravity() {
2246            return mContentIconGravity;
2247        }
2248
2249        /**
2250         * Set an action from this notification's actions to be clickable with the content of
2251         * this notification page. This action will no longer display separately from the
2252         * notification content. This action's icon will display with optional subtext provided
2253         * by the action's title.
2254         * @param actionIndex The index of the action to hoist on the current notification page.
2255         *                    If wearable actions are present, this index will apply to that list,
2256         *                    otherwise it will apply to the main notification's actions list.
2257         */
2258        public WearableExtender setContentAction(int actionIndex) {
2259            mContentActionIndex = actionIndex;
2260            return this;
2261        }
2262
2263        /**
2264         * Get the action index of an action from this notification to show as clickable with
2265         * the content of this notification page. When the user clicks this notification page,
2266         * this action will trigger. This action will no longer display separately from the
2267         * notification content. The action's icon will display with optional subtext provided
2268         * by the action's title.
2269         *
2270         * <p>If wearable specific actions are present, this index will apply to that list,
2271         * otherwise it will apply to the main notification's actions list.
2272         */
2273        public int getContentAction() {
2274            return mContentActionIndex;
2275        }
2276
2277        /**
2278         * Set the gravity that this notification should have within the available viewport space.
2279         * Supported values include {@link android.view.Gravity#TOP},
2280         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
2281         * The default value is {@link android.view.Gravity#BOTTOM}.
2282         */
2283        public WearableExtender setGravity(int gravity) {
2284            mGravity = gravity;
2285            return this;
2286        }
2287
2288        /**
2289         * Get the gravity that this notification should have within the available viewport space.
2290         * Supported values include {@link android.view.Gravity#TOP},
2291         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
2292         * The default value is {@link android.view.Gravity#BOTTOM}.
2293         */
2294        public int getGravity() {
2295            return mGravity;
2296        }
2297
2298        /**
2299         * Set the custom size preset for the display of this notification out of the available
2300         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
2301         * {@link #SIZE_LARGE}.
2302         * <p>Some custom size presets are only applicable for custom display notifications created
2303         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. Check the
2304         * documentation for the preset in question. See also
2305         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
2306         */
2307        public WearableExtender setCustomSizePreset(int sizePreset) {
2308            mCustomSizePreset = sizePreset;
2309            return this;
2310        }
2311
2312        /**
2313         * Get the custom size preset for the display of this notification out of the available
2314         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
2315         * {@link #SIZE_LARGE}.
2316         * <p>Some custom size presets are only applicable for custom display notifications created
2317         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
2318         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
2319         */
2320        public int getCustomSizePreset() {
2321            return mCustomSizePreset;
2322        }
2323
2324        /**
2325         * Set the custom height in pixels for the display of this notification's content.
2326         * <p>This option is only available for custom display notifications created
2327         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. See also
2328         * {@link NotificationCompat.WearableExtender#setCustomSizePreset} and
2329         * {@link #getCustomContentHeight}.
2330         */
2331        public WearableExtender setCustomContentHeight(int height) {
2332            mCustomContentHeight = height;
2333            return this;
2334        }
2335
2336        /**
2337         * Get the custom height in pixels for the display of this notification's content.
2338         * <p>This option is only available for custom display notifications created
2339         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
2340         * {@link #setCustomContentHeight}.
2341         */
2342        public int getCustomContentHeight() {
2343            return mCustomContentHeight;
2344        }
2345
2346        /**
2347         * Set whether the scrolling position for the contents of this notification should start
2348         * at the bottom of the contents instead of the top when the contents are too long to
2349         * display within the screen.  Default is false (start scroll at the top).
2350         */
2351        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
2352            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
2353            return this;
2354        }
2355
2356        /**
2357         * Get whether the scrolling position for the contents of this notification should start
2358         * at the bottom of the contents instead of the top when the contents are too long to
2359         * display within the screen. Default is false (start scroll at the top).
2360         */
2361        public boolean getStartScrollBottom() {
2362            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
2363        }
2364
2365        /**
2366         * Set whether the content intent is available when the wearable device is not connected
2367         * to a companion device.  The user can still trigger this intent when the wearable device
2368         * is offline, but a visual hint will indicate that the content intent may not be available.
2369         * Defaults to true.
2370         */
2371        public WearableExtender setContentIntentAvailableOffline(
2372                boolean contentIntentAvailableOffline) {
2373            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
2374            return this;
2375        }
2376
2377        /**
2378         * Get whether the content intent is available when the wearable device is not connected
2379         * to a companion device.  The user can still trigger this intent when the wearable device
2380         * is offline, but a visual hint will indicate that the content intent may not be available.
2381         * Defaults to true.
2382         */
2383        public boolean getContentIntentAvailableOffline() {
2384            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
2385        }
2386
2387        /**
2388         * Set a hint that this notification's icon should not be displayed.
2389         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
2390         * @return this object for method chaining
2391         */
2392        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
2393            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
2394            return this;
2395        }
2396
2397        /**
2398         * Get a hint that this notification's icon should not be displayed.
2399         * @return {@code true} if this icon should not be displayed, false otherwise.
2400         * The default value is {@code false} if this was never set.
2401         */
2402        public boolean getHintHideIcon() {
2403            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
2404        }
2405
2406        /**
2407         * Set a visual hint that only the background image of this notification should be
2408         * displayed, and other semantic content should be hidden. This hint is only applicable
2409         * to sub-pages added using {@link #addPage}.
2410         */
2411        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
2412            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
2413            return this;
2414        }
2415
2416        /**
2417         * Get a visual hint that only the background image of this notification should be
2418         * displayed, and other semantic content should be hidden. This hint is only applicable
2419         * to sub-pages added using {@link NotificationCompat.WearableExtender#addPage}.
2420         */
2421        public boolean getHintShowBackgroundOnly() {
2422            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
2423        }
2424
2425        private void setFlag(int mask, boolean value) {
2426            if (value) {
2427                mFlags |= mask;
2428            } else {
2429                mFlags &= ~mask;
2430            }
2431        }
2432    }
2433
2434    /**
2435     * Get an array of Notification objects from a parcelable array bundle field.
2436     * Update the bundle to have a typed array so fetches in the future don't need
2437     * to do an array copy.
2438     */
2439    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
2440        Parcelable[] array = bundle.getParcelableArray(key);
2441        if (array instanceof Notification[] || array == null) {
2442            return (Notification[]) array;
2443        }
2444        Notification[] typedArray = new Notification[array.length];
2445        for (int i = 0; i < array.length; i++) {
2446            typedArray[i] = (Notification) array[i];
2447        }
2448        bundle.putParcelableArray(key, typedArray);
2449        return typedArray;
2450    }
2451
2452    /**
2453     * Gets the {@link Notification#extras} field from a notification in a backwards
2454     * compatible manner. Extras field was supported from JellyBean (Api level 16)
2455     * forwards. This function will return null on older api levels.
2456     */
2457    public static Bundle getExtras(Notification notif) {
2458        return IMPL.getExtras(notif);
2459    }
2460
2461    /**
2462     * Get the number of actions in this notification in a backwards compatible
2463     * manner. Actions were supported from JellyBean (Api level 16) forwards.
2464     */
2465    public static int getActionCount(Notification notif) {
2466        return IMPL.getActionCount(notif);
2467    }
2468
2469    /**
2470     * Get an action on this notification in a backwards compatible
2471     * manner. Actions were supported from JellyBean (Api level 16) forwards.
2472     * @param notif The notification to inspect.
2473     * @param actionIndex The index of the action to retrieve.
2474     */
2475    public static Action getAction(Notification notif, int actionIndex) {
2476        return IMPL.getAction(notif, actionIndex);
2477    }
2478
2479    /**
2480     * Get whether or not this notification is only relevant to the current device.
2481     *
2482     * <p>Some notifications can be bridged to other devices for remote display.
2483     * If this hint is set, it is recommend that this notification not be bridged.
2484     */
2485    public static boolean getLocalOnly(Notification notif) {
2486        return IMPL.getLocalOnly(notif);
2487    }
2488
2489    /**
2490     * Get the key used to group this notification into a cluster or stack
2491     * with other notifications on devices which support such rendering.
2492     */
2493    public static String getGroup(Notification notif) {
2494        return IMPL.getGroup(notif);
2495    }
2496
2497    /**
2498     * Get whether this notification to be the group summary for a group of notifications.
2499     * Grouped notifications may display in a cluster or stack on devices which
2500     * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
2501     * @return Whether this notification is a group summary.
2502     */
2503    public static boolean isGroupSummary(Notification notif) {
2504        return IMPL.isGroupSummary(notif);
2505    }
2506
2507    /**
2508     * Get a sort key that orders this notification among other notifications from the
2509     * same package. This can be useful if an external sort was already applied and an app
2510     * would like to preserve this. Notifications will be sorted lexicographically using this
2511     * value, although providing different priorities in addition to providing sort key may
2512     * cause this value to be ignored.
2513     *
2514     * <p>This sort key can also be used to order members of a notification group. See
2515     * {@link Builder#setGroup}.
2516     *
2517     * @see String#compareTo(String)
2518     */
2519    public static String getSortKey(Notification notif) {
2520        return IMPL.getSortKey(notif);
2521    }
2522}
2523