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