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