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