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