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