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