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