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