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