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