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