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