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