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