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