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