NotificationCompat.java revision b2216c3ffda4d63d3f0c9f6b16afc8c912c4296e
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.mCategory,
643                    b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion,
644                    b.mGroupKey, b.mGroupSummary, b.mSortKey);
645            addActionsToBuilder(builder, b.mActions);
646            addStyleToBuilderJellybean(builder, b.mStyle);
647            return builder.build();
648        }
649
650        @Override
651        public Action getAction(Notification n, int actionIndex) {
652            return (Action) NotificationCompatApi20.getAction(n, actionIndex, Action.FACTORY,
653                    RemoteInput.FACTORY);
654        }
655
656        @Override
657        public Action[] getActionsFromParcelableArrayList(
658                ArrayList<Parcelable> parcelables) {
659            return (Action[]) NotificationCompatApi20.getActionsFromParcelableArrayList(
660                    parcelables, Action.FACTORY, RemoteInput.FACTORY);
661        }
662
663        @Override
664        public ArrayList<Parcelable> getParcelableArrayListForActions(
665                Action[] actions) {
666            return NotificationCompatApi20.getParcelableArrayListForActions(actions);
667        }
668
669        @Override
670        public boolean getLocalOnly(Notification n) {
671            return NotificationCompatApi20.getLocalOnly(n);
672        }
673
674        @Override
675        public String getGroup(Notification n) {
676            return NotificationCompatApi20.getGroup(n);
677        }
678
679        @Override
680        public boolean isGroupSummary(Notification n) {
681            return NotificationCompatApi20.isGroupSummary(n);
682        }
683
684        @Override
685        public String getSortKey(Notification n) {
686            return NotificationCompatApi20.getSortKey(n);
687        }
688    }
689
690    private static void addActionsToBuilder(NotificationBuilderWithActions builder,
691            ArrayList<Action> actions) {
692        for (Action action : actions) {
693            builder.addAction(action);
694        }
695    }
696
697    private static void addStyleToBuilderJellybean(NotificationBuilderWithBuilderAccessor builder,
698            Style style) {
699        if (style != null) {
700            if (style instanceof BigTextStyle) {
701                BigTextStyle bigTextStyle = (BigTextStyle) style;
702                NotificationCompatJellybean.addBigTextStyle(builder,
703                        bigTextStyle.mBigContentTitle,
704                        bigTextStyle.mSummaryTextSet,
705                        bigTextStyle.mSummaryText,
706                        bigTextStyle.mBigText);
707            } else if (style instanceof InboxStyle) {
708                InboxStyle inboxStyle = (InboxStyle) style;
709                NotificationCompatJellybean.addInboxStyle(builder,
710                        inboxStyle.mBigContentTitle,
711                        inboxStyle.mSummaryTextSet,
712                        inboxStyle.mSummaryText,
713                        inboxStyle.mTexts);
714            } else if (style instanceof BigPictureStyle) {
715                BigPictureStyle bigPictureStyle = (BigPictureStyle) style;
716                NotificationCompatJellybean.addBigPictureStyle(builder,
717                        bigPictureStyle.mBigContentTitle,
718                        bigPictureStyle.mSummaryTextSet,
719                        bigPictureStyle.mSummaryText,
720                        bigPictureStyle.mPicture,
721                        bigPictureStyle.mBigLargeIcon,
722                        bigPictureStyle.mBigLargeIconSet);
723            }
724        }
725    }
726
727    static class NotificationCompatImplApi21 extends NotificationCompatImplApi20 {
728        @Override
729        public String getCategory(Notification notif) {
730            return NotificationCompatApi21.getCategory(notif);
731        }
732    }
733
734    static {
735        // TODO: Replace this if clause when SDK_INT is incremented to 21.
736        if (Build.VERSION.RELEASE.equals("L")) {
737            IMPL = new NotificationCompatImplApi21();
738        } else if (Build.VERSION.SDK_INT >= 20) {
739            IMPL = new NotificationCompatImplApi20();
740        } else if (Build.VERSION.SDK_INT >= 19) {
741            IMPL = new NotificationCompatImplKitKat();
742        } else if (Build.VERSION.SDK_INT >= 16) {
743            IMPL = new NotificationCompatImplJellybean();
744        } else if (Build.VERSION.SDK_INT >= 14) {
745            IMPL = new NotificationCompatImplIceCreamSandwich();
746        } else if (Build.VERSION.SDK_INT >= 11) {
747            IMPL = new NotificationCompatImplHoneycomb();
748        } else if (Build.VERSION.SDK_INT >= 9) {
749            IMPL = new NotificationCompatImplGingerbread();
750        } else {
751            IMPL = new NotificationCompatImplBase();
752        }
753    }
754
755    /**
756     * Builder class for {@link NotificationCompat} objects.  Allows easier control over
757     * all the flags, as well as help constructing the typical notification layouts.
758     * <p>
759     * On platform versions that don't offer expanded notifications, methods that depend on
760     * expanded notifications have no effect.
761     * </p>
762     * <p>
763     * For example, action buttons won't appear on platforms prior to Android 4.1. Action
764     * buttons depend on expanded notifications, which are only available in Android 4.1
765     * and later.
766     * <p>
767     * For this reason, you should always ensure that UI controls in a notification are also
768     * available in an {@link android.app.Activity} in your app, and you should always start that
769     * {@link android.app.Activity} when users click the notification. To do this, use the
770     * {@link NotificationCompat.Builder#setContentIntent setContentIntent()}
771     * method.
772     * </p>
773     *
774     */
775    public static class Builder {
776        Context mContext;
777
778        CharSequence mContentTitle;
779        CharSequence mContentText;
780        PendingIntent mContentIntent;
781        PendingIntent mFullScreenIntent;
782        RemoteViews mTickerView;
783        Bitmap mLargeIcon;
784        CharSequence mContentInfo;
785        int mNumber;
786        int mPriority;
787        boolean mUseChronometer;
788        Style mStyle;
789        CharSequence mSubText;
790        int mProgressMax;
791        int mProgress;
792        boolean mProgressIndeterminate;
793        String mGroupKey;
794        boolean mGroupSummary;
795        String mSortKey;
796        ArrayList<Action> mActions = new ArrayList<Action>();
797        boolean mLocalOnly = false;
798        String mCategory;
799        Bundle mExtras;
800        int mColor = COLOR_DEFAULT;
801        int mVisibility = VISIBILITY_PRIVATE;
802        Notification mPublicVersion;
803
804        Notification mNotification = new Notification();
805        public ArrayList<String> mPeople;
806
807        /**
808         * Constructor.
809         *
810         * Automatically sets the when field to {@link System#currentTimeMillis()
811         * System.currentTimeMillis()} and the audio stream to the
812         * {@link Notification#STREAM_DEFAULT}.
813         *
814         * @param context A {@link Context} that will be used to construct the
815         *      RemoteViews. The Context will not be held past the lifetime of this
816         *      Builder object.
817         */
818        public Builder(Context context) {
819            mContext = context;
820
821            // Set defaults to match the defaults of a Notification
822            mNotification.when = System.currentTimeMillis();
823            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
824            mPriority = PRIORITY_DEFAULT;
825            mPeople = new ArrayList<String>();
826        }
827
828        /**
829         * Set the time that the event occurred.  Notifications in the panel are
830         * sorted by this time.
831         */
832        public Builder setWhen(long when) {
833            mNotification.when = when;
834            return this;
835        }
836
837        /**
838         * Show the {@link Notification#when} field as a stopwatch.
839         *
840         * Instead of presenting <code>when</code> as a timestamp, the notification will show an
841         * automatically updating display of the minutes and seconds since <code>when</code>.
842         *
843         * Useful when showing an elapsed time (like an ongoing phone call).
844         *
845         * @see android.widget.Chronometer
846         * @see Notification#when
847         */
848        public Builder setUsesChronometer(boolean b) {
849            mUseChronometer = b;
850            return this;
851        }
852
853        /**
854         * Set the small icon to use in the notification layouts.  Different classes of devices
855         * may return different sizes.  See the UX guidelines for more information on how to
856         * design these icons.
857         *
858         * @param icon A resource ID in the application's package of the drawble to use.
859         */
860        public Builder setSmallIcon(int icon) {
861            mNotification.icon = icon;
862            return this;
863        }
864
865        /**
866         * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
867         * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
868         * LevelListDrawable}.
869         *
870         * @param icon A resource ID in the application's package of the drawble to use.
871         * @param level The level to use for the icon.
872         *
873         * @see android.graphics.drawable.LevelListDrawable
874         */
875        public Builder setSmallIcon(int icon, int level) {
876            mNotification.icon = icon;
877            mNotification.iconLevel = level;
878            return this;
879        }
880
881        /**
882         * Set the title (first row) of the notification, in a standard notification.
883         */
884        public Builder setContentTitle(CharSequence title) {
885            mContentTitle = title;
886            return this;
887        }
888
889        /**
890         * Set the text (second row) of the notification, in a standard notification.
891         */
892        public Builder setContentText(CharSequence text) {
893            mContentText = text;
894            return this;
895        }
896
897        /**
898         * Set the third line of text in the platform notification template.
899         * Don't use if you're also using {@link #setProgress(int, int, boolean)};
900         * they occupy the same location in the standard template.
901         * <br>
902         * If the platform does not provide large-format notifications, this method has no effect.
903         * The third line of text only appears in expanded view.
904         * <br>
905         */
906        public Builder setSubText(CharSequence text) {
907            mSubText = text;
908            return this;
909        }
910
911        /**
912         * Set the large number at the right-hand side of the notification.  This is
913         * equivalent to setContentInfo, although it might show the number in a different
914         * font size for readability.
915         */
916        public Builder setNumber(int number) {
917            mNumber = number;
918            return this;
919        }
920
921        /**
922         * Set the large text at the right-hand side of the notification.
923         */
924        public Builder setContentInfo(CharSequence info) {
925            mContentInfo = info;
926            return this;
927        }
928
929        /**
930         * Set the progress this notification represents, which may be
931         * represented as a {@link android.widget.ProgressBar}.
932         */
933        public Builder setProgress(int max, int progress, boolean indeterminate) {
934            mProgressMax = max;
935            mProgress = progress;
936            mProgressIndeterminate = indeterminate;
937            return this;
938        }
939
940        /**
941         * Supply a custom RemoteViews to use instead of the standard one.
942         */
943        public Builder setContent(RemoteViews views) {
944            mNotification.contentView = views;
945            return this;
946        }
947
948        /**
949         * Supply a {@link PendingIntent} to send when the notification is clicked.
950         * If you do not supply an intent, you can now add PendingIntents to individual
951         * views to be launched when clicked by calling {@link RemoteViews#setOnClickPendingIntent
952         * RemoteViews.setOnClickPendingIntent(int,PendingIntent)}.  Be sure to
953         * read {@link Notification#contentIntent Notification.contentIntent} for
954         * how to correctly use this.
955         */
956        public Builder setContentIntent(PendingIntent intent) {
957            mContentIntent = intent;
958            return this;
959        }
960
961        /**
962         * Supply a {@link PendingIntent} to send when the notification is cleared by the user
963         * directly from the notification panel.  For example, this intent is sent when the user
964         * clicks the "Clear all" button, or the individual "X" buttons on notifications.  This
965         * intent is not sent when the application calls
966         * {@link android.app.NotificationManager#cancel NotificationManager.cancel(int)}.
967         */
968        public Builder setDeleteIntent(PendingIntent intent) {
969            mNotification.deleteIntent = intent;
970            return this;
971        }
972
973        /**
974         * An intent to launch instead of posting the notification to the status bar.
975         * Only for use with extremely high-priority notifications demanding the user's
976         * <strong>immediate</strong> attention, such as an incoming phone call or
977         * alarm clock that the user has explicitly set to a particular time.
978         * If this facility is used for something else, please give the user an option
979         * to turn it off and use a normal notification, as this can be extremely
980         * disruptive.
981         *
982         * <p>
983         * On some platforms, the system UI may choose to display a heads-up notification,
984         * instead of launching this intent, while the user is using the device.
985         * </p>
986         *
987         * @param intent The pending intent to launch.
988         * @param highPriority Passing true will cause this notification to be sent
989         *          even if other notifications are suppressed.
990         */
991        public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
992            mFullScreenIntent = intent;
993            setFlag(FLAG_HIGH_PRIORITY, highPriority);
994            return this;
995        }
996
997        /**
998         * Set the text that is displayed in the status bar when the notification first
999         * arrives.
1000         */
1001        public Builder setTicker(CharSequence tickerText) {
1002            mNotification.tickerText = tickerText;
1003            return this;
1004        }
1005
1006        /**
1007         * Set the text that is displayed in the status bar when the notification first
1008         * arrives, and also a RemoteViews object that may be displayed instead on some
1009         * devices.
1010         */
1011        public Builder setTicker(CharSequence tickerText, RemoteViews views) {
1012            mNotification.tickerText = tickerText;
1013            mTickerView = views;
1014            return this;
1015        }
1016
1017        /**
1018         * Set the large icon that is shown in the ticker and notification.
1019         */
1020        public Builder setLargeIcon(Bitmap icon) {
1021            mLargeIcon = icon;
1022            return this;
1023        }
1024
1025        /**
1026         * Set the sound to play.  It will play on the default stream.
1027         *
1028         * <p>
1029         * On some platforms, a notification that is noisy is more likely to be presented
1030         * as a heads-up notification.
1031         * </p>
1032         */
1033        public Builder setSound(Uri sound) {
1034            mNotification.sound = sound;
1035            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
1036            return this;
1037        }
1038
1039        /**
1040         * Set the sound to play.  It will play on the stream you supply.
1041         *
1042         * <p>
1043         * On some platforms, a notification that is noisy is more likely to be presented
1044         * as a heads-up notification.
1045         * </p>
1046         *
1047         * @see Notification#STREAM_DEFAULT
1048         * @see AudioManager for the <code>STREAM_</code> constants.
1049         */
1050        public Builder setSound(Uri sound, int streamType) {
1051            mNotification.sound = sound;
1052            mNotification.audioStreamType = streamType;
1053            return this;
1054        }
1055
1056        /**
1057         * Set the vibration pattern to use.
1058         *
1059         * <p>
1060         * On some platforms, a notification that vibrates is more likely to be presented
1061         * as a heads-up notification.
1062         * </p>
1063         *
1064         * @see android.os.Vibrator for a discussion of the <code>pattern</code>
1065         * parameter.
1066         */
1067        public Builder setVibrate(long[] pattern) {
1068            mNotification.vibrate = pattern;
1069            return this;
1070        }
1071
1072        /**
1073         * Set the argb value that you would like the LED on the device to blnk, as well as the
1074         * rate.  The rate is specified in terms of the number of milliseconds to be on
1075         * and then the number of milliseconds to be off.
1076         */
1077        public Builder setLights(int argb, int onMs, int offMs) {
1078            mNotification.ledARGB = argb;
1079            mNotification.ledOnMS = onMs;
1080            mNotification.ledOffMS = offMs;
1081            boolean showLights = mNotification.ledOnMS != 0 && mNotification.ledOffMS != 0;
1082            mNotification.flags = (mNotification.flags & ~Notification.FLAG_SHOW_LIGHTS) |
1083                    (showLights ? Notification.FLAG_SHOW_LIGHTS : 0);
1084            return this;
1085        }
1086
1087        /**
1088         * Set whether this is an ongoing notification.
1089         *
1090         * <p>Ongoing notifications differ from regular notifications in the following ways:
1091         * <ul>
1092         *   <li>Ongoing notifications are sorted above the regular notifications in the
1093         *   notification panel.</li>
1094         *   <li>Ongoing notifications do not have an 'X' close button, and are not affected
1095         *   by the "Clear all" button.
1096         * </ul>
1097         */
1098        public Builder setOngoing(boolean ongoing) {
1099            setFlag(Notification.FLAG_ONGOING_EVENT, ongoing);
1100            return this;
1101        }
1102
1103        /**
1104         * Set this flag if you would only like the sound, vibrate
1105         * and ticker to be played if the notification is not already showing.
1106         */
1107        public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
1108            setFlag(Notification.FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
1109            return this;
1110        }
1111
1112        /**
1113         * Setting this flag will make it so the notification is automatically
1114         * canceled when the user clicks it in the panel.  The PendingIntent
1115         * set with {@link #setDeleteIntent} will be broadcast when the notification
1116         * is canceled.
1117         */
1118        public Builder setAutoCancel(boolean autoCancel) {
1119            setFlag(Notification.FLAG_AUTO_CANCEL, autoCancel);
1120            return this;
1121        }
1122
1123        /**
1124         * Set whether or not this notification is only relevant to the current device.
1125         *
1126         * <p>Some notifications can be bridged to other devices for remote display.
1127         * This hint can be set to recommend this notification not be bridged.
1128         */
1129        public Builder setLocalOnly(boolean b) {
1130            mLocalOnly = b;
1131            return this;
1132        }
1133
1134        /**
1135         * Set the notification category.
1136         *
1137         * <p>Must be one of the predefined notification categories (see the <code>CATEGORY_*</code>
1138         * constants in {@link Notification}) that best describes this notification.
1139         * May be used by the system for ranking and filtering.
1140         */
1141        public Builder setCategory(String category) {
1142            mCategory = category;
1143            return this;
1144        }
1145
1146        /**
1147         * Set the default notification options that will be used.
1148         * <p>
1149         * The value should be one or more of the following fields combined with
1150         * bitwise-or:
1151         * {@link Notification#DEFAULT_SOUND}, {@link Notification#DEFAULT_VIBRATE},
1152         * {@link Notification#DEFAULT_LIGHTS}.
1153         * <p>
1154         * For all default values, use {@link Notification#DEFAULT_ALL}.
1155         */
1156        public Builder setDefaults(int defaults) {
1157            mNotification.defaults = defaults;
1158            if ((defaults & Notification.DEFAULT_LIGHTS) != 0) {
1159                mNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
1160            }
1161            return this;
1162        }
1163
1164        private void setFlag(int mask, boolean value) {
1165            if (value) {
1166                mNotification.flags |= mask;
1167            } else {
1168                mNotification.flags &= ~mask;
1169            }
1170        }
1171
1172        /**
1173         * Set the relative priority for this notification.
1174         *
1175         * Priority is an indication of how much of the user's
1176         * valuable attention should be consumed by this
1177         * notification. Low-priority notifications may be hidden from
1178         * the user in certain situations, while the user might be
1179         * interrupted for a higher-priority notification.
1180         * The system sets a notification's priority based on various factors including the
1181         * setPriority value. The effect may differ slightly on different platforms.
1182         *
1183         * @param pri Relative priority for this notification. Must be one of
1184         *     the priority constants defined by {@link NotificationCompat}.
1185         *     Acceptable values range from {@link
1186         *     NotificationCompat#PRIORITY_MIN} (-2) to {@link
1187         *     NotificationCompat#PRIORITY_MAX} (2).
1188         */
1189        public Builder setPriority(int pri) {
1190            mPriority = pri;
1191            return this;
1192        }
1193
1194        /**
1195         * Add a person that is relevant to this notification.
1196         *
1197         * @see Notification#EXTRA_PEOPLE
1198         */
1199        public Builder addPerson(String handle) {
1200            mPeople.add(handle);
1201            return this;
1202        }
1203
1204        /**
1205         * Set this notification to be part of a group of notifications sharing the same key.
1206         * Grouped notifications may display in a cluster or stack on devices which
1207         * support such rendering.
1208         *
1209         * <p>To make this notification the summary for its group, also call
1210         * {@link #setGroupSummary}. A sort order can be specified for group members by using
1211         * {@link #setSortKey}.
1212         * @param groupKey The group key of the group.
1213         * @return this object for method chaining
1214         */
1215        public Builder setGroup(String groupKey) {
1216            mGroupKey = groupKey;
1217            return this;
1218        }
1219
1220        /**
1221         * Set this notification to be the group summary for a group of notifications.
1222         * Grouped notifications may display in a cluster or stack on devices which
1223         * support such rendering. Requires a group key also be set using {@link #setGroup}.
1224         * @param isGroupSummary Whether this notification should be a group summary.
1225         * @return this object for method chaining
1226         */
1227        public Builder setGroupSummary(boolean isGroupSummary) {
1228            mGroupSummary = isGroupSummary;
1229            return this;
1230        }
1231
1232        /**
1233         * Set a sort key that orders this notification among other notifications from the
1234         * same package. This can be useful if an external sort was already applied and an app
1235         * would like to preserve this. Notifications will be sorted lexicographically using this
1236         * value, although providing different priorities in addition to providing sort key may
1237         * cause this value to be ignored.
1238         *
1239         * <p>This sort key can also be used to order members of a notification group. See
1240         * {@link Builder#setGroup}.
1241         *
1242         * @see String#compareTo(String)
1243         */
1244        public Builder setSortKey(String sortKey) {
1245            mSortKey = sortKey;
1246            return this;
1247        }
1248
1249        /**
1250         * Merge additional metadata into this notification.
1251         *
1252         * <p>Values within the Bundle will replace existing extras values in this Builder.
1253         *
1254         * @see Notification#extras
1255         */
1256        public Builder addExtras(Bundle extras) {
1257            if (extras != null) {
1258                if (mExtras == null) {
1259                    mExtras = new Bundle(extras);
1260                } else {
1261                    mExtras.putAll(extras);
1262                }
1263            }
1264            return this;
1265        }
1266
1267        /**
1268         * Set metadata for this notification.
1269         *
1270         * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
1271         * current contents are copied into the Notification each time {@link #build()} is
1272         * called.
1273         *
1274         * <p>Replaces any existing extras values with those from the provided Bundle.
1275         * Use {@link #addExtras} to merge in metadata instead.
1276         *
1277         * @see Notification#extras
1278         */
1279        public Builder setExtras(Bundle extras) {
1280            mExtras = extras;
1281            return this;
1282        }
1283
1284        /**
1285         * Get the current metadata Bundle used by this notification Builder.
1286         *
1287         * <p>The returned Bundle is shared with this Builder.
1288         *
1289         * <p>The current contents of this Bundle are copied into the Notification each time
1290         * {@link #build()} is called.
1291         *
1292         * @see Notification#extras
1293         */
1294        public Bundle getExtras() {
1295            if (mExtras == null) {
1296                mExtras = new Bundle();
1297            }
1298            return mExtras;
1299        }
1300
1301        /**
1302         * Add an action to this notification. Actions are typically displayed by
1303         * the system as a button adjacent to the notification content.
1304         * <br>
1305         * Action buttons won't appear on platforms prior to Android 4.1. Action
1306         * buttons depend on expanded notifications, which are only available in Android 4.1
1307         * and later. To ensure that an action button's functionality is always available, first
1308         * implement the functionality in the {@link android.app.Activity} that starts when a user
1309         * clicks the  notification (see {@link #setContentIntent setContentIntent()}), and then
1310         * enhance the notification by implementing the same functionality with
1311         * {@link #addAction addAction()}.
1312         *
1313         * @param icon Resource ID of a drawable that represents the action.
1314         * @param title Text describing the action.
1315         * @param intent {@link android.app.PendingIntent} to be fired when the action is invoked.
1316         */
1317        public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
1318            mActions.add(new Action(icon, title, intent));
1319            return this;
1320        }
1321
1322        /**
1323         * Add an action to this notification. Actions are typically displayed by
1324         * the system as a button adjacent to the notification content.
1325         * <br>
1326         * Action buttons won't appear on platforms prior to Android 4.1. Action
1327         * buttons depend on expanded notifications, which are only available in Android 4.1
1328         * and later. To ensure that an action button's functionality is always available, first
1329         * implement the functionality in the {@link android.app.Activity} that starts when a user
1330         * clicks the  notification (see {@link #setContentIntent setContentIntent()}), and then
1331         * enhance the notification by implementing the same functionality with
1332         * {@link #addAction addAction()}.
1333         *
1334         * @param action The action to add.
1335         */
1336        public Builder addAction(Action action) {
1337            mActions.add(action);
1338            return this;
1339        }
1340
1341        /**
1342         * Add a rich notification style to be applied at build time.
1343         * <br>
1344         * If the platform does not provide rich notification styles, this method has no effect. The
1345         * user will always see the normal notification style.
1346         *
1347         * @param style Object responsible for modifying the notification style.
1348         */
1349        public Builder setStyle(Style style) {
1350            if (mStyle != style) {
1351                mStyle = style;
1352                if (mStyle != null) {
1353                    mStyle.setBuilder(this);
1354                }
1355            }
1356            return this;
1357        }
1358
1359        /**
1360         * Sets {@link Notification#color}.
1361         *
1362         * @param argb The accent color to use
1363         *
1364         * @return The same Builder.
1365         */
1366        public Builder setColor(int argb) {
1367            mColor = argb;
1368            return this;
1369        }
1370
1371        /**
1372         * Sets {@link Notification#visibility}.
1373         *
1374         * @param visibility One of {@link Notification#VISIBILITY_PRIVATE} (the default),
1375         *                   {@link Notification#VISIBILITY_PUBLIC}, or
1376         *                   {@link Notification#VISIBILITY_SECRET}.
1377         */
1378        public Builder setVisibility(int visibility) {
1379            mVisibility = visibility;
1380            return this;
1381        }
1382
1383        /**
1384         * Supply a replacement Notification whose contents should be shown in insecure contexts
1385         * (i.e. atop the secure lockscreen). See {@link Notification#visibility} and
1386         * {@link #VISIBILITY_PUBLIC}.
1387         *
1388         * @param n A replacement notification, presumably with some or all info redacted.
1389         * @return The same Builder.
1390         */
1391        public Builder setPublicVersion(Notification n) {
1392            mPublicVersion = n;
1393            return this;
1394        }
1395
1396        /**
1397         * Apply an extender to this notification builder. Extenders may be used to add
1398         * metadata or change options on this builder.
1399         */
1400        public Builder extend(Extender extender) {
1401            extender.extend(this);
1402            return this;
1403        }
1404
1405        /**
1406         * @deprecated Use {@link #build()} instead.
1407         */
1408        @Deprecated
1409        public Notification getNotification() {
1410            return IMPL.build(this);
1411        }
1412
1413        /**
1414         * Combine all of the options that have been set and return a new {@link Notification}
1415         * object.
1416         */
1417        public Notification build() {
1418            return IMPL.build(this);
1419        }
1420    }
1421
1422    /**
1423     * An object that can apply a rich notification style to a {@link Notification.Builder}
1424     * object.
1425     * <br>
1426     * If the platform does not provide rich notification styles, methods in this class have no
1427     * effect.
1428     */
1429    public static abstract class Style {
1430        Builder mBuilder;
1431        CharSequence mBigContentTitle;
1432        CharSequence mSummaryText;
1433        boolean mSummaryTextSet = false;
1434
1435        public void setBuilder(Builder builder) {
1436            if (mBuilder != builder) {
1437                mBuilder = builder;
1438                if (mBuilder != null) {
1439                    mBuilder.setStyle(this);
1440                }
1441            }
1442        }
1443
1444        public Notification build() {
1445            Notification notification = null;
1446            if (mBuilder != null) {
1447                notification = mBuilder.build();
1448            }
1449            return notification;
1450        }
1451    }
1452
1453    /**
1454     * Helper class for generating large-format notifications that include a large image attachment.
1455     * <br>
1456     * If the platform does not provide large-format notifications, this method has no effect. The
1457     * user will always see the normal notification view.
1458     * <br>
1459     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1460     * <pre class="prettyprint">
1461     * Notification notif = new Notification.Builder(mContext)
1462     *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
1463     *     .setContentText(subject)
1464     *     .setSmallIcon(R.drawable.new_post)
1465     *     .setLargeIcon(aBitmap)
1466     *     .setStyle(new Notification.BigPictureStyle()
1467     *         .bigPicture(aBigBitmap))
1468     *     .build();
1469     * </pre>
1470     *
1471     * @see Notification#bigContentView
1472     */
1473    public static class BigPictureStyle extends Style {
1474        Bitmap mPicture;
1475        Bitmap mBigLargeIcon;
1476        boolean mBigLargeIconSet;
1477
1478        public BigPictureStyle() {
1479        }
1480
1481        public BigPictureStyle(Builder builder) {
1482            setBuilder(builder);
1483        }
1484
1485        /**
1486         * Overrides ContentTitle in the big form of the template.
1487         * This defaults to the value passed to setContentTitle().
1488         */
1489        public BigPictureStyle setBigContentTitle(CharSequence title) {
1490            mBigContentTitle = title;
1491            return this;
1492        }
1493
1494        /**
1495         * Set the first line of text after the detail section in the big form of the template.
1496         */
1497        public BigPictureStyle setSummaryText(CharSequence cs) {
1498            mSummaryText = cs;
1499            mSummaryTextSet = true;
1500            return this;
1501        }
1502
1503        /**
1504         * Provide the bitmap to be used as the payload for the BigPicture notification.
1505         */
1506        public BigPictureStyle bigPicture(Bitmap b) {
1507            mPicture = b;
1508            return this;
1509        }
1510
1511        /**
1512         * Override the large icon when the big notification is shown.
1513         */
1514        public BigPictureStyle bigLargeIcon(Bitmap b) {
1515            mBigLargeIcon = b;
1516            mBigLargeIconSet = true;
1517            return this;
1518        }
1519    }
1520
1521    /**
1522     * Helper class for generating large-format notifications that include a lot of text.
1523     *
1524     * <br>
1525     * If the platform does not provide large-format notifications, this method has no effect. The
1526     * user will always see the normal notification view.
1527     * <br>
1528     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1529     * <pre class="prettyprint">
1530     * Notification notif = new Notification.Builder(mContext)
1531     *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
1532     *     .setContentText(subject)
1533     *     .setSmallIcon(R.drawable.new_mail)
1534     *     .setLargeIcon(aBitmap)
1535     *     .setStyle(new Notification.BigTextStyle()
1536     *         .bigText(aVeryLongString))
1537     *     .build();
1538     * </pre>
1539     *
1540     * @see Notification#bigContentView
1541     */
1542    public static class BigTextStyle extends Style {
1543        CharSequence mBigText;
1544
1545        public BigTextStyle() {
1546        }
1547
1548        public BigTextStyle(Builder builder) {
1549            setBuilder(builder);
1550        }
1551
1552        /**
1553         * Overrides ContentTitle in the big form of the template.
1554         * This defaults to the value passed to setContentTitle().
1555         */
1556        public BigTextStyle setBigContentTitle(CharSequence title) {
1557            mBigContentTitle = title;
1558            return this;
1559        }
1560
1561        /**
1562         * Set the first line of text after the detail section in the big form of the template.
1563         */
1564        public BigTextStyle setSummaryText(CharSequence cs) {
1565            mSummaryText = cs;
1566            mSummaryTextSet = true;
1567            return this;
1568        }
1569
1570        /**
1571         * Provide the longer text to be displayed in the big form of the
1572         * template in place of the content text.
1573         */
1574        public BigTextStyle bigText(CharSequence cs) {
1575            mBigText = cs;
1576            return this;
1577        }
1578    }
1579
1580    /**
1581     * Helper class for generating large-format notifications that include a list of (up to 5) strings.
1582     *
1583     * <br>
1584     * If the platform does not provide large-format notifications, this method has no effect. The
1585     * user will always see the normal notification view.
1586     * <br>
1587     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1588     * <pre class="prettyprint">
1589     * Notification noti = new Notification.Builder()
1590     *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
1591     *     .setContentText(subject)
1592     *     .setSmallIcon(R.drawable.new_mail)
1593     *     .setLargeIcon(aBitmap)
1594     *     .setStyle(new Notification.InboxStyle()
1595     *         .addLine(str1)
1596     *         .addLine(str2)
1597     *         .setContentTitle(&quot;&quot;)
1598     *         .setSummaryText(&quot;+3 more&quot;))
1599     *     .build();
1600     * </pre>
1601     *
1602     * @see Notification#bigContentView
1603     */
1604    public static class InboxStyle extends Style {
1605        ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
1606
1607        public InboxStyle() {
1608        }
1609
1610        public InboxStyle(Builder builder) {
1611            setBuilder(builder);
1612        }
1613
1614        /**
1615         * Overrides ContentTitle in the big form of the template.
1616         * This defaults to the value passed to setContentTitle().
1617         */
1618        public InboxStyle setBigContentTitle(CharSequence title) {
1619            mBigContentTitle = title;
1620            return this;
1621        }
1622
1623        /**
1624         * Set the first line of text after the detail section in the big form of the template.
1625         */
1626        public InboxStyle setSummaryText(CharSequence cs) {
1627            mSummaryText = cs;
1628            mSummaryTextSet = true;
1629            return this;
1630        }
1631
1632        /**
1633         * Append a line to the digest section of the Inbox notification.
1634         */
1635        public InboxStyle addLine(CharSequence cs) {
1636            mTexts.add(cs);
1637            return this;
1638        }
1639    }
1640
1641    /**
1642     * Structure to encapsulate a named action that can be shown as part of this notification.
1643     * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1644     * selected by the user. Action buttons won't appear on platforms prior to Android 4.1.
1645     * <p>
1646     * Apps should use {@link NotificationCompat.Builder#addAction(int, CharSequence, PendingIntent)}
1647     * or {@link NotificationCompat.Builder#addAction(NotificationCompat.Action)}
1648     * to attach actions.
1649     */
1650    public static class Action extends NotificationCompatBase.Action {
1651        private final Bundle mExtras;
1652        private final RemoteInput[] mRemoteInputs;
1653
1654        /**
1655         * Small icon representing the action.
1656         */
1657        public int icon;
1658        /**
1659         * Title of the action.
1660         */
1661        public CharSequence title;
1662        /**
1663         * Intent to send when the user invokes this action. May be null, in which case the action
1664         * may be rendered in a disabled presentation.
1665         */
1666        public PendingIntent actionIntent;
1667
1668        public Action(int icon, CharSequence title, PendingIntent intent) {
1669            this(icon, title, intent, new Bundle(), null);
1670        }
1671
1672        private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
1673                RemoteInput[] remoteInputs) {
1674            this.icon = icon;
1675            this.title = title;
1676            this.actionIntent = intent;
1677            this.mExtras = extras != null ? extras : new Bundle();
1678            this.mRemoteInputs = remoteInputs;
1679        }
1680
1681        @Override
1682        protected int getIcon() {
1683            return icon;
1684        }
1685
1686        @Override
1687        protected CharSequence getTitle() {
1688            return title;
1689        }
1690
1691        @Override
1692        protected PendingIntent getActionIntent() {
1693            return actionIntent;
1694        }
1695
1696        /**
1697         * Get additional metadata carried around with this Action.
1698         */
1699        public Bundle getExtras() {
1700            return mExtras;
1701        }
1702
1703        /**
1704         * Get the list of inputs to be collected from the user when this action is sent.
1705         * May return null if no remote inputs were added.
1706         */
1707        public RemoteInput[] getRemoteInputs() {
1708            return mRemoteInputs;
1709        }
1710
1711        /**
1712         * Builder class for {@link Action} objects.
1713         */
1714        public static final class Builder {
1715            private final int mIcon;
1716            private final CharSequence mTitle;
1717            private final PendingIntent mIntent;
1718            private final Bundle mExtras;
1719            private ArrayList<RemoteInput> mRemoteInputs;
1720
1721            /**
1722             * Construct a new builder for {@link Action} object.
1723             * @param icon icon to show for this action
1724             * @param title the title of the action
1725             * @param intent the {@link PendingIntent} to fire when users trigger this action
1726             */
1727            public Builder(int icon, CharSequence title, PendingIntent intent) {
1728                this(icon, title, intent, new Bundle());
1729            }
1730
1731            /**
1732             * Construct a new builder for {@link Action} object using the fields from an
1733             * {@link Action}.
1734             * @param action the action to read fields from.
1735             */
1736            public Builder(Action action) {
1737                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras));
1738            }
1739
1740            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
1741                mIcon = icon;
1742                mTitle = title;
1743                mIntent = intent;
1744                mExtras = extras;
1745            }
1746
1747            /**
1748             * Merge additional metadata into this builder.
1749             *
1750             * <p>Values within the Bundle will replace existing extras values in this Builder.
1751             *
1752             * @see NotificationCompat.Action#getExtras
1753             */
1754            public Builder addExtras(Bundle extras) {
1755                if (extras != null) {
1756                    mExtras.putAll(extras);
1757                }
1758                return this;
1759            }
1760
1761            /**
1762             * Get the metadata Bundle used by this Builder.
1763             *
1764             * <p>The returned Bundle is shared with this Builder.
1765             */
1766            public Bundle getExtras() {
1767                return mExtras;
1768            }
1769
1770            /**
1771             * Add an input to be collected from the user when this action is sent.
1772             * Response values can be retrieved from the fired intent by using the
1773             * {@link RemoteInput#getResultsFromIntent} function.
1774             * @param remoteInput a {@link RemoteInput} to add to the action
1775             * @return this object for method chaining
1776             */
1777            public Builder addRemoteInput(RemoteInput remoteInput) {
1778                if (mRemoteInputs == null) {
1779                    mRemoteInputs = new ArrayList<RemoteInput>();
1780                }
1781                mRemoteInputs.add(remoteInput);
1782                return this;
1783            }
1784
1785            /**
1786             * Apply an extender to this action builder. Extenders may be used to add
1787             * metadata or change options on this builder.
1788             */
1789            public Builder extend(Extender extender) {
1790                extender.extend(this);
1791                return this;
1792            }
1793
1794            /**
1795             * Combine all of the options that have been set and return a new {@link Action}
1796             * object.
1797             * @return the built action
1798             */
1799            public Action build() {
1800                RemoteInput[] remoteInputs = mRemoteInputs != null
1801                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1802                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
1803            }
1804        }
1805
1806
1807        /**
1808         * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1809         * metadata or change options on an action builder.
1810         */
1811        public interface Extender {
1812            /**
1813             * Apply this extender to a notification action builder.
1814             * @param builder the builder to be modified.
1815             * @return the build object for chaining.
1816             */
1817            public Builder extend(Builder builder);
1818        }
1819
1820        /**
1821         * Wearable extender for notification actions. To add extensions to an action,
1822         * create a new {@link NotificationCompat.Action.WearableExtender} object using
1823         * the {@code WearableExtender()} constructor and apply it to a
1824         * {@link NotificationCompat.Action.Builder} using
1825         * {@link NotificationCompat.Action.Builder#extend}.
1826         *
1827         * <pre class="prettyprint">
1828         * NotificationCompat.Action action = new NotificationCompat.Action.Builder(
1829         *         R.drawable.archive_all, "Archive all", actionIntent)
1830         *         .extend(new NotificationCompat.Action.WearableExtender()
1831         *                 .setAvailableOffline(false))
1832         *         .build();</pre>
1833         */
1834        public static final class WearableExtender implements Extender {
1835            /** Notification action extra which contains wearable extensions */
1836            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1837
1838            private static final String KEY_FLAGS = "flags";
1839
1840            // Flags bitwise-ored to mFlags
1841            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1842
1843            // Default value for flags integer
1844            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1845
1846            private int mFlags = DEFAULT_FLAGS;
1847
1848            /**
1849             * Create a {@link NotificationCompat.Action.WearableExtender} with default
1850             * options.
1851             */
1852            public WearableExtender() {
1853            }
1854
1855            /**
1856             * Create a {@link NotificationCompat.Action.WearableExtender} by reading
1857             * wearable options present in an existing notification action.
1858             * @param action the notification action to inspect.
1859             */
1860            public WearableExtender(Action action) {
1861                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1862                if (wearableBundle != null) {
1863                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1864                }
1865            }
1866
1867            /**
1868             * Apply wearable extensions to a notification action that is being built. This is
1869             * typically called by the {@link NotificationCompat.Action.Builder#extend}
1870             * method of {@link NotificationCompat.Action.Builder}.
1871             */
1872            @Override
1873            public Action.Builder extend(Action.Builder builder) {
1874                Bundle wearableBundle = new Bundle();
1875
1876                if (mFlags != DEFAULT_FLAGS) {
1877                    wearableBundle.putInt(KEY_FLAGS, mFlags);
1878                }
1879
1880                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1881                return builder;
1882            }
1883
1884            @Override
1885            public WearableExtender clone() {
1886                WearableExtender that = new WearableExtender();
1887                that.mFlags = this.mFlags;
1888                return that;
1889            }
1890
1891            /**
1892             * Set whether this action is available when the wearable device is not connected to
1893             * a companion device. The user can still trigger this action when the wearable device
1894             * is offline, but a visual hint will indicate that the action may not be available.
1895             * Defaults to true.
1896             */
1897            public WearableExtender setAvailableOffline(boolean availableOffline) {
1898                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1899                return this;
1900            }
1901
1902            /**
1903             * Get whether this action is available when the wearable device is not connected to
1904             * a companion device. The user can still trigger this action when the wearable device
1905             * is offline, but a visual hint will indicate that the action may not be available.
1906             * Defaults to true.
1907             */
1908            public boolean isAvailableOffline() {
1909                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1910            }
1911
1912            private void setFlag(int mask, boolean value) {
1913                if (value) {
1914                    mFlags |= mask;
1915                } else {
1916                    mFlags &= ~mask;
1917                }
1918            }
1919        }
1920
1921        /** @hide */
1922        public static final Factory FACTORY = new Factory() {
1923            @Override
1924            public Action build(int icon, CharSequence title,
1925                    PendingIntent actionIntent, Bundle extras,
1926                    RemoteInputCompatBase.RemoteInput[] remoteInputs) {
1927                return new Action(icon, title, actionIntent, extras,
1928                        (RemoteInput[]) remoteInputs);
1929            }
1930
1931            @Override
1932            public Action[] newArray(int length) {
1933                return new Action[length];
1934            }
1935        };
1936    }
1937
1938
1939    /**
1940     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1941     * metadata or change options on a notification builder.
1942     */
1943    public interface Extender {
1944        /**
1945         * Apply this extender to a notification builder.
1946         * @param builder the builder to be modified.
1947         * @return the build object for chaining.
1948         */
1949        public Builder extend(Builder builder);
1950    }
1951
1952    /**
1953     * Helper class to add wearable extensions to notifications.
1954     * <p class="note"> See
1955     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
1956     * for Android Wear</a> for more information on how to use this class.
1957     * <p>
1958     * To create a notification with wearable extensions:
1959     * <ol>
1960     *   <li>Create a {@link NotificationCompat.Builder}, setting any desired
1961     *   properties.
1962     *   <li>Create a {@link NotificationCompat.WearableExtender}.
1963     *   <li>Set wearable-specific properties using the
1964     *   {@code add} and {@code set} methods of {@link NotificationCompat.WearableExtender}.
1965     *   <li>Call {@link NotificationCompat.Builder#extend} to apply the extensions to a
1966     *   notification.
1967     *   <li>Post the notification to the notification
1968     *   system with the {@code NotificationManagerCompat.notify(...)} methods
1969     *   and not the {@code NotificationManager.notify(...)} methods.
1970     * </ol>
1971     *
1972     * <pre class="prettyprint">
1973     * Notification notif = new NotificationCompat.Builder(mContext)
1974     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
1975     *         .setContentText(subject)
1976     *         .setSmallIcon(R.drawable.new_mail)
1977     *         .extend(new NotificationCompat.WearableExtender()
1978     *                 .setContentIcon(R.drawable.new_mail))
1979     *         .build();
1980     * NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
1981     *
1982     * <p>Wearable extensions can be accessed on an existing notification by using the
1983     * {@code WearableExtender(Notification)} constructor,
1984     * and then using the {@code get} methods to access values.
1985     *
1986     * <pre class="prettyprint">
1987     * NotificationCompat.WearableExtender wearableExtender =
1988     *         new NotificationCompat.WearableExtender(notification);
1989     * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
1990     */
1991    public static final class WearableExtender implements Extender {
1992        /**
1993         * Sentinel value for an action index that is unset.
1994         */
1995        public static final int UNSET_ACTION_INDEX = -1;
1996
1997        /**
1998         * Size value for use with {@link #setCustomSizePreset} to show this notification with
1999         * default sizing.
2000         * <p>For custom display notifications created using {@link #setDisplayIntent},
2001         * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
2002         * on their content.
2003         */
2004        public static final int SIZE_DEFAULT = 0;
2005
2006        /**
2007         * Size value for use with {@link #setCustomSizePreset} to show this notification
2008         * with an extra small size.
2009         * <p>This value is only applicable for custom display notifications created using
2010         * {@link #setDisplayIntent}.
2011         */
2012        public static final int SIZE_XSMALL = 1;
2013
2014        /**
2015         * Size value for use with {@link #setCustomSizePreset} to show this notification
2016         * with a small size.
2017         * <p>This value is only applicable for custom display notifications created using
2018         * {@link #setDisplayIntent}.
2019         */
2020        public static final int SIZE_SMALL = 2;
2021
2022        /**
2023         * Size value for use with {@link #setCustomSizePreset} to show this notification
2024         * with a medium size.
2025         * <p>This value is only applicable for custom display notifications created using
2026         * {@link #setDisplayIntent}.
2027         */
2028        public static final int SIZE_MEDIUM = 3;
2029
2030        /**
2031         * Size value for use with {@link #setCustomSizePreset} to show this notification
2032         * with a large size.
2033         * <p>This value is only applicable for custom display notifications created using
2034         * {@link #setDisplayIntent}.
2035         */
2036        public static final int SIZE_LARGE = 4;
2037
2038        /**
2039         * Size value for use with {@link #setCustomSizePreset} to show this notification
2040         * full screen.
2041         * <p>This value is only applicable for custom display notifications created using
2042         * {@link #setDisplayIntent}.
2043         */
2044        public static final int SIZE_FULL_SCREEN = 5;
2045
2046        /** Notification extra which contains wearable extensions */
2047        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
2048
2049        // Keys within EXTRA_WEARABLE_OPTIONS for wearable options.
2050        private static final String KEY_ACTIONS = "actions";
2051        private static final String KEY_FLAGS = "flags";
2052        private static final String KEY_DISPLAY_INTENT = "displayIntent";
2053        private static final String KEY_PAGES = "pages";
2054        private static final String KEY_BACKGROUND = "background";
2055        private static final String KEY_CONTENT_ICON = "contentIcon";
2056        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
2057        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
2058        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
2059        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
2060        private static final String KEY_GRAVITY = "gravity";
2061
2062        // Flags bitwise-ored to mFlags
2063        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
2064        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
2065        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
2066        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
2067
2068        // Default value for flags integer
2069        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
2070
2071        private static final int DEFAULT_CONTENT_ICON_GRAVITY = GravityCompat.END;
2072        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
2073
2074        private ArrayList<Action> mActions = new ArrayList<Action>();
2075        private int mFlags = DEFAULT_FLAGS;
2076        private PendingIntent mDisplayIntent;
2077        private ArrayList<Notification> mPages = new ArrayList<Notification>();
2078        private Bitmap mBackground;
2079        private int mContentIcon;
2080        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
2081        private int mContentActionIndex = UNSET_ACTION_INDEX;
2082        private int mCustomSizePreset = SIZE_DEFAULT;
2083        private int mCustomContentHeight;
2084        private int mGravity = DEFAULT_GRAVITY;
2085
2086        /**
2087         * Create a {@link NotificationCompat.WearableExtender} with default
2088         * options.
2089         */
2090        public WearableExtender() {
2091        }
2092
2093        public WearableExtender(Notification notif) {
2094            Bundle extras = getExtras(notif);
2095            Bundle wearableBundle = extras != null ? extras.getBundle(EXTRA_WEARABLE_EXTENSIONS)
2096                    : null;
2097            if (wearableBundle != null) {
2098                Action[] actions = IMPL.getActionsFromParcelableArrayList(
2099                        wearableBundle.getParcelableArrayList(KEY_ACTIONS));
2100                if (actions != null) {
2101                    Collections.addAll(mActions, actions);
2102                }
2103
2104                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
2105                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
2106
2107                Notification[] pages = getNotificationArrayFromBundle(
2108                        wearableBundle, KEY_PAGES);
2109                if (pages != null) {
2110                    Collections.addAll(mPages, pages);
2111                }
2112
2113                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
2114                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
2115                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
2116                        DEFAULT_CONTENT_ICON_GRAVITY);
2117                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
2118                        UNSET_ACTION_INDEX);
2119                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
2120                        SIZE_DEFAULT);
2121                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
2122                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
2123            }
2124        }
2125
2126        /**
2127         * Apply wearable extensions to a notification that is being built. This is typically
2128         * called by the {@link NotificationCompat.Builder#extend} method of
2129         * {@link NotificationCompat.Builder}.
2130         */
2131        @Override
2132        public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
2133            Bundle wearableBundle = new Bundle();
2134
2135            if (!mActions.isEmpty()) {
2136                wearableBundle.putParcelableArrayList(KEY_ACTIONS,
2137                        IMPL.getParcelableArrayListForActions(mActions.toArray(
2138                                new Action[mActions.size()])));
2139            }
2140            if (mFlags != DEFAULT_FLAGS) {
2141                wearableBundle.putInt(KEY_FLAGS, mFlags);
2142            }
2143            if (mDisplayIntent != null) {
2144                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
2145            }
2146            if (!mPages.isEmpty()) {
2147                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
2148                        new Notification[mPages.size()]));
2149            }
2150            if (mBackground != null) {
2151                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
2152            }
2153            if (mContentIcon != 0) {
2154                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
2155            }
2156            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
2157                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
2158            }
2159            if (mContentActionIndex != UNSET_ACTION_INDEX) {
2160                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
2161                        mContentActionIndex);
2162            }
2163            if (mCustomSizePreset != SIZE_DEFAULT) {
2164                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
2165            }
2166            if (mCustomContentHeight != 0) {
2167                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
2168            }
2169            if (mGravity != DEFAULT_GRAVITY) {
2170                wearableBundle.putInt(KEY_GRAVITY, mGravity);
2171            }
2172
2173            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
2174            return builder;
2175        }
2176
2177        @Override
2178        public WearableExtender clone() {
2179            WearableExtender that = new WearableExtender();
2180            that.mActions = new ArrayList<Action>(this.mActions);
2181            that.mFlags = this.mFlags;
2182            that.mDisplayIntent = this.mDisplayIntent;
2183            that.mPages = new ArrayList<Notification>(this.mPages);
2184            that.mBackground = this.mBackground;
2185            that.mContentIcon = this.mContentIcon;
2186            that.mContentIconGravity = this.mContentIconGravity;
2187            that.mContentActionIndex = this.mContentActionIndex;
2188            that.mCustomSizePreset = this.mCustomSizePreset;
2189            that.mCustomContentHeight = this.mCustomContentHeight;
2190            that.mGravity = this.mGravity;
2191            return that;
2192        }
2193
2194        /**
2195         * Add a wearable action to this notification.
2196         *
2197         * <p>When wearable actions are added using this method, the set of actions that
2198         * show on a wearable device splits from devices that only show actions added
2199         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
2200         * of which actions display on different devices.
2201         *
2202         * @param action the action to add to this notification
2203         * @return this object for method chaining
2204         * @see NotificationCompat.Action
2205         */
2206        public WearableExtender addAction(Action action) {
2207            mActions.add(action);
2208            return this;
2209        }
2210
2211        /**
2212         * Adds wearable actions to this notification.
2213         *
2214         * <p>When wearable actions are added using this method, the set of actions that
2215         * show on a wearable device splits from devices that only show actions added
2216         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
2217         * of which actions display on different devices.
2218         *
2219         * @param actions the actions to add to this notification
2220         * @return this object for method chaining
2221         * @see NotificationCompat.Action
2222         */
2223        public WearableExtender addActions(List<Action> actions) {
2224            mActions.addAll(actions);
2225            return this;
2226        }
2227
2228        /**
2229         * Clear all wearable actions present on this builder.
2230         * @return this object for method chaining.
2231         * @see #addAction
2232         */
2233        public WearableExtender clearActions() {
2234            mActions.clear();
2235            return this;
2236        }
2237
2238        /**
2239         * Get the wearable actions present on this notification.
2240         */
2241        public List<Action> getActions() {
2242            return mActions;
2243        }
2244
2245        /**
2246         * Set an intent to launch inside of an activity view when displaying
2247         * this notification. The {@link PendingIntent} provided should be for an activity.
2248         *
2249         * <pre class="prettyprint">
2250         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
2251         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
2252         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
2253         * Notification notif = new NotificationCompat.Builder(context)
2254         *         .extend(new NotificationCompat.WearableExtender()
2255         *                 .setDisplayIntent(displayPendingIntent)
2256         *                 .setCustomSizePreset(NotificationCompat.WearableExtender.SIZE_MEDIUM))
2257         *         .build();</pre>
2258         *
2259         * <p>The activity to launch needs to allow embedding, must be exported, and
2260         * should have an empty task affinity. It is also recommended to use the device
2261         * default light theme.
2262         *
2263         * <p>Example AndroidManifest.xml entry:
2264         * <pre class="prettyprint">
2265         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
2266         *     android:exported=&quot;true&quot;
2267         *     android:allowEmbedded=&quot;true&quot;
2268         *     android:taskAffinity=&quot;&quot;
2269         *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
2270         *
2271         * @param intent the {@link PendingIntent} for an activity
2272         * @return this object for method chaining
2273         * @see NotificationCompat.WearableExtender#getDisplayIntent
2274         */
2275        public WearableExtender setDisplayIntent(PendingIntent intent) {
2276            mDisplayIntent = intent;
2277            return this;
2278        }
2279
2280        /**
2281         * Get the intent to launch inside of an activity view when displaying this
2282         * notification. This {@code PendingIntent} should be for an activity.
2283         */
2284        public PendingIntent getDisplayIntent() {
2285            return mDisplayIntent;
2286        }
2287
2288        /**
2289         * Add an additional page of content to display with this notification. The current
2290         * notification forms the first page, and pages added using this function form
2291         * subsequent pages. This field can be used to separate a notification into multiple
2292         * sections.
2293         *
2294         * @param page the notification to add as another page
2295         * @return this object for method chaining
2296         * @see NotificationCompat.WearableExtender#getPages
2297         */
2298        public WearableExtender addPage(Notification page) {
2299            mPages.add(page);
2300            return this;
2301        }
2302
2303        /**
2304         * Add additional pages of content to display with this notification. The current
2305         * notification forms the first page, and pages added using this function form
2306         * subsequent pages. This field can be used to separate a notification into multiple
2307         * sections.
2308         *
2309         * @param pages a list of notifications
2310         * @return this object for method chaining
2311         * @see NotificationCompat.WearableExtender#getPages
2312         */
2313        public WearableExtender addPages(List<Notification> pages) {
2314            mPages.addAll(pages);
2315            return this;
2316        }
2317
2318        /**
2319         * Clear all additional pages present on this builder.
2320         * @return this object for method chaining.
2321         * @see #addPage
2322         */
2323        public WearableExtender clearPages() {
2324            mPages.clear();
2325            return this;
2326        }
2327
2328        /**
2329         * Get the array of additional pages of content for displaying this notification. The
2330         * current notification forms the first page, and elements within this array form
2331         * subsequent pages. This field can be used to separate a notification into multiple
2332         * sections.
2333         * @return the pages for this notification
2334         */
2335        public List<Notification> getPages() {
2336            return mPages;
2337        }
2338
2339        /**
2340         * Set a background image to be displayed behind the notification content.
2341         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
2342         * will work with any notification style.
2343         *
2344         * @param background the background bitmap
2345         * @return this object for method chaining
2346         * @see NotificationCompat.WearableExtender#getBackground
2347         */
2348        public WearableExtender setBackground(Bitmap background) {
2349            mBackground = background;
2350            return this;
2351        }
2352
2353        /**
2354         * Get a background image to be displayed behind the notification content.
2355         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
2356         * will work with any notification style.
2357         *
2358         * @return the background image
2359         * @see NotificationCompat.WearableExtender#setBackground
2360         */
2361        public Bitmap getBackground() {
2362            return mBackground;
2363        }
2364
2365        /**
2366         * Set an icon that goes with the content of this notification.
2367         */
2368        public WearableExtender setContentIcon(int icon) {
2369            mContentIcon = icon;
2370            return this;
2371        }
2372
2373        /**
2374         * Get an icon that goes with the content of this notification.
2375         */
2376        public int getContentIcon() {
2377            return mContentIcon;
2378        }
2379
2380        /**
2381         * Set the gravity that the content icon should have within the notification display.
2382         * Supported values include {@link android.view.Gravity#START} and
2383         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
2384         * @see #setContentIcon
2385         */
2386        public WearableExtender setContentIconGravity(int contentIconGravity) {
2387            mContentIconGravity = contentIconGravity;
2388            return this;
2389        }
2390
2391        /**
2392         * Get the gravity that the content icon should have within the notification display.
2393         * Supported values include {@link android.view.Gravity#START} and
2394         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
2395         * @see #getContentIcon
2396         */
2397        public int getContentIconGravity() {
2398            return mContentIconGravity;
2399        }
2400
2401        /**
2402         * Set an action from this notification's actions to be clickable with the content of
2403         * this notification. This action will no longer display separately from the
2404         * notification's content.
2405         *
2406         * <p>For notifications with multiple pages, child pages can also have content actions
2407         * set, although the list of available actions comes from the main notification and not
2408         * from the child page's notification.
2409         *
2410         * @param actionIndex The index of the action to hoist onto the current notification page.
2411         *                    If wearable actions were added to the main notification, this index
2412         *                    will apply to that list, otherwise it will apply to the regular
2413         *                    actions list.
2414         */
2415        public WearableExtender setContentAction(int actionIndex) {
2416            mContentActionIndex = actionIndex;
2417            return this;
2418        }
2419
2420        /**
2421         * Get the index of the notification action, if any, that was specified as being clickable
2422         * with the content of this notification. This action will no longer display separately
2423         * from the notification's content.
2424         *
2425         * <p>For notifications with multiple pages, child pages can also have content actions
2426         * set, although the list of available actions comes from the main notification and not
2427         * from the child page's notification.
2428         *
2429         * <p>If wearable specific actions were added to the main notification, this index will
2430         * apply to that list, otherwise it will apply to the regular actions list.
2431         *
2432         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
2433         */
2434        public int getContentAction() {
2435            return mContentActionIndex;
2436        }
2437
2438        /**
2439         * Set the gravity that this notification should have within the available viewport space.
2440         * Supported values include {@link android.view.Gravity#TOP},
2441         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
2442         * The default value is {@link android.view.Gravity#BOTTOM}.
2443         */
2444        public WearableExtender setGravity(int gravity) {
2445            mGravity = gravity;
2446            return this;
2447        }
2448
2449        /**
2450         * Get the gravity that this notification should have within the available viewport space.
2451         * Supported values include {@link android.view.Gravity#TOP},
2452         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
2453         * The default value is {@link android.view.Gravity#BOTTOM}.
2454         */
2455        public int getGravity() {
2456            return mGravity;
2457        }
2458
2459        /**
2460         * Set the custom size preset for the display of this notification out of the available
2461         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
2462         * {@link #SIZE_LARGE}.
2463         * <p>Some custom size presets are only applicable for custom display notifications created
2464         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. Check the
2465         * documentation for the preset in question. See also
2466         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
2467         */
2468        public WearableExtender setCustomSizePreset(int sizePreset) {
2469            mCustomSizePreset = sizePreset;
2470            return this;
2471        }
2472
2473        /**
2474         * Get the custom size preset for the display of this notification out of the available
2475         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
2476         * {@link #SIZE_LARGE}.
2477         * <p>Some custom size presets are only applicable for custom display notifications created
2478         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
2479         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
2480         */
2481        public int getCustomSizePreset() {
2482            return mCustomSizePreset;
2483        }
2484
2485        /**
2486         * Set the custom height in pixels for the display of this notification's content.
2487         * <p>This option is only available for custom display notifications created
2488         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. See also
2489         * {@link NotificationCompat.WearableExtender#setCustomSizePreset} and
2490         * {@link #getCustomContentHeight}.
2491         */
2492        public WearableExtender setCustomContentHeight(int height) {
2493            mCustomContentHeight = height;
2494            return this;
2495        }
2496
2497        /**
2498         * Get the custom height in pixels for the display of this notification's content.
2499         * <p>This option is only available for custom display notifications created
2500         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
2501         * {@link #setCustomContentHeight}.
2502         */
2503        public int getCustomContentHeight() {
2504            return mCustomContentHeight;
2505        }
2506
2507        /**
2508         * Set whether the scrolling position for the contents of this notification should start
2509         * at the bottom of the contents instead of the top when the contents are too long to
2510         * display within the screen.  Default is false (start scroll at the top).
2511         */
2512        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
2513            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
2514            return this;
2515        }
2516
2517        /**
2518         * Get whether the scrolling position for the contents of this notification should start
2519         * at the bottom of the contents instead of the top when the contents are too long to
2520         * display within the screen. Default is false (start scroll at the top).
2521         */
2522        public boolean getStartScrollBottom() {
2523            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
2524        }
2525
2526        /**
2527         * Set whether the content intent is available when the wearable device is not connected
2528         * to a companion device.  The user can still trigger this intent when the wearable device
2529         * is offline, but a visual hint will indicate that the content intent may not be available.
2530         * Defaults to true.
2531         */
2532        public WearableExtender setContentIntentAvailableOffline(
2533                boolean contentIntentAvailableOffline) {
2534            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
2535            return this;
2536        }
2537
2538        /**
2539         * Get whether the content intent is available when the wearable device is not connected
2540         * to a companion device.  The user can still trigger this intent when the wearable device
2541         * is offline, but a visual hint will indicate that the content intent may not be available.
2542         * Defaults to true.
2543         */
2544        public boolean getContentIntentAvailableOffline() {
2545            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
2546        }
2547
2548        /**
2549         * Set a hint that this notification's icon should not be displayed.
2550         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
2551         * @return this object for method chaining
2552         */
2553        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
2554            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
2555            return this;
2556        }
2557
2558        /**
2559         * Get a hint that this notification's icon should not be displayed.
2560         * @return {@code true} if this icon should not be displayed, false otherwise.
2561         * The default value is {@code false} if this was never set.
2562         */
2563        public boolean getHintHideIcon() {
2564            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
2565        }
2566
2567        /**
2568         * Set a visual hint that only the background image of this notification should be
2569         * displayed, and other semantic content should be hidden. This hint is only applicable
2570         * to sub-pages added using {@link #addPage}.
2571         */
2572        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
2573            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
2574            return this;
2575        }
2576
2577        /**
2578         * Get a visual hint that only the background image of this notification should be
2579         * displayed, and other semantic content should be hidden. This hint is only applicable
2580         * to sub-pages added using {@link NotificationCompat.WearableExtender#addPage}.
2581         */
2582        public boolean getHintShowBackgroundOnly() {
2583            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
2584        }
2585
2586        private void setFlag(int mask, boolean value) {
2587            if (value) {
2588                mFlags |= mask;
2589            } else {
2590                mFlags &= ~mask;
2591            }
2592        }
2593    }
2594
2595    /**
2596     * Get an array of Notification objects from a parcelable array bundle field.
2597     * Update the bundle to have a typed array so fetches in the future don't need
2598     * to do an array copy.
2599     */
2600    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
2601        Parcelable[] array = bundle.getParcelableArray(key);
2602        if (array instanceof Notification[] || array == null) {
2603            return (Notification[]) array;
2604        }
2605        Notification[] typedArray = new Notification[array.length];
2606        for (int i = 0; i < array.length; i++) {
2607            typedArray[i] = (Notification) array[i];
2608        }
2609        bundle.putParcelableArray(key, typedArray);
2610        return typedArray;
2611    }
2612
2613    /**
2614     * Gets the {@link Notification#extras} field from a notification in a backwards
2615     * compatible manner. Extras field was supported from JellyBean (Api level 16)
2616     * forwards. This function will return null on older api levels.
2617     */
2618    public static Bundle getExtras(Notification notif) {
2619        return IMPL.getExtras(notif);
2620    }
2621
2622    /**
2623     * Get the number of actions in this notification in a backwards compatible
2624     * manner. Actions were supported from JellyBean (Api level 16) forwards.
2625     */
2626    public static int getActionCount(Notification notif) {
2627        return IMPL.getActionCount(notif);
2628    }
2629
2630    /**
2631     * Get an action on this notification in a backwards compatible
2632     * manner. Actions were supported from JellyBean (Api level 16) forwards.
2633     * @param notif The notification to inspect.
2634     * @param actionIndex The index of the action to retrieve.
2635     */
2636    public static Action getAction(Notification notif, int actionIndex) {
2637        return IMPL.getAction(notif, actionIndex);
2638    }
2639
2640    /**
2641    * Get the category of this notification in a backwards compatible
2642    * manner.
2643    * @param notif The notification to inspect.
2644    */
2645    public static String getCategory(Notification notif) {
2646        return IMPL.getCategory(notif);
2647    }
2648
2649    /**
2650     * Get whether or not this notification is only relevant to the current device.
2651     *
2652     * <p>Some notifications can be bridged to other devices for remote display.
2653     * If this hint is set, it is recommend that this notification not be bridged.
2654     */
2655    public static boolean getLocalOnly(Notification notif) {
2656        return IMPL.getLocalOnly(notif);
2657    }
2658
2659    /**
2660     * Get the key used to group this notification into a cluster or stack
2661     * with other notifications on devices which support such rendering.
2662     */
2663    public static String getGroup(Notification notif) {
2664        return IMPL.getGroup(notif);
2665    }
2666
2667    /**
2668     * Get whether this notification to be the group summary for a group of notifications.
2669     * Grouped notifications may display in a cluster or stack on devices which
2670     * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
2671     * @return Whether this notification is a group summary.
2672     */
2673    public static boolean isGroupSummary(Notification notif) {
2674        return IMPL.isGroupSummary(notif);
2675    }
2676
2677    /**
2678     * Get a sort key that orders this notification among other notifications from the
2679     * same package. This can be useful if an external sort was already applied and an app
2680     * would like to preserve this. Notifications will be sorted lexicographically using this
2681     * value, although providing different priorities in addition to providing sort key may
2682     * cause this value to be ignored.
2683     *
2684     * <p>This sort key can also be used to order members of a notification group. See
2685     * {@link Builder#setGroup}.
2686     *
2687     * @see String#compareTo(String)
2688     */
2689    public static String getSortKey(Notification notif) {
2690        return IMPL.getSortKey(notif);
2691    }
2692}
2693