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