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