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 
17 package android.support.v4.app;
18 
19 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20 
21 import static java.lang.annotation.RetentionPolicy.SOURCE;
22 
23 import android.app.Activity;
24 import android.app.Notification;
25 import android.app.PendingIntent;
26 import android.content.Context;
27 import android.graphics.Bitmap;
28 import android.graphics.Color;
29 import android.media.AudioManager;
30 import android.net.Uri;
31 import android.os.Build;
32 import android.os.Bundle;
33 import android.os.Parcelable;
34 import android.support.annotation.ColorInt;
35 import android.support.annotation.IntDef;
36 import android.support.annotation.NonNull;
37 import android.support.annotation.RequiresApi;
38 import android.support.annotation.RestrictTo;
39 import android.support.v4.os.BuildCompat;
40 import android.support.v4.view.GravityCompat;
41 import android.view.Gravity;
42 import android.widget.RemoteViews;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Collections;
49 import 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  */
55 public 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 {
build(Builder b, BuilderExtender extender)584         Notification build(Builder b, BuilderExtender extender);
getAction(Notification n, int actionIndex)585         Action getAction(Notification n, int actionIndex);
getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables)586         Action[] getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables);
getParcelableArrayListForActions(Action[] actions)587         ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions);
getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc)588         Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc);
getUnreadConversationFromBundle( Bundle b, NotificationCompatBase.UnreadConversation.Factory factory, RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory)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 {
build(Builder b, NotificationBuilderWithBuilderAccessor builder)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 
BuilderBase(Context context, Notification n, CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo, RemoteViews tickerView, int number, PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon, int progressMax, int progress, boolean progressIndeterminate)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
getBuilder()646             public Notification.Builder getBuilder() {
647                 return mBuilder;
648             }
649 
650             @Override
build()651             public Notification build() {
652                 return mBuilder.getNotification();
653             }
654         }
655 
656         @Override
build(Builder b, BuilderExtender extender)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
getAction(Notification n, int actionIndex)667         public Action getAction(Notification n, int actionIndex) {
668             return null;
669         }
670 
671         @Override
getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables)672         public Action[] getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables) {
673             return null;
674         }
675 
676         @Override
getParcelableArrayListForActions(Action[] actions)677         public ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions) {
678             return null;
679         }
680 
681         @Override
getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc)682         public Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) {
683             return null;
684         }
685 
686         @Override
getUnreadConversationFromBundle( Bundle b, NotificationCompatBase.UnreadConversation.Factory factory, RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory)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
build(Builder b, BuilderExtender extender)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
getAction(Notification n, int actionIndex)717         public Action getAction(Notification n, int actionIndex) {
718             return (Action) NotificationCompatJellybean.getAction(n, actionIndex, Action.FACTORY,
719                     RemoteInput.FACTORY);
720         }
721 
722         @Override
getActionsFromParcelableArrayList( ArrayList<Parcelable> parcelables)723         public Action[] getActionsFromParcelableArrayList(
724                 ArrayList<Parcelable> parcelables) {
725             return (Action[]) NotificationCompatJellybean.getActionsFromParcelableArrayList(
726                     parcelables, Action.FACTORY, RemoteInput.FACTORY);
727         }
728 
729         @Override
getParcelableArrayListForActions( Action[] actions)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
build(Builder b, BuilderExtender extender)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
getAction(Notification n, int actionIndex)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
build(Builder b, BuilderExtender extender)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
getAction(Notification n, int actionIndex)780         public Action getAction(Notification n, int actionIndex) {
781             return (Action) NotificationCompatApi20.getAction(n, actionIndex, Action.FACTORY,
782                     RemoteInput.FACTORY);
783         }
784 
785         @Override
getActionsFromParcelableArrayList( ArrayList<Parcelable> parcelables)786         public Action[] getActionsFromParcelableArrayList(
787                 ArrayList<Parcelable> parcelables) {
788             return (Action[]) NotificationCompatApi20.getActionsFromParcelableArrayList(
789                     parcelables, Action.FACTORY, RemoteInput.FACTORY);
790         }
791 
792         @Override
getParcelableArrayListForActions( Action[] actions)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
build(Builder b, BuilderExtender extender)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
getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc)821         public Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) {
822             return NotificationCompatApi21.getBundleForUnreadConversation(uc);
823         }
824 
825         @Override
getUnreadConversationFromBundle( Bundle b, NotificationCompatBase.UnreadConversation.Factory factory, RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory)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
build(Builder b, BuilderExtender extender)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
build(Builder b, BuilderExtender extender)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 
addActionsToBuilder(NotificationBuilderWithActions builder, ArrayList<Action> actions)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)
addStyleToBuilderJellybean(NotificationBuilderWithBuilderAccessor builder, Style style)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)
addStyleToBuilderApi24(NotificationBuilderWithBuilderAccessor builder, Style style)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          */
Builder(@onNull Context context, @NonNull String channelId)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
Builder(Context context)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          */
setWhen(long when)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          */
setShowWhen(boolean show)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          */
setUsesChronometer(boolean b)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          */
setSmallIcon(int icon)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          */
setSmallIcon(int icon, int level)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          */
setContentTitle(CharSequence title)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          */
setContentText(CharSequence text)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          */
setSubText(CharSequence text)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          */
setRemoteInputHistory(CharSequence[] text)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          */
setNumber(int number)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          */
setContentInfo(CharSequence info)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          */
setProgress(int max, int progress, boolean indeterminate)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          */
setContent(RemoteViews views)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          */
setContentIntent(PendingIntent intent)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          */
setDeleteIntent(PendingIntent intent)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          */
setFullScreenIntent(PendingIntent intent, boolean highPriority)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          */
setTicker(CharSequence tickerText)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          */
setTicker(CharSequence tickerText, RemoteViews views)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          */
setLargeIcon(Bitmap icon)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          */
setSound(Uri sound)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          */
setSound(Uri sound, int streamType)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          */
setVibrate(long[] pattern)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          */
setLights(@olorInt int argb, int onMs, int offMs)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          */
setOngoing(boolean ongoing)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          */
setColorized(boolean colorize)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          */
setOnlyAlertOnce(boolean onlyAlertOnce)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          */
setAutoCancel(boolean autoCancel)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          */
setLocalOnly(boolean b)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          */
setCategory(String category)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          */
setDefaults(int defaults)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 
setFlag(int mask, boolean value)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          */
setPriority(int pri)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          */
addPerson(String uri)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          */
setGroup(String groupKey)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          */
setGroupSummary(boolean isGroupSummary)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          */
setSortKey(String sortKey)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          */
addExtras(Bundle extras)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          */
setExtras(Bundle extras)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          */
getExtras()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          */
addAction(int icon, CharSequence title, PendingIntent intent)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          */
addAction(Action action)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          */
setStyle(Style style)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          */
setColor(@olorInt int argb)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          */
setVisibility(@otificationVisibility int visibility)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          */
setPublicVersion(Notification n)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          */
setCustomContentView(RemoteViews contentView)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          */
setCustomBigContentView(RemoteViews contentView)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          */
setCustomHeadsUpContentView(RemoteViews contentView)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          */
setChannelId(@onNull String channelId)1778         public Builder setChannelId(@NonNull String channelId) {
1779             mChannelId = channelId;
1780             return this;
1781         }
1782 
1783         /** @deprecated removed from API 26 */
1784         @Deprecated
setChannel(@onNull String channelId)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          */
setTimeoutAfter(long durationMs)1793         public Builder setTimeoutAfter(long durationMs) {
1794             mTimeout = durationMs;
1795             return this;
1796         }
1797 
1798         /** @deprecated removed from API 26 */
1799         @Deprecated
setTimeout(long durationMs)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          */
setShortcutId(String shortcutId)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          */
setBadgeIconType(@adgeIconType int icon)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          */
setGroupAlertBehavior(int groupAlertBehavior)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          */
extend(Extender extender)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
getNotification()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          */
build()1868         public Notification build() {
1869             return IMPL.build(this, getExtender());
1870         }
1871 
1872         /**
1873          * @hide
1874          */
1875         @RestrictTo(LIBRARY_GROUP)
getExtender()1876         protected BuilderExtender getExtender() {
1877             return new BuilderExtender();
1878         }
1879 
limitCharSequenceLength(CharSequence cs)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)
getContentView()1892         public RemoteViews getContentView() {
1893             return mContentView;
1894         }
1895 
1896         /**
1897          * @hide
1898          */
1899         @RestrictTo(LIBRARY_GROUP)
getBigContentView()1900         public RemoteViews getBigContentView() {
1901             return mBigContentView;
1902         }
1903 
1904         /**
1905          * @hide
1906          */
1907         @RestrictTo(LIBRARY_GROUP)
getHeadsUpContentView()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)
getWhenIfShowing()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)
getPriority()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)
getColor()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)
resolveText()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)
resolveTitle()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 
setBuilder(Builder builder)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 
build()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
addCompatExtras(Bundle extras)1999         public void addCompatExtras(Bundle extras) {
2000         }
2001 
2002         /**
2003          * @hide
2004          */
2005         @RestrictTo(LIBRARY_GROUP)
2006         // TODO: implement for all styles
restoreFromCompatExtras(Bundle extras)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 
BigPictureStyle()2036         public BigPictureStyle() {
2037         }
2038 
BigPictureStyle(Builder builder)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          */
setBigContentTitle(CharSequence title)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          */
setSummaryText(CharSequence cs)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          */
bigPicture(Bitmap b)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          */
bigLargeIcon(Bitmap b)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 
BigTextStyle()2103         public BigTextStyle() {
2104         }
2105 
BigTextStyle(Builder builder)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          */
setBigContentTitle(CharSequence title)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          */
setSummaryText(CharSequence cs)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          */
bigText(CharSequence cs)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 
MessagingStyle()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          */
MessagingStyle(@onNull CharSequence userDisplayName)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          */
getUserDisplayName()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          */
setConversationTitle(CharSequence conversationTitle)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          */
getConversationTitle()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          */
addMessage(CharSequence text, long timestamp, CharSequence sender)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          */
addMessage(Message message)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          */
getMessages()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          */
extractMessagingStyleFromNotification( Notification notification)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
addCompatExtras(Bundle extras)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
restoreFromCompatExtras(Bundle extras)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              */
Message(CharSequence text, long timestamp, CharSequence sender)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              */
setData(String dataMimeType, Uri dataUri)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              */
getText()2387             public CharSequence getText() {
2388                 return mText;
2389             }
2390 
2391             /**
2392              * Get the time at which this message arrived
2393              */
getTimestamp()2394             public long getTimestamp() {
2395                 return mTimestamp;
2396             }
2397 
2398             /**
2399              * Get the extras Bundle for this message.
2400              */
getExtras()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              */
getSender()2408             public CharSequence getSender() {
2409                 return mSender;
2410             }
2411 
2412             /**
2413              * Get the MIME type of the data pointed to by the Uri
2414              */
getDataMimeType()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              */
getDataUri()2423             public Uri getDataUri() {
2424                 return mDataUri;
2425             }
2426 
toBundle()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 
getBundleArrayForMessages(List<Message> messages)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 
getMessagesFromBundleArray(Parcelable[] bundles)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 
getMessageFromBundle(Bundle bundle)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 
InboxStyle()2521         public InboxStyle() {
2522         }
2523 
InboxStyle(Builder builder)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          */
setBigContentTitle(CharSequence title)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          */
setSummaryText(CharSequence cs)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          */
addLine(CharSequence cs)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 
Action(int icon, CharSequence title, PendingIntent intent)2596         public Action(int icon, CharSequence title, PendingIntent intent) {
2597             this(icon, title, intent, new Bundle(), null, null, true);
2598         }
2599 
Action(int icon, CharSequence title, PendingIntent intent, Bundle extras, RemoteInput[] remoteInputs, RemoteInput[] dataOnlyRemoteInputs, boolean allowGeneratedReplies)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
getIcon()2613         public int getIcon() {
2614             return icon;
2615         }
2616 
2617         @Override
getTitle()2618         public CharSequence getTitle() {
2619             return title;
2620         }
2621 
2622         @Override
getActionIntent()2623         public PendingIntent getActionIntent() {
2624             return actionIntent;
2625         }
2626 
2627         /**
2628          * Get additional metadata carried around with this Action.
2629          */
2630         @Override
getExtras()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
getAllowGeneratedReplies()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
getRemoteInputs()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
getDataOnlyRemoteInputs()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              */
Builder(int icon, CharSequence title, PendingIntent intent)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              */
Builder(Action action)2695             public Builder(Action action) {
2696                 this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
2697                         action.getRemoteInputs(), action.getAllowGeneratedReplies());
2698             }
2699 
Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras, RemoteInput[] remoteInputs, boolean allowGeneratedReplies)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              */
addExtras(Bundle extras)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              */
getExtras()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              */
addRemoteInput(RemoteInput remoteInput)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              */
setAllowGeneratedReplies(boolean allowGeneratedReplies)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              */
extend(Extender extender)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              */
build()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              */
extend(Builder builder)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              */
WearableExtender()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              */
WearableExtender(Action action)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
extend(Action.Builder builder)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
clone()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              */
setAvailableOffline(boolean availableOffline)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              */
isAvailableOffline()2925             public boolean isAvailableOffline() {
2926                 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
2927             }
2928 
setFlag(int mask, boolean value)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              */
setInProgressLabel(CharSequence label)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              */
getInProgressLabel()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              */
setConfirmLabel(CharSequence label)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              */
getConfirmLabel()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              */
setCancelLabel(CharSequence label)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              */
getCancelLabel()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              */
setHintLaunchesActivity( boolean hintLaunchesActivity)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              */
getHintLaunchesActivity()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              */
setHintDisplayActionInline( boolean hintDisplayInline)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              */
getHintDisplayActionInline()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          */
extend(Builder builder)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          */
WearableExtender()3250         public WearableExtender() {
3251         }
3252 
WearableExtender(Notification notification)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
extend(NotificationCompat.Builder builder)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
clone()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          */
addAction(Action action)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          */
addActions(List<Action> actions)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          */
clearActions()3408         public WearableExtender clearActions() {
3409             mActions.clear();
3410             return this;
3411         }
3412 
3413         /**
3414          * Get the wearable actions present on this notification.
3415          */
getActions()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          */
setDisplayIntent(PendingIntent intent)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          */
getDisplayIntent()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          */
addPage(Notification page)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          */
addPages(List<Notification> pages)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          */
clearPages()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          */
getPages()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          */
setBackground(Bitmap background)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          */
getBackground()3536         public Bitmap getBackground() {
3537             return mBackground;
3538         }
3539 
3540         /**
3541          * Set an icon that goes with the content of this notification.
3542          */
setContentIcon(int icon)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          */
getContentIcon()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          */
setContentIconGravity(int contentIconGravity)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          */
getContentIconGravity()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          */
setContentAction(int actionIndex)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          */
getContentAction()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          */
setGravity(int gravity)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          */
getGravity()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          */
setCustomSizePreset(int sizePreset)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          */
getCustomSizePreset()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          */
setCustomContentHeight(int height)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          */
getCustomContentHeight()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          */
setStartScrollBottom(boolean startScrollBottom)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          */
getStartScrollBottom()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          */
setContentIntentAvailableOffline( boolean contentIntentAvailableOffline)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          */
getContentIntentAvailableOffline()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          */
setHintHideIcon(boolean hintHideIcon)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          */
getHintHideIcon()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          */
setHintShowBackgroundOnly(boolean hintShowBackgroundOnly)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          */
getHintShowBackgroundOnly()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          */
setHintAvoidBackgroundClipping( boolean hintAvoidBackgroundClipping)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          */
getHintAvoidBackgroundClipping()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          */
setHintScreenTimeout(int timeout)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          */
getHintScreenTimeout()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          */
setHintAmbientBigPicture(boolean hintAmbientBigPicture)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          */
getHintAmbientBigPicture()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          */
setHintContentIntentLaunchesActivity( boolean hintContentIntentLaunchesActivity)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          */
getHintContentIntentLaunchesActivity()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          */
setDismissalId(String dismissalId)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          */
getDismissalId()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          */
setBridgeTag(String bridgeTag)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          */
getBridgeTag()3892         public String getBridgeTag() {
3893             return mBridgeTag;
3894         }
3895 
setFlag(int mask, boolean value)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          */
CarExtender()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          */
CarExtender(Notification notification)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
extend(NotificationCompat.Builder builder)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          */
setColor(@olorInt int color)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
getColor()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          */
setLargeIcon(Bitmap largeIcon)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          */
getLargeIcon()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          */
setUnreadConversation(UnreadConversation unreadConversation)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          */
getUnreadConversation()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 
UnreadConversation(String[] messages, RemoteInput remoteInput, PendingIntent replyPendingIntent, PendingIntent readPendingIntent, String[] participants, long latestTimestamp)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
getMessages()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
getRemoteInput()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
getReplyPendingIntent()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
getReadPendingIntent()4122             public PendingIntent getReadPendingIntent() {
4123                 return mReadPendingIntent;
4124             }
4125 
4126             /**
4127              * Gets the participants in the conversation.
4128              */
4129             @Override
getParticipants()4130             public String[] getParticipants() {
4131                 return mParticipants;
4132             }
4133 
4134             /**
4135              * Gets the firs participant in the conversation.
4136              */
4137             @Override
getParticipant()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
getLatestTimestamp()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                  */
Builder(String name)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                  */
addMessage(String message)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                  */
setReplyAction( PendingIntent pendingIntent, RemoteInput remoteInput)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                  */
setReadPendingIntent(PendingIntent pendingIntent)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                  */
setLatestTimestamp(long timestamp)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                  */
build()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      */
getNotificationArrayFromBundle(Bundle bundle, String key)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      */
getExtras(Notification notification)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      */
getActionCount(Notification notification)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      */
getAction(Notification notification, int actionIndex)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      */
getCategory(Notification notification)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      */
getLocalOnly(Notification notification)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      */
getGroup(Notification notification)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      */
isGroupSummary(Notification notification)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      */
getSortKey(Notification notification)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      */
getChannelId(Notification notification)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
getChannel(Notification notification)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      */
getTimeoutAfter(Notification notification)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
getTimeout(Notification notification)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      */
getBadgeIconType(Notification notification)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      */
getShortcutId(Notification notification)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      */
getGroupAlertBehavior(Notification notification)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