NotificationCompat.java revision ca3133e389ded7b6fd142d514523c6ebf41d6599
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            return builder.build();
516        }
517    }
518
519    static class NotificationCompatImplBase implements NotificationCompatImpl {
520        @Override
521        public Notification build(Builder b, BuilderExtender extender) {
522            Notification result = b.mNotification;
523            result = NotificationCompatBase.add(result, b.mContext,
524                    b.mContentTitle, b.mContentText, b.mContentIntent, b.mFullScreenIntent);
525            // translate high priority requests into legacy flag
526            if (b.mPriority > PRIORITY_DEFAULT) {
527                result.flags |= FLAG_HIGH_PRIORITY;
528            }
529            if (b.mContentView != null) {
530                result.contentView = b.mContentView;
531            }
532            return result;
533        }
534
535        @Override
536        public Bundle getExtras(Notification n) {
537            return null;
538        }
539
540        @Override
541        public int getActionCount(Notification n) {
542            return 0;
543        }
544
545        @Override
546        public Action getAction(Notification n, int actionIndex) {
547            return null;
548        }
549
550        @Override
551        public Action[] getActionsFromParcelableArrayList(
552                ArrayList<Parcelable> parcelables) {
553            return null;
554        }
555
556        @Override
557        public ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions) {
558            return null;
559        }
560
561        @Override
562        public String getCategory(Notification n) {
563            return null;
564        }
565
566        @Override
567        public boolean getLocalOnly(Notification n) {
568            return false;
569        }
570
571        @Override
572        public String getGroup(Notification n) {
573            return null;
574        }
575
576        @Override
577        public boolean isGroupSummary(Notification n) {
578            return false;
579        }
580
581        @Override
582        public String getSortKey(Notification n) {
583            return null;
584        }
585
586        @Override
587        public Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) {
588            return null;
589        }
590
591        @Override
592        public NotificationCompatBase.UnreadConversation getUnreadConversationFromBundle(
593                Bundle b, NotificationCompatBase.UnreadConversation.Factory factory,
594                RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
595            return null;
596        }
597    }
598
599    static class NotificationCompatImplHoneycomb extends NotificationCompatImplBase {
600        @Override
601        public Notification build(Builder b, BuilderExtender extender) {
602            Notification notification = NotificationCompatHoneycomb.add(b.mContext, b.mNotification,
603                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
604                    b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon);
605            if (b.mContentView != null) {
606                notification.contentView = b.mContentView;
607            }
608            return notification;
609        }
610    }
611
612    static class NotificationCompatImplIceCreamSandwich extends NotificationCompatImplBase {
613        @Override
614        public Notification build(Builder b, BuilderExtender extender) {
615            NotificationCompatIceCreamSandwich.Builder builder =
616                    new NotificationCompatIceCreamSandwich.Builder(
617                            b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
618                            b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
619                            b.mProgressMax, b.mProgress, b.mProgressIndeterminate);
620            Notification notification = extender.build(b, builder);
621            if (b.mContentView != null) {
622                notification.contentView = b.mContentView;
623            }
624            return notification;
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    /**
1746     * An object that can apply a rich notification style to a {@link Notification.Builder}
1747     * object.
1748     * <br>
1749     * If the platform does not provide rich notification styles, methods in this class have no
1750     * effect.
1751     */
1752    public static abstract class Style {
1753        Builder mBuilder;
1754        CharSequence mBigContentTitle;
1755        CharSequence mSummaryText;
1756        boolean mSummaryTextSet = false;
1757
1758        public void setBuilder(Builder builder) {
1759            if (mBuilder != builder) {
1760                mBuilder = builder;
1761                if (mBuilder != null) {
1762                    mBuilder.setStyle(this);
1763                }
1764            }
1765        }
1766
1767        public Notification build() {
1768            Notification notification = null;
1769            if (mBuilder != null) {
1770                notification = mBuilder.build();
1771            }
1772            return notification;
1773        }
1774
1775        /**
1776         * @hide
1777         */
1778        // TODO: implement for all styles
1779        public void addCompatExtras(Bundle extras) {
1780        }
1781
1782        /**
1783         * @hide
1784         */
1785        // TODO: implement for all styles
1786        protected void restoreFromCompatExtras(Bundle extras) {
1787        }
1788    }
1789
1790    /**
1791     * Helper class for generating large-format notifications that include a large image attachment.
1792     * <br>
1793     * If the platform does not provide large-format notifications, this method has no effect. The
1794     * user will always see the normal notification view.
1795     * <br>
1796     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1797     * <pre class="prettyprint">
1798     * Notification notif = new Notification.Builder(mContext)
1799     *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
1800     *     .setContentText(subject)
1801     *     .setSmallIcon(R.drawable.new_post)
1802     *     .setLargeIcon(aBitmap)
1803     *     .setStyle(new Notification.BigPictureStyle()
1804     *         .bigPicture(aBigBitmap))
1805     *     .build();
1806     * </pre>
1807     *
1808     * @see Notification#bigContentView
1809     */
1810    public static class BigPictureStyle extends Style {
1811        Bitmap mPicture;
1812        Bitmap mBigLargeIcon;
1813        boolean mBigLargeIconSet;
1814
1815        public BigPictureStyle() {
1816        }
1817
1818        public BigPictureStyle(Builder builder) {
1819            setBuilder(builder);
1820        }
1821
1822        /**
1823         * Overrides ContentTitle in the big form of the template.
1824         * This defaults to the value passed to setContentTitle().
1825         */
1826        public BigPictureStyle setBigContentTitle(CharSequence title) {
1827            mBigContentTitle = Builder.limitCharSequenceLength(title);
1828            return this;
1829        }
1830
1831        /**
1832         * Set the first line of text after the detail section in the big form of the template.
1833         */
1834        public BigPictureStyle setSummaryText(CharSequence cs) {
1835            mSummaryText = Builder.limitCharSequenceLength(cs);
1836            mSummaryTextSet = true;
1837            return this;
1838        }
1839
1840        /**
1841         * Provide the bitmap to be used as the payload for the BigPicture notification.
1842         */
1843        public BigPictureStyle bigPicture(Bitmap b) {
1844            mPicture = b;
1845            return this;
1846        }
1847
1848        /**
1849         * Override the large icon when the big notification is shown.
1850         */
1851        public BigPictureStyle bigLargeIcon(Bitmap b) {
1852            mBigLargeIcon = b;
1853            mBigLargeIconSet = true;
1854            return this;
1855        }
1856    }
1857
1858    /**
1859     * Helper class for generating large-format notifications that include a lot of text.
1860     *
1861     * <br>
1862     * If the platform does not provide large-format notifications, this method has no effect. The
1863     * user will always see the normal notification view.
1864     * <br>
1865     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1866     * <pre class="prettyprint">
1867     * Notification notif = new Notification.Builder(mContext)
1868     *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
1869     *     .setContentText(subject)
1870     *     .setSmallIcon(R.drawable.new_mail)
1871     *     .setLargeIcon(aBitmap)
1872     *     .setStyle(new Notification.BigTextStyle()
1873     *         .bigText(aVeryLongString))
1874     *     .build();
1875     * </pre>
1876     *
1877     * @see Notification#bigContentView
1878     */
1879    public static class BigTextStyle extends Style {
1880        CharSequence mBigText;
1881
1882        public BigTextStyle() {
1883        }
1884
1885        public BigTextStyle(Builder builder) {
1886            setBuilder(builder);
1887        }
1888
1889        /**
1890         * Overrides ContentTitle in the big form of the template.
1891         * This defaults to the value passed to setContentTitle().
1892         */
1893        public BigTextStyle setBigContentTitle(CharSequence title) {
1894            mBigContentTitle = Builder.limitCharSequenceLength(title);
1895            return this;
1896        }
1897
1898        /**
1899         * Set the first line of text after the detail section in the big form of the template.
1900         */
1901        public BigTextStyle setSummaryText(CharSequence cs) {
1902            mSummaryText = Builder.limitCharSequenceLength(cs);
1903            mSummaryTextSet = true;
1904            return this;
1905        }
1906
1907        /**
1908         * Provide the longer text to be displayed in the big form of the
1909         * template in place of the content text.
1910         */
1911        public BigTextStyle bigText(CharSequence cs) {
1912            mBigText = Builder.limitCharSequenceLength(cs);
1913            return this;
1914        }
1915    }
1916
1917    /**
1918     * Helper class for generating large-format notifications that include multiple back-and-forth
1919     * messages of varying types between any number of people.
1920     *
1921     * <br>
1922     * If the platform does not provide large-format notifications, this method has no effect. The
1923     * user will always see the normal notification view.
1924     * <br>
1925     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
1926     * so:
1927     * <pre class="prettyprint">
1928     *
1929     * Notification noti = new Notification.Builder()
1930     *     .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
1931     *     .setContentText(subject)
1932     *     .setSmallIcon(R.drawable.new_message)
1933     *     .setLargeIcon(aBitmap)
1934     *     .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
1935     *         .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
1936     *         .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
1937     *     .build();
1938     * </pre>
1939     */
1940    public static class MessagingStyle extends Style {
1941
1942        /**
1943         * The maximum number of messages that will be retained in the Notification itself (the
1944         * number displayed is up to the platform).
1945         */
1946        public static final int MAXIMUM_RETAINED_MESSAGES = 25;
1947
1948        CharSequence mUserDisplayName;
1949        CharSequence mConversationTitle;
1950        List<Message> mMessages = new ArrayList<>();
1951
1952        MessagingStyle() {
1953        }
1954
1955        /**
1956         * @param userDisplayName the name to be displayed for any replies sent by the user before the
1957         * posting app reposts the notification with those messages after they've been actually
1958         * sent and in previous messages sent by the user added in
1959         * {@link #addMessage(Message)}
1960         */
1961        public MessagingStyle(CharSequence userDisplayName) {
1962            mUserDisplayName = userDisplayName;
1963        }
1964
1965        /**
1966         * Returns the name to be displayed for any replies sent by the user
1967         */
1968        public CharSequence getUserDisplayName() {
1969            return mUserDisplayName;
1970        }
1971
1972        /**
1973         * Sets the title to be displayed on this conversation. This should only be used for
1974         * group messaging and left unset for one-on-one conversations.
1975         * @param conversationTitle
1976         * @return this object for method chaining.
1977         */
1978        public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
1979            mConversationTitle = conversationTitle;
1980            return this;
1981        }
1982
1983        /**
1984         * Return the title to be displayed on this conversation. Can be <code>null</code> and
1985         * should be for one-on-one conversations
1986         */
1987        public CharSequence getConversationTitle() {
1988            return mConversationTitle;
1989        }
1990
1991        /**
1992         * Adds a message for display by this notification. Convenience call for a simple
1993         * {@link Message} in {@link #addMessage(Message)}
1994         * @param text A {@link CharSequence} to be displayed as the message content
1995         * @param timestamp Time at which the message arrived
1996         * @param sender A {@link CharSequence} to be used for displaying the name of the
1997         * sender. Should be <code>null</code> for messages by the current user, in which case
1998         * the platform will insert {@link #getUserDisplayName()}.
1999         * Should be unique amongst all individuals in the conversation, and should be
2000         * consistent during re-posts of the notification.
2001         *
2002         * @see Message#Message(CharSequence, long, CharSequence)
2003         *
2004         * @return this object for method chaining
2005         */
2006        public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
2007            mMessages.add(new Message(text, timestamp, sender));
2008            if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
2009                mMessages.remove(0);
2010            }
2011            return this;
2012        }
2013
2014        /**
2015         * Adds a {@link Message} for display in this notification.
2016         * @param message The {@link Message} to be displayed
2017         * @return this object for method chaining
2018         */
2019        public MessagingStyle addMessage(Message message) {
2020            mMessages.add(message);
2021            if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
2022                mMessages.remove(0);
2023            }
2024            return this;
2025        }
2026
2027        /**
2028         * Gets the list of {@code Message} objects that represent the notification
2029         */
2030        public List<Message> getMessages() {
2031            return mMessages;
2032        }
2033
2034        /**
2035         * Retrieves a {@link MessagingStyle} from a {@link Notification}, enabling an application
2036         * that has set a {@link MessagingStyle} using {@link NotificationCompat} or
2037         * {@link android.app.Notification.Builder} to send messaging information to another
2038         * application using {@link NotificationCompat}, regardless of the API level of the system.
2039         * Returns {@code null} if there is no {@link MessagingStyle} set.
2040         */
2041        public static MessagingStyle extractMessagingStyleFromNotification(Notification notif) {
2042            MessagingStyle style;
2043            Bundle extras = IMPL.getExtras(notif);
2044            if (!extras.containsKey(EXTRA_SELF_DISPLAY_NAME)) {
2045                style = null;
2046            } else {
2047                try {
2048                    style = new MessagingStyle();
2049                    style.restoreFromCompatExtras(extras);
2050                } catch (ClassCastException e) {
2051                    style = null;
2052                }
2053            }
2054            return style;
2055        }
2056
2057        @Override
2058        public void addCompatExtras(Bundle extras) {
2059            super.addCompatExtras(extras);
2060            if (mUserDisplayName != null) {
2061                extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
2062            }
2063            if (mConversationTitle != null) {
2064                extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
2065            }
2066            if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
2067                    Message.getBundleArrayForMessages(mMessages));
2068            }
2069        }
2070
2071        /**
2072         * @hide
2073         */
2074        @Override
2075        protected void restoreFromCompatExtras(Bundle extras) {
2076            mMessages.clear();
2077            mUserDisplayName = extras.getString(EXTRA_SELF_DISPLAY_NAME);
2078            mConversationTitle = extras.getString(EXTRA_CONVERSATION_TITLE);
2079            Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES);
2080            if (parcelables != null) {
2081                mMessages = Message.getMessagesFromBundleArray(parcelables);
2082            }
2083        }
2084
2085        public static final class Message {
2086
2087            static final String KEY_TEXT = "text";
2088            static final String KEY_TIMESTAMP = "time";
2089            static final String KEY_SENDER = "sender";
2090            static final String KEY_DATA_MIME_TYPE = "type";
2091            static final String KEY_DATA_URI= "uri";
2092
2093            private final CharSequence mText;
2094            private final long mTimestamp;
2095            private final CharSequence mSender;
2096
2097            private String mDataMimeType;
2098            private Uri mDataUri;
2099
2100            /**
2101             * Constructor
2102             * @param text A {@link CharSequence} to be displayed as the message content
2103             * @param timestamp Time at which the message arrived
2104             * @param sender A {@link CharSequence} to be used for displaying the name of the
2105             * sender. Should be <code>null</code> for messages by the current user, in which case
2106             * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
2107             * Should be unique amongst all individuals in the conversation, and should be
2108             * consistent during re-posts of the notification.
2109             */
2110            public Message(CharSequence text, long timestamp, CharSequence sender){
2111                mText = text;
2112                mTimestamp = timestamp;
2113                mSender = sender;
2114            }
2115
2116            /**
2117             * Sets a binary blob of data and an associated MIME type for a message. In the case
2118             * where the platform doesn't support the MIME type, the original text provided in the
2119             * constructor will be used.
2120             * @param dataMimeType The MIME type of the content. See
2121             * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
2122             * types on Android and Android Wear.
2123             * @param dataUri The uri containing the content whose type is given by the MIME type.
2124             * <p class="note">
2125             * <ol>
2126             *   <li>Notification Listeners including the System UI need permission to access the
2127             *       data the Uri points to. The recommended ways to do this are:</li>
2128             *   <li>Store the data in your own ContentProvider, making sure that other apps have
2129             *       the correct permission to access your provider. The preferred mechanism for
2130             *       providing access is to use per-URI permissions which are temporary and only
2131             *       grant access to the receiving application. An easy way to create a
2132             *       ContentProvider like this is to use the FileProvider helper class.</li>
2133             *   <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
2134             *       and image MIME types, however beginning with Android 3.0 (API level 11) it can
2135             *       also store non-media types (see MediaStore.Files for more info). Files can be
2136             *       inserted into the MediaStore using scanFile() after which a content:// style
2137             *       Uri suitable for sharing is passed to the provided onScanCompleted() callback.
2138             *       Note that once added to the system MediaStore the content is accessible to any
2139             *       app on the device.</li>
2140             * </ol>
2141             * @return this object for method chaining
2142             */
2143            public Message setData(String dataMimeType, Uri dataUri) {
2144                mDataMimeType = dataMimeType;
2145                mDataUri = dataUri;
2146                return this;
2147            }
2148
2149            /**
2150             * Get the text to be used for this message, or the fallback text if a type and content
2151             * Uri have been set
2152             */
2153            public CharSequence getText() {
2154                return mText;
2155            }
2156
2157            /**
2158             * Get the time at which this message arrived
2159             */
2160            public long getTimestamp() {
2161                return mTimestamp;
2162            }
2163
2164            /**
2165             * Get the text used to display the contact's name in the messaging experience
2166             */
2167            public CharSequence getSender() {
2168                return mSender;
2169            }
2170
2171            /**
2172             * Get the MIME type of the data pointed to by the Uri
2173             */
2174            public String getDataMimeType() {
2175                return mDataMimeType;
2176            }
2177
2178            /**
2179             * Get the the Uri pointing to the content of the message. Can be null, in which case
2180             * {@see #getText()} is used.
2181             */
2182            public Uri getDataUri() {
2183                return mDataUri;
2184            }
2185
2186            private Bundle toBundle() {
2187                Bundle bundle = new Bundle();
2188                if (mText != null) {
2189                    bundle.putCharSequence(KEY_TEXT, mText);
2190                }
2191                bundle.putLong(KEY_TIMESTAMP, mTimestamp);
2192                if (mSender != null) {
2193                    bundle.putCharSequence(KEY_SENDER, mSender);
2194                }
2195                if (mDataMimeType != null) {
2196                    bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
2197                }
2198                if (mDataUri != null) {
2199                    bundle.putParcelable(KEY_DATA_URI, mDataUri);
2200                }
2201                return bundle;
2202            }
2203
2204            static Bundle[] getBundleArrayForMessages(List<Message> messages) {
2205                Bundle[] bundles = new Bundle[messages.size()];
2206                final int N = messages.size();
2207                for (int i = 0; i < N; i++) {
2208                    bundles[i] = messages.get(i).toBundle();
2209                }
2210                return bundles;
2211            }
2212
2213            static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
2214                List<Message> messages = new ArrayList<>(bundles.length);
2215                for (int i = 0; i < bundles.length; i++) {
2216                    if (bundles[i] instanceof Bundle) {
2217                        Message message = getMessageFromBundle((Bundle)bundles[i]);
2218                        if (message != null) {
2219                            messages.add(message);
2220                        }
2221                    }
2222                }
2223                return messages;
2224            }
2225
2226            static Message getMessageFromBundle(Bundle bundle) {
2227                try {
2228                    if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
2229                        return null;
2230                    } else {
2231                        Message message = new Message(bundle.getCharSequence(KEY_TEXT),
2232                                bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
2233                        if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
2234                                bundle.containsKey(KEY_DATA_URI)) {
2235
2236                            message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
2237                                    (Uri) bundle.getParcelable(KEY_DATA_URI));
2238                        }
2239                        return message;
2240                    }
2241                } catch (ClassCastException e) {
2242                    return null;
2243                }
2244            }
2245        }
2246    }
2247
2248    /**
2249     * Helper class for generating large-format notifications that include a list of (up to 5) strings.
2250     *
2251     * <br>
2252     * If the platform does not provide large-format notifications, this method has no effect. The
2253     * user will always see the normal notification view.
2254     * <br>
2255     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
2256     * <pre class="prettyprint">
2257     * Notification noti = new Notification.Builder()
2258     *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
2259     *     .setContentText(subject)
2260     *     .setSmallIcon(R.drawable.new_mail)
2261     *     .setLargeIcon(aBitmap)
2262     *     .setStyle(new Notification.InboxStyle()
2263     *         .addLine(str1)
2264     *         .addLine(str2)
2265     *         .setContentTitle(&quot;&quot;)
2266     *         .setSummaryText(&quot;+3 more&quot;))
2267     *     .build();
2268     * </pre>
2269     *
2270     * @see Notification#bigContentView
2271     */
2272    public static class InboxStyle extends Style {
2273        ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
2274
2275        public InboxStyle() {
2276        }
2277
2278        public InboxStyle(Builder builder) {
2279            setBuilder(builder);
2280        }
2281
2282        /**
2283         * Overrides ContentTitle in the big form of the template.
2284         * This defaults to the value passed to setContentTitle().
2285         */
2286        public InboxStyle setBigContentTitle(CharSequence title) {
2287            mBigContentTitle = Builder.limitCharSequenceLength(title);
2288            return this;
2289        }
2290
2291        /**
2292         * Set the first line of text after the detail section in the big form of the template.
2293         */
2294        public InboxStyle setSummaryText(CharSequence cs) {
2295            mSummaryText = Builder.limitCharSequenceLength(cs);
2296            mSummaryTextSet = true;
2297            return this;
2298        }
2299
2300        /**
2301         * Append a line to the digest section of the Inbox notification.
2302         */
2303        public InboxStyle addLine(CharSequence cs) {
2304            mTexts.add(Builder.limitCharSequenceLength(cs));
2305            return this;
2306        }
2307    }
2308
2309    /**
2310     * Structure to encapsulate a named action that can be shown as part of this notification.
2311     * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
2312     * selected by the user. Action buttons won't appear on platforms prior to Android 4.1.
2313     * <p>
2314     * Apps should use {@link NotificationCompat.Builder#addAction(int, CharSequence, PendingIntent)}
2315     * or {@link NotificationCompat.Builder#addAction(NotificationCompat.Action)}
2316     * to attach actions.
2317     */
2318    public static class Action extends NotificationCompatBase.Action {
2319        private final Bundle mExtras;
2320        private final RemoteInput[] mRemoteInputs;
2321        private boolean mAllowGeneratedReplies = false;
2322
2323        /**
2324         * Small icon representing the action.
2325         */
2326        public int icon;
2327        /**
2328         * Title of the action.
2329         */
2330        public CharSequence title;
2331        /**
2332         * Intent to send when the user invokes this action. May be null, in which case the action
2333         * may be rendered in a disabled presentation.
2334         */
2335        public PendingIntent actionIntent;
2336
2337        public Action(int icon, CharSequence title, PendingIntent intent) {
2338            this(icon, title, intent, new Bundle(), null, false);
2339        }
2340
2341        private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
2342                RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
2343            this.icon = icon;
2344            this.title = NotificationCompat.Builder.limitCharSequenceLength(title);
2345            this.actionIntent = intent;
2346            this.mExtras = extras != null ? extras : new Bundle();
2347            this.mRemoteInputs = remoteInputs;
2348            this.mAllowGeneratedReplies = allowGeneratedReplies;
2349        }
2350
2351        @Override
2352        public int getIcon() {
2353            return icon;
2354        }
2355
2356        @Override
2357        public CharSequence getTitle() {
2358            return title;
2359        }
2360
2361        @Override
2362        public PendingIntent getActionIntent() {
2363            return actionIntent;
2364        }
2365
2366        /**
2367         * Get additional metadata carried around with this Action.
2368         */
2369        @Override
2370        public Bundle getExtras() {
2371            return mExtras;
2372        }
2373
2374        /**
2375         * Return whether the platform should automatically generate possible replies for this
2376         * {@link Action}
2377         */
2378        @Override
2379        public boolean getAllowGeneratedReplies() {
2380            return mAllowGeneratedReplies;
2381        }
2382
2383        /**
2384         * Get the list of inputs to be collected from the user when this action is sent.
2385         * May return null if no remote inputs were added.
2386         */
2387        @Override
2388        public RemoteInput[] getRemoteInputs() {
2389            return mRemoteInputs;
2390        }
2391
2392        /**
2393         * Builder class for {@link Action} objects.
2394         */
2395        public static final class Builder {
2396            private final int mIcon;
2397            private final CharSequence mTitle;
2398            private final PendingIntent mIntent;
2399            private boolean mAllowGeneratedReplies;
2400            private final Bundle mExtras;
2401            private ArrayList<RemoteInput> mRemoteInputs;
2402
2403            /**
2404             * Construct a new builder for {@link Action} object.
2405             * @param icon icon to show for this action
2406             * @param title the title of the action
2407             * @param intent the {@link PendingIntent} to fire when users trigger this action
2408             */
2409            public Builder(int icon, CharSequence title, PendingIntent intent) {
2410                this(icon, title, intent, new Bundle());
2411            }
2412
2413            /**
2414             * Construct a new builder for {@link Action} object using the fields from an
2415             * {@link Action}.
2416             * @param action the action to read fields from.
2417             */
2418            public Builder(Action action) {
2419                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras));
2420            }
2421
2422            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
2423                mIcon = icon;
2424                mTitle = NotificationCompat.Builder.limitCharSequenceLength(title);
2425                mIntent = intent;
2426                mExtras = extras;
2427            }
2428
2429            /**
2430             * Merge additional metadata into this builder.
2431             *
2432             * <p>Values within the Bundle will replace existing extras values in this Builder.
2433             *
2434             * @see NotificationCompat.Action#getExtras
2435             */
2436            public Builder addExtras(Bundle extras) {
2437                if (extras != null) {
2438                    mExtras.putAll(extras);
2439                }
2440                return this;
2441            }
2442
2443            /**
2444             * Get the metadata Bundle used by this Builder.
2445             *
2446             * <p>The returned Bundle is shared with this Builder.
2447             */
2448            public Bundle getExtras() {
2449                return mExtras;
2450            }
2451
2452            /**
2453             * Add an input to be collected from the user when this action is sent.
2454             * Response values can be retrieved from the fired intent by using the
2455             * {@link RemoteInput#getResultsFromIntent} function.
2456             * @param remoteInput a {@link RemoteInput} to add to the action
2457             * @return this object for method chaining
2458             */
2459            public Builder addRemoteInput(RemoteInput remoteInput) {
2460                if (mRemoteInputs == null) {
2461                    mRemoteInputs = new ArrayList<RemoteInput>();
2462                }
2463                mRemoteInputs.add(remoteInput);
2464                return this;
2465            }
2466
2467            /**
2468             * Set whether the platform should automatically generate possible replies to add to
2469             * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
2470             * {@link RemoteInput}, this has no effect.
2471             * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
2472             * otherwise
2473             * @return this object for method chaining
2474             * The default value is {@code false}
2475             */
2476            public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
2477                mAllowGeneratedReplies = allowGeneratedReplies;
2478                return this;
2479            }
2480
2481            /**
2482             * Apply an extender to this action builder. Extenders may be used to add
2483             * metadata or change options on this builder.
2484             */
2485            public Builder extend(Extender extender) {
2486                extender.extend(this);
2487                return this;
2488            }
2489
2490            /**
2491             * Combine all of the options that have been set and return a new {@link Action}
2492             * object.
2493             * @return the built action
2494             */
2495            public Action build() {
2496                RemoteInput[] remoteInputs = mRemoteInputs != null
2497                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
2498                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs,
2499                        mAllowGeneratedReplies);
2500            }
2501        }
2502
2503
2504        /**
2505         * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
2506         * metadata or change options on an action builder.
2507         */
2508        public interface Extender {
2509            /**
2510             * Apply this extender to a notification action builder.
2511             * @param builder the builder to be modified.
2512             * @return the build object for chaining.
2513             */
2514            public Builder extend(Builder builder);
2515        }
2516
2517        /**
2518         * Wearable extender for notification actions. To add extensions to an action,
2519         * create a new {@link NotificationCompat.Action.WearableExtender} object using
2520         * the {@code WearableExtender()} constructor and apply it to a
2521         * {@link NotificationCompat.Action.Builder} using
2522         * {@link NotificationCompat.Action.Builder#extend}.
2523         *
2524         * <pre class="prettyprint">
2525         * NotificationCompat.Action action = new NotificationCompat.Action.Builder(
2526         *         R.drawable.archive_all, "Archive all", actionIntent)
2527         *         .extend(new NotificationCompat.Action.WearableExtender()
2528         *                 .setAvailableOffline(false))
2529         *         .build();</pre>
2530         */
2531        public static final class WearableExtender implements Extender {
2532            /** Notification action extra which contains wearable extensions */
2533            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
2534
2535            // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
2536            private static final String KEY_FLAGS = "flags";
2537            private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
2538            private static final String KEY_CONFIRM_LABEL = "confirmLabel";
2539            private static final String KEY_CANCEL_LABEL = "cancelLabel";
2540
2541            // Flags bitwise-ored to mFlags
2542            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
2543            private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
2544
2545            // Default value for flags integer
2546            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
2547
2548            private int mFlags = DEFAULT_FLAGS;
2549
2550            private CharSequence mInProgressLabel;
2551            private CharSequence mConfirmLabel;
2552            private CharSequence mCancelLabel;
2553
2554            /**
2555             * Create a {@link NotificationCompat.Action.WearableExtender} with default
2556             * options.
2557             */
2558            public WearableExtender() {
2559            }
2560
2561            /**
2562             * Create a {@link NotificationCompat.Action.WearableExtender} by reading
2563             * wearable options present in an existing notification action.
2564             * @param action the notification action to inspect.
2565             */
2566            public WearableExtender(Action action) {
2567                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
2568                if (wearableBundle != null) {
2569                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
2570                    mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
2571                    mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
2572                    mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
2573                }
2574            }
2575
2576            /**
2577             * Apply wearable extensions to a notification action that is being built. This is
2578             * typically called by the {@link NotificationCompat.Action.Builder#extend}
2579             * method of {@link NotificationCompat.Action.Builder}.
2580             */
2581            @Override
2582            public Action.Builder extend(Action.Builder builder) {
2583                Bundle wearableBundle = new Bundle();
2584
2585                if (mFlags != DEFAULT_FLAGS) {
2586                    wearableBundle.putInt(KEY_FLAGS, mFlags);
2587                }
2588                if (mInProgressLabel != null) {
2589                    wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
2590                }
2591                if (mConfirmLabel != null) {
2592                    wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
2593                }
2594                if (mCancelLabel != null) {
2595                    wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
2596                }
2597
2598                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
2599                return builder;
2600            }
2601
2602            @Override
2603            public WearableExtender clone() {
2604                WearableExtender that = new WearableExtender();
2605                that.mFlags = this.mFlags;
2606                that.mInProgressLabel = this.mInProgressLabel;
2607                that.mConfirmLabel = this.mConfirmLabel;
2608                that.mCancelLabel = this.mCancelLabel;
2609                return that;
2610            }
2611
2612            /**
2613             * Set whether this action is available when the wearable device is not connected to
2614             * a companion device. The user can still trigger this action when the wearable device
2615             * is offline, but a visual hint will indicate that the action may not be available.
2616             * Defaults to true.
2617             */
2618            public WearableExtender setAvailableOffline(boolean availableOffline) {
2619                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
2620                return this;
2621            }
2622
2623            /**
2624             * Get whether this action is available when the wearable device is not connected to
2625             * a companion device. The user can still trigger this action when the wearable device
2626             * is offline, but a visual hint will indicate that the action may not be available.
2627             * Defaults to true.
2628             */
2629            public boolean isAvailableOffline() {
2630                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
2631            }
2632
2633            private void setFlag(int mask, boolean value) {
2634                if (value) {
2635                    mFlags |= mask;
2636                } else {
2637                    mFlags &= ~mask;
2638                }
2639            }
2640
2641            /**
2642             * Set a label to display while the wearable is preparing to automatically execute the
2643             * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
2644             *
2645             * @param label the label to display while the action is being prepared to execute
2646             * @return this object for method chaining
2647             */
2648            public WearableExtender setInProgressLabel(CharSequence label) {
2649                mInProgressLabel = label;
2650                return this;
2651            }
2652
2653            /**
2654             * Get the label to display while the wearable is preparing to automatically execute
2655             * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
2656             *
2657             * @return the label to display while the action is being prepared to execute
2658             */
2659            public CharSequence getInProgressLabel() {
2660                return mInProgressLabel;
2661            }
2662
2663            /**
2664             * Set a label to display to confirm that the action should be executed.
2665             * This is usually an imperative verb like "Send".
2666             *
2667             * @param label the label to confirm the action should be executed
2668             * @return this object for method chaining
2669             */
2670            public WearableExtender setConfirmLabel(CharSequence label) {
2671                mConfirmLabel = label;
2672                return this;
2673            }
2674
2675            /**
2676             * Get the label to display to confirm that the action should be executed.
2677             * This is usually an imperative verb like "Send".
2678             *
2679             * @return the label to confirm the action should be executed
2680             */
2681            public CharSequence getConfirmLabel() {
2682                return mConfirmLabel;
2683            }
2684
2685            /**
2686             * Set a label to display to cancel the action.
2687             * This is usually an imperative verb, like "Cancel".
2688             *
2689             * @param label the label to display to cancel the action
2690             * @return this object for method chaining
2691             */
2692            public WearableExtender setCancelLabel(CharSequence label) {
2693                mCancelLabel = label;
2694                return this;
2695            }
2696
2697            /**
2698             * Get the label to display to cancel the action.
2699             * This is usually an imperative verb like "Cancel".
2700             *
2701             * @return the label to display to cancel the action
2702             */
2703            public CharSequence getCancelLabel() {
2704                return mCancelLabel;
2705            }
2706
2707            /**
2708             * Set a hint that this Action will launch an {@link Activity} directly, telling the
2709             * platform that it can generate the appropriate transitions.
2710             * @param hintLaunchesActivity {@code true} if the content intent will launch
2711             * an activity and transitions should be generated, false otherwise.
2712             * @return this object for method chaining
2713             */
2714            public WearableExtender setHintLaunchesActivity(
2715                    boolean hintLaunchesActivity) {
2716                setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
2717                return this;
2718            }
2719
2720            /**
2721             * Get a hint that this Action will launch an {@link Activity} directly, telling the
2722             * platform that it can generate the appropriate transitions
2723             * @return {@code true} if the content intent will launch an activity and transitions
2724             * should be generated, false otherwise. The default value is {@code false} if this was
2725             * never set.
2726             */
2727            public boolean getHintLaunchesActivity() {
2728                return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
2729            }
2730        }
2731
2732        /** @hide */
2733        public static final Factory FACTORY = new Factory() {
2734            @Override
2735            public NotificationCompatBase.Action build(int icon, CharSequence title,
2736                    PendingIntent actionIntent, Bundle extras,
2737                    RemoteInputCompatBase.RemoteInput[] remoteInputs,
2738                    boolean allowGeneratedReplies) {
2739                return new Action(icon, title, actionIntent, extras,
2740                        (RemoteInput[]) remoteInputs, allowGeneratedReplies);
2741            }
2742
2743            @Override
2744            public Action[] newArray(int length) {
2745                return new Action[length];
2746            }
2747        };
2748    }
2749
2750
2751    /**
2752     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
2753     * metadata or change options on a notification builder.
2754     */
2755    public interface Extender {
2756        /**
2757         * Apply this extender to a notification builder.
2758         * @param builder the builder to be modified.
2759         * @return the build object for chaining.
2760         */
2761        public Builder extend(Builder builder);
2762    }
2763
2764    /**
2765     * Helper class to add wearable extensions to notifications.
2766     * <p class="note"> See
2767     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
2768     * for Android Wear</a> for more information on how to use this class.
2769     * <p>
2770     * To create a notification with wearable extensions:
2771     * <ol>
2772     *   <li>Create a {@link NotificationCompat.Builder}, setting any desired
2773     *   properties.
2774     *   <li>Create a {@link NotificationCompat.WearableExtender}.
2775     *   <li>Set wearable-specific properties using the
2776     *   {@code add} and {@code set} methods of {@link NotificationCompat.WearableExtender}.
2777     *   <li>Call {@link NotificationCompat.Builder#extend} to apply the extensions to a
2778     *   notification.
2779     *   <li>Post the notification to the notification
2780     *   system with the {@code NotificationManagerCompat.notify(...)} methods
2781     *   and not the {@code NotificationManager.notify(...)} methods.
2782     * </ol>
2783     *
2784     * <pre class="prettyprint">
2785     * Notification notif = new NotificationCompat.Builder(mContext)
2786     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
2787     *         .setContentText(subject)
2788     *         .setSmallIcon(R.drawable.new_mail)
2789     *         .extend(new NotificationCompat.WearableExtender()
2790     *                 .setContentIcon(R.drawable.new_mail))
2791     *         .build();
2792     * NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
2793     *
2794     * <p>Wearable extensions can be accessed on an existing notification by using the
2795     * {@code WearableExtender(Notification)} constructor,
2796     * and then using the {@code get} methods to access values.
2797     *
2798     * <pre class="prettyprint">
2799     * NotificationCompat.WearableExtender wearableExtender =
2800     *         new NotificationCompat.WearableExtender(notification);
2801     * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
2802     */
2803    public static final class WearableExtender implements Extender {
2804        /**
2805         * Sentinel value for an action index that is unset.
2806         */
2807        public static final int UNSET_ACTION_INDEX = -1;
2808
2809        /**
2810         * Size value for use with {@link #setCustomSizePreset} to show this notification with
2811         * default sizing.
2812         * <p>For custom display notifications created using {@link #setDisplayIntent},
2813         * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
2814         * on their content.
2815         */
2816        public static final int SIZE_DEFAULT = 0;
2817
2818        /**
2819         * Size value for use with {@link #setCustomSizePreset} to show this notification
2820         * with an extra small size.
2821         * <p>This value is only applicable for custom display notifications created using
2822         * {@link #setDisplayIntent}.
2823         */
2824        public static final int SIZE_XSMALL = 1;
2825
2826        /**
2827         * Size value for use with {@link #setCustomSizePreset} to show this notification
2828         * with a small size.
2829         * <p>This value is only applicable for custom display notifications created using
2830         * {@link #setDisplayIntent}.
2831         */
2832        public static final int SIZE_SMALL = 2;
2833
2834        /**
2835         * Size value for use with {@link #setCustomSizePreset} to show this notification
2836         * with a medium size.
2837         * <p>This value is only applicable for custom display notifications created using
2838         * {@link #setDisplayIntent}.
2839         */
2840        public static final int SIZE_MEDIUM = 3;
2841
2842        /**
2843         * Size value for use with {@link #setCustomSizePreset} to show this notification
2844         * with a large size.
2845         * <p>This value is only applicable for custom display notifications created using
2846         * {@link #setDisplayIntent}.
2847         */
2848        public static final int SIZE_LARGE = 4;
2849
2850        /**
2851         * Size value for use with {@link #setCustomSizePreset} to show this notification
2852         * full screen.
2853         * <p>This value is only applicable for custom display notifications created using
2854         * {@link #setDisplayIntent}.
2855         */
2856        public static final int SIZE_FULL_SCREEN = 5;
2857
2858        /**
2859         * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
2860         * short amount of time when this notification is displayed on the screen. This
2861         * is the default value.
2862         */
2863        public static final int SCREEN_TIMEOUT_SHORT = 0;
2864
2865        /**
2866         * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
2867         * for a longer amount of time when this notification is displayed on the screen.
2868         */
2869        public static final int SCREEN_TIMEOUT_LONG = -1;
2870
2871        /** Notification extra which contains wearable extensions */
2872        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
2873
2874        // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
2875        private static final String KEY_ACTIONS = "actions";
2876        private static final String KEY_FLAGS = "flags";
2877        private static final String KEY_DISPLAY_INTENT = "displayIntent";
2878        private static final String KEY_PAGES = "pages";
2879        private static final String KEY_BACKGROUND = "background";
2880        private static final String KEY_CONTENT_ICON = "contentIcon";
2881        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
2882        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
2883        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
2884        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
2885        private static final String KEY_GRAVITY = "gravity";
2886        private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
2887        private static final String KEY_DISMISSAL_ID = "dismissalId";
2888
2889        // Flags bitwise-ored to mFlags
2890        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
2891        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
2892        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
2893        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
2894        private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
2895        private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
2896        private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
2897
2898        // Default value for flags integer
2899        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
2900
2901        private static final int DEFAULT_CONTENT_ICON_GRAVITY = GravityCompat.END;
2902        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
2903
2904        private ArrayList<Action> mActions = new ArrayList<Action>();
2905        private int mFlags = DEFAULT_FLAGS;
2906        private PendingIntent mDisplayIntent;
2907        private ArrayList<Notification> mPages = new ArrayList<Notification>();
2908        private Bitmap mBackground;
2909        private int mContentIcon;
2910        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
2911        private int mContentActionIndex = UNSET_ACTION_INDEX;
2912        private int mCustomSizePreset = SIZE_DEFAULT;
2913        private int mCustomContentHeight;
2914        private int mGravity = DEFAULT_GRAVITY;
2915        private int mHintScreenTimeout;
2916        private String mDismissalId;
2917
2918        /**
2919         * Create a {@link NotificationCompat.WearableExtender} with default
2920         * options.
2921         */
2922        public WearableExtender() {
2923        }
2924
2925        public WearableExtender(Notification notif) {
2926            Bundle extras = getExtras(notif);
2927            Bundle wearableBundle = extras != null ? extras.getBundle(EXTRA_WEARABLE_EXTENSIONS)
2928                    : null;
2929            if (wearableBundle != null) {
2930                Action[] actions = IMPL.getActionsFromParcelableArrayList(
2931                        wearableBundle.getParcelableArrayList(KEY_ACTIONS));
2932                if (actions != null) {
2933                    Collections.addAll(mActions, actions);
2934                }
2935
2936                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
2937                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
2938
2939                Notification[] pages = getNotificationArrayFromBundle(
2940                        wearableBundle, KEY_PAGES);
2941                if (pages != null) {
2942                    Collections.addAll(mPages, pages);
2943                }
2944
2945                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
2946                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
2947                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
2948                        DEFAULT_CONTENT_ICON_GRAVITY);
2949                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
2950                        UNSET_ACTION_INDEX);
2951                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
2952                        SIZE_DEFAULT);
2953                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
2954                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
2955                mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
2956                mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
2957            }
2958        }
2959
2960        /**
2961         * Apply wearable extensions to a notification that is being built. This is typically
2962         * called by the {@link NotificationCompat.Builder#extend} method of
2963         * {@link NotificationCompat.Builder}.
2964         */
2965        @Override
2966        public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
2967            Bundle wearableBundle = new Bundle();
2968
2969            if (!mActions.isEmpty()) {
2970                wearableBundle.putParcelableArrayList(KEY_ACTIONS,
2971                        IMPL.getParcelableArrayListForActions(mActions.toArray(
2972                                new Action[mActions.size()])));
2973            }
2974            if (mFlags != DEFAULT_FLAGS) {
2975                wearableBundle.putInt(KEY_FLAGS, mFlags);
2976            }
2977            if (mDisplayIntent != null) {
2978                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
2979            }
2980            if (!mPages.isEmpty()) {
2981                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
2982                        new Notification[mPages.size()]));
2983            }
2984            if (mBackground != null) {
2985                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
2986            }
2987            if (mContentIcon != 0) {
2988                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
2989            }
2990            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
2991                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
2992            }
2993            if (mContentActionIndex != UNSET_ACTION_INDEX) {
2994                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
2995                        mContentActionIndex);
2996            }
2997            if (mCustomSizePreset != SIZE_DEFAULT) {
2998                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
2999            }
3000            if (mCustomContentHeight != 0) {
3001                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
3002            }
3003            if (mGravity != DEFAULT_GRAVITY) {
3004                wearableBundle.putInt(KEY_GRAVITY, mGravity);
3005            }
3006            if (mHintScreenTimeout != 0) {
3007                wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
3008            }
3009            if (mDismissalId != null) {
3010                wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
3011            }
3012
3013            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
3014            return builder;
3015        }
3016
3017        @Override
3018        public WearableExtender clone() {
3019            WearableExtender that = new WearableExtender();
3020            that.mActions = new ArrayList<Action>(this.mActions);
3021            that.mFlags = this.mFlags;
3022            that.mDisplayIntent = this.mDisplayIntent;
3023            that.mPages = new ArrayList<Notification>(this.mPages);
3024            that.mBackground = this.mBackground;
3025            that.mContentIcon = this.mContentIcon;
3026            that.mContentIconGravity = this.mContentIconGravity;
3027            that.mContentActionIndex = this.mContentActionIndex;
3028            that.mCustomSizePreset = this.mCustomSizePreset;
3029            that.mCustomContentHeight = this.mCustomContentHeight;
3030            that.mGravity = this.mGravity;
3031            that.mHintScreenTimeout = this.mHintScreenTimeout;
3032            that.mDismissalId = this.mDismissalId;
3033            return that;
3034        }
3035
3036        /**
3037         * Add a wearable action to this notification.
3038         *
3039         * <p>When wearable actions are added using this method, the set of actions that
3040         * show on a wearable device splits from devices that only show actions added
3041         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
3042         * of which actions display on different devices.
3043         *
3044         * @param action the action to add to this notification
3045         * @return this object for method chaining
3046         * @see NotificationCompat.Action
3047         */
3048        public WearableExtender addAction(Action action) {
3049            mActions.add(action);
3050            return this;
3051        }
3052
3053        /**
3054         * Adds wearable actions to this notification.
3055         *
3056         * <p>When wearable actions are added using this method, the set of actions that
3057         * show on a wearable device splits from devices that only show actions added
3058         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
3059         * of which actions display on different devices.
3060         *
3061         * @param actions the actions to add to this notification
3062         * @return this object for method chaining
3063         * @see NotificationCompat.Action
3064         */
3065        public WearableExtender addActions(List<Action> actions) {
3066            mActions.addAll(actions);
3067            return this;
3068        }
3069
3070        /**
3071         * Clear all wearable actions present on this builder.
3072         * @return this object for method chaining.
3073         * @see #addAction
3074         */
3075        public WearableExtender clearActions() {
3076            mActions.clear();
3077            return this;
3078        }
3079
3080        /**
3081         * Get the wearable actions present on this notification.
3082         */
3083        public List<Action> getActions() {
3084            return mActions;
3085        }
3086
3087        /**
3088         * Set an intent to launch inside of an activity view when displaying
3089         * this notification. The {@link PendingIntent} provided should be for an activity.
3090         *
3091         * <pre class="prettyprint">
3092         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
3093         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
3094         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
3095         * Notification notif = new NotificationCompat.Builder(context)
3096         *         .extend(new NotificationCompat.WearableExtender()
3097         *                 .setDisplayIntent(displayPendingIntent)
3098         *                 .setCustomSizePreset(NotificationCompat.WearableExtender.SIZE_MEDIUM))
3099         *         .build();</pre>
3100         *
3101         * <p>The activity to launch needs to allow embedding, must be exported, and
3102         * should have an empty task affinity. It is also recommended to use the device
3103         * default light theme.
3104         *
3105         * <p>Example AndroidManifest.xml entry:
3106         * <pre class="prettyprint">
3107         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
3108         *     android:exported=&quot;true&quot;
3109         *     android:allowEmbedded=&quot;true&quot;
3110         *     android:taskAffinity=&quot;&quot;
3111         *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
3112         *
3113         * @param intent the {@link PendingIntent} for an activity
3114         * @return this object for method chaining
3115         * @see NotificationCompat.WearableExtender#getDisplayIntent
3116         */
3117        public WearableExtender setDisplayIntent(PendingIntent intent) {
3118            mDisplayIntent = intent;
3119            return this;
3120        }
3121
3122        /**
3123         * Get the intent to launch inside of an activity view when displaying this
3124         * notification. This {@code PendingIntent} should be for an activity.
3125         */
3126        public PendingIntent getDisplayIntent() {
3127            return mDisplayIntent;
3128        }
3129
3130        /**
3131         * Add an additional page of content to display with this notification. The current
3132         * notification forms the first page, and pages added using this function form
3133         * subsequent pages. This field can be used to separate a notification into multiple
3134         * sections.
3135         *
3136         * @param page the notification to add as another page
3137         * @return this object for method chaining
3138         * @see NotificationCompat.WearableExtender#getPages
3139         */
3140        public WearableExtender addPage(Notification page) {
3141            mPages.add(page);
3142            return this;
3143        }
3144
3145        /**
3146         * Add additional pages of content to display with this notification. The current
3147         * notification forms the first page, and pages added using this function form
3148         * subsequent pages. This field can be used to separate a notification into multiple
3149         * sections.
3150         *
3151         * @param pages a list of notifications
3152         * @return this object for method chaining
3153         * @see NotificationCompat.WearableExtender#getPages
3154         */
3155        public WearableExtender addPages(List<Notification> pages) {
3156            mPages.addAll(pages);
3157            return this;
3158        }
3159
3160        /**
3161         * Clear all additional pages present on this builder.
3162         * @return this object for method chaining.
3163         * @see #addPage
3164         */
3165        public WearableExtender clearPages() {
3166            mPages.clear();
3167            return this;
3168        }
3169
3170        /**
3171         * Get the array of additional pages of content for displaying this notification. The
3172         * current notification forms the first page, and elements within this array form
3173         * subsequent pages. This field can be used to separate a notification into multiple
3174         * sections.
3175         * @return the pages for this notification
3176         */
3177        public List<Notification> getPages() {
3178            return mPages;
3179        }
3180
3181        /**
3182         * Set a background image to be displayed behind the notification content.
3183         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
3184         * will work with any notification style.
3185         *
3186         * @param background the background bitmap
3187         * @return this object for method chaining
3188         * @see NotificationCompat.WearableExtender#getBackground
3189         */
3190        public WearableExtender setBackground(Bitmap background) {
3191            mBackground = background;
3192            return this;
3193        }
3194
3195        /**
3196         * Get a background image to be displayed behind the notification content.
3197         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
3198         * will work with any notification style.
3199         *
3200         * @return the background image
3201         * @see NotificationCompat.WearableExtender#setBackground
3202         */
3203        public Bitmap getBackground() {
3204            return mBackground;
3205        }
3206
3207        /**
3208         * Set an icon that goes with the content of this notification.
3209         */
3210        public WearableExtender setContentIcon(int icon) {
3211            mContentIcon = icon;
3212            return this;
3213        }
3214
3215        /**
3216         * Get an icon that goes with the content of this notification.
3217         */
3218        public int getContentIcon() {
3219            return mContentIcon;
3220        }
3221
3222        /**
3223         * Set the gravity that the content icon should have within the notification display.
3224         * Supported values include {@link android.view.Gravity#START} and
3225         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
3226         * @see #setContentIcon
3227         */
3228        public WearableExtender setContentIconGravity(int contentIconGravity) {
3229            mContentIconGravity = contentIconGravity;
3230            return this;
3231        }
3232
3233        /**
3234         * Get the gravity that the content icon should have within the notification display.
3235         * Supported values include {@link android.view.Gravity#START} and
3236         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
3237         * @see #getContentIcon
3238         */
3239        public int getContentIconGravity() {
3240            return mContentIconGravity;
3241        }
3242
3243        /**
3244         * Set an action from this notification's actions to be clickable with the content of
3245         * this notification. This action will no longer display separately from the
3246         * notification's content.
3247         *
3248         * <p>For notifications with multiple pages, child pages can also have content actions
3249         * set, although the list of available actions comes from the main notification and not
3250         * from the child page's notification.
3251         *
3252         * @param actionIndex The index of the action to hoist onto the current notification page.
3253         *                    If wearable actions were added to the main notification, this index
3254         *                    will apply to that list, otherwise it will apply to the regular
3255         *                    actions list.
3256         */
3257        public WearableExtender setContentAction(int actionIndex) {
3258            mContentActionIndex = actionIndex;
3259            return this;
3260        }
3261
3262        /**
3263         * Get the index of the notification action, if any, that was specified as being clickable
3264         * with the content of this notification. This action will no longer display separately
3265         * from the notification's content.
3266         *
3267         * <p>For notifications with multiple pages, child pages can also have content actions
3268         * set, although the list of available actions comes from the main notification and not
3269         * from the child page's notification.
3270         *
3271         * <p>If wearable specific actions were added to the main notification, this index will
3272         * apply to that list, otherwise it will apply to the regular actions list.
3273         *
3274         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
3275         */
3276        public int getContentAction() {
3277            return mContentActionIndex;
3278        }
3279
3280        /**
3281         * Set the gravity that this notification should have within the available viewport space.
3282         * Supported values include {@link android.view.Gravity#TOP},
3283         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
3284         * The default value is {@link android.view.Gravity#BOTTOM}.
3285         */
3286        public WearableExtender setGravity(int gravity) {
3287            mGravity = gravity;
3288            return this;
3289        }
3290
3291        /**
3292         * Get the gravity that this notification should have within the available viewport space.
3293         * Supported values include {@link android.view.Gravity#TOP},
3294         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
3295         * The default value is {@link android.view.Gravity#BOTTOM}.
3296         */
3297        public int getGravity() {
3298            return mGravity;
3299        }
3300
3301        /**
3302         * Set the custom size preset for the display of this notification out of the available
3303         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
3304         * {@link #SIZE_LARGE}.
3305         * <p>Some custom size presets are only applicable for custom display notifications created
3306         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. Check the
3307         * documentation for the preset in question. See also
3308         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
3309         */
3310        public WearableExtender setCustomSizePreset(int sizePreset) {
3311            mCustomSizePreset = sizePreset;
3312            return this;
3313        }
3314
3315        /**
3316         * Get the custom size preset for the display of this notification out of the available
3317         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
3318         * {@link #SIZE_LARGE}.
3319         * <p>Some custom size presets are only applicable for custom display notifications created
3320         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
3321         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
3322         */
3323        public int getCustomSizePreset() {
3324            return mCustomSizePreset;
3325        }
3326
3327        /**
3328         * Set the custom height in pixels for the display of this notification's content.
3329         * <p>This option is only available for custom display notifications created
3330         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. See also
3331         * {@link NotificationCompat.WearableExtender#setCustomSizePreset} and
3332         * {@link #getCustomContentHeight}.
3333         */
3334        public WearableExtender setCustomContentHeight(int height) {
3335            mCustomContentHeight = height;
3336            return this;
3337        }
3338
3339        /**
3340         * Get the custom height in pixels for the display of this notification's content.
3341         * <p>This option is only available for custom display notifications created
3342         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
3343         * {@link #setCustomContentHeight}.
3344         */
3345        public int getCustomContentHeight() {
3346            return mCustomContentHeight;
3347        }
3348
3349        /**
3350         * Set whether the scrolling position for the contents of this notification should start
3351         * at the bottom of the contents instead of the top when the contents are too long to
3352         * display within the screen.  Default is false (start scroll at the top).
3353         */
3354        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
3355            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
3356            return this;
3357        }
3358
3359        /**
3360         * Get whether the scrolling position for the contents of this notification should start
3361         * at the bottom of the contents instead of the top when the contents are too long to
3362         * display within the screen. Default is false (start scroll at the top).
3363         */
3364        public boolean getStartScrollBottom() {
3365            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
3366        }
3367
3368        /**
3369         * Set whether the content intent is available when the wearable device is not connected
3370         * to a companion device.  The user can still trigger this intent when the wearable device
3371         * is offline, but a visual hint will indicate that the content intent may not be available.
3372         * Defaults to true.
3373         */
3374        public WearableExtender setContentIntentAvailableOffline(
3375                boolean contentIntentAvailableOffline) {
3376            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
3377            return this;
3378        }
3379
3380        /**
3381         * Get whether the content intent is available when the wearable device is not connected
3382         * to a companion device.  The user can still trigger this intent when the wearable device
3383         * is offline, but a visual hint will indicate that the content intent may not be available.
3384         * Defaults to true.
3385         */
3386        public boolean getContentIntentAvailableOffline() {
3387            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
3388        }
3389
3390        /**
3391         * Set a hint that this notification's icon should not be displayed.
3392         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
3393         * @return this object for method chaining
3394         */
3395        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
3396            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
3397            return this;
3398        }
3399
3400        /**
3401         * Get a hint that this notification's icon should not be displayed.
3402         * @return {@code true} if this icon should not be displayed, false otherwise.
3403         * The default value is {@code false} if this was never set.
3404         */
3405        public boolean getHintHideIcon() {
3406            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
3407        }
3408
3409        /**
3410         * Set a visual hint that only the background image of this notification should be
3411         * displayed, and other semantic content should be hidden. This hint is only applicable
3412         * to sub-pages added using {@link #addPage}.
3413         */
3414        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
3415            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
3416            return this;
3417        }
3418
3419        /**
3420         * Get a visual hint that only the background image of this notification should be
3421         * displayed, and other semantic content should be hidden. This hint is only applicable
3422         * to sub-pages added using {@link NotificationCompat.WearableExtender#addPage}.
3423         */
3424        public boolean getHintShowBackgroundOnly() {
3425            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
3426        }
3427
3428        /**
3429         * Set a hint that this notification's background should not be clipped if possible,
3430         * and should instead be resized to fully display on the screen, retaining the aspect
3431         * ratio of the image. This can be useful for images like barcodes or qr codes.
3432         * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
3433         * @return this object for method chaining
3434         */
3435        public WearableExtender setHintAvoidBackgroundClipping(
3436                boolean hintAvoidBackgroundClipping) {
3437            setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
3438            return this;
3439        }
3440
3441        /**
3442         * Get a hint that this notification's background should not be clipped if possible,
3443         * and should instead be resized to fully display on the screen, retaining the aspect
3444         * ratio of the image. This can be useful for images like barcodes or qr codes.
3445         * @return {@code true} if it's ok if the background is clipped on the screen, false
3446         * otherwise. The default value is {@code false} if this was never set.
3447         */
3448        public boolean getHintAvoidBackgroundClipping() {
3449            return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
3450        }
3451
3452        /**
3453         * Set a hint that the screen should remain on for at least this duration when
3454         * this notification is displayed on the screen.
3455         * @param timeout The requested screen timeout in milliseconds. Can also be either
3456         *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
3457         * @return this object for method chaining
3458         */
3459        public WearableExtender setHintScreenTimeout(int timeout) {
3460            mHintScreenTimeout = timeout;
3461            return this;
3462        }
3463
3464        /**
3465         * Get the duration, in milliseconds, that the screen should remain on for
3466         * when this notification is displayed.
3467         * @return the duration in milliseconds if > 0, or either one of the sentinel values
3468         *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
3469         */
3470        public int getHintScreenTimeout() {
3471            return mHintScreenTimeout;
3472        }
3473
3474        /**
3475         * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
3476         * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
3477         * qr codes, as well as other simple black-and-white tickets.
3478         * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
3479         * @return this object for method chaining
3480         */
3481        public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
3482            setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
3483            return this;
3484        }
3485
3486        /**
3487         * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
3488         * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
3489         * qr codes, as well as other simple black-and-white tickets.
3490         * @return {@code true} if it should be displayed in ambient, false otherwise
3491         * otherwise. The default value is {@code false} if this was never set.
3492         */
3493        public boolean getHintAmbientBigPicture() {
3494            return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
3495        }
3496
3497        /**
3498         * Set a hint that this notification's content intent will launch an {@link Activity}
3499         * directly, telling the platform that it can generate the appropriate transitions.
3500         * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
3501         * an activity and transitions should be generated, false otherwise.
3502         * @return this object for method chaining
3503         */
3504        public WearableExtender setHintContentIntentLaunchesActivity(
3505                boolean hintContentIntentLaunchesActivity) {
3506            setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
3507            return this;
3508        }
3509
3510        /**
3511         * Get a hint that this notification's content intent will launch an {@link Activity}
3512         * directly, telling the platform that it can generate the appropriate transitions
3513         * @return {@code true} if the content intent will launch an activity and transitions should
3514         * be generated, false otherwise. The default value is {@code false} if this was never set.
3515         */
3516        public boolean getHintContentIntentLaunchesActivity() {
3517            return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
3518        }
3519
3520        /**
3521         * When you post a notification, if you set the dismissal id field, then when that
3522         * notification is canceled, notifications on other wearables and the paired Android phone
3523         * having that same dismissal id will also be canceled.  Note that this only works if you
3524         * have notification bridge mode set to NO_BRIDGING in your Wear app manifest.  See
3525         * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
3526         * Notifications</a> for more information on how to use the bridge mode feature.
3527         * @param dismissalId the dismissal id of the notification.
3528         * @return this object for method chaining
3529         */
3530        public WearableExtender setDismissalId(String dismissalId) {
3531            mDismissalId = dismissalId;
3532            return this;
3533        }
3534
3535        /**
3536         * Returns the dismissal id of the notification.
3537         * @return the dismissal id of the notification or null if it has not been set.
3538         */
3539        public String getDismissalId() {
3540            return mDismissalId;
3541        }
3542
3543        private void setFlag(int mask, boolean value) {
3544            if (value) {
3545                mFlags |= mask;
3546            } else {
3547                mFlags &= ~mask;
3548            }
3549        }
3550    }
3551
3552    /**
3553     * <p>Helper class to add Android Auto extensions to notifications. To create a notification
3554     * with car extensions:
3555     *
3556     * <ol>
3557     *  <li>Create an {@link NotificationCompat.Builder}, setting any desired
3558     *  properties.
3559     *  <li>Create a {@link CarExtender}.
3560     *  <li>Set car-specific properties using the {@code add} and {@code set} methods of
3561     *  {@link CarExtender}.
3562     *  <li>Call {@link android.support.v4.app.NotificationCompat.Builder#extend(NotificationCompat.Extender)}
3563     *  to apply the extensions to a notification.
3564     *  <li>Post the notification to the notification system with the
3565     *  {@code NotificationManagerCompat.notify(...)} methods and not the
3566     *  {@code NotificationManager.notify(...)} methods.
3567     * </ol>
3568     *
3569     * <pre class="prettyprint">
3570     * Notification notification = new NotificationCompat.Builder(context)
3571     *         ...
3572     *         .extend(new CarExtender()
3573     *                 .set*(...))
3574     *         .build();
3575     * </pre>
3576     *
3577     * <p>Car extensions can be accessed on an existing notification by using the
3578     * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
3579     * to access values.
3580     */
3581    public static final class CarExtender implements Extender {
3582        private static final String TAG = "CarExtender";
3583
3584        private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
3585        private static final String EXTRA_LARGE_ICON = "large_icon";
3586        private static final String EXTRA_CONVERSATION = "car_conversation";
3587        private static final String EXTRA_COLOR = "app_color";
3588
3589        private Bitmap mLargeIcon;
3590        private UnreadConversation mUnreadConversation;
3591        private int mColor = NotificationCompat.COLOR_DEFAULT;
3592
3593        /**
3594         * Create a {@link CarExtender} with default options.
3595         */
3596        public CarExtender() {
3597        }
3598
3599        /**
3600         * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
3601         *
3602         * @param notif The notification from which to copy options.
3603         */
3604        public CarExtender(Notification notif) {
3605            if (Build.VERSION.SDK_INT < 21) {
3606                return;
3607            }
3608
3609            Bundle carBundle = getExtras(notif)==null ?
3610                    null : getExtras(notif).getBundle(EXTRA_CAR_EXTENDER);
3611            if (carBundle != null) {
3612                mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
3613                mColor = carBundle.getInt(EXTRA_COLOR, NotificationCompat.COLOR_DEFAULT);
3614
3615                Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
3616                mUnreadConversation = (UnreadConversation) IMPL.getUnreadConversationFromBundle(
3617                        b, UnreadConversation.FACTORY, RemoteInput.FACTORY);
3618            }
3619        }
3620
3621        /**
3622         * Apply car extensions to a notification that is being built. This is typically called by
3623         * the {@link android.support.v4.app.NotificationCompat.Builder#extend(NotificationCompat.Extender)}
3624         * method of {@link NotificationCompat.Builder}.
3625         */
3626        @Override
3627        public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
3628            if (Build.VERSION.SDK_INT < 21) {
3629                return builder;
3630            }
3631
3632            Bundle carExtensions = new Bundle();
3633
3634            if (mLargeIcon != null) {
3635                carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
3636            }
3637            if (mColor != NotificationCompat.COLOR_DEFAULT) {
3638                carExtensions.putInt(EXTRA_COLOR, mColor);
3639            }
3640
3641            if (mUnreadConversation != null) {
3642                Bundle b = IMPL.getBundleForUnreadConversation(mUnreadConversation);
3643                carExtensions.putBundle(EXTRA_CONVERSATION, b);
3644            }
3645
3646            builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
3647            return builder;
3648        }
3649
3650        /**
3651         * Sets the accent color to use when Android Auto presents the notification.
3652         *
3653         * Android Auto uses the color set with {@link android.support.v4.app.NotificationCompat.Builder#setColor(int)}
3654         * to accent the displayed notification. However, not all colors are acceptable in an
3655         * automotive setting. This method can be used to override the color provided in the
3656         * notification in such a situation.
3657         */
3658        public CarExtender setColor(@ColorInt int color) {
3659            mColor = color;
3660            return this;
3661        }
3662
3663        /**
3664         * Gets the accent color.
3665         *
3666         * @see #setColor
3667         */
3668        @ColorInt
3669        public int getColor() {
3670            return mColor;
3671        }
3672
3673        /**
3674         * Sets the large icon of the car notification.
3675         *
3676         * If no large icon is set in the extender, Android Auto will display the icon
3677         * specified by {@link android.support.v4.app.NotificationCompat.Builder#setLargeIcon(android.graphics.Bitmap)}
3678         *
3679         * @param largeIcon The large icon to use in the car notification.
3680         * @return This object for method chaining.
3681         */
3682        public CarExtender setLargeIcon(Bitmap largeIcon) {
3683            mLargeIcon = largeIcon;
3684            return this;
3685        }
3686
3687        /**
3688         * Gets the large icon used in this car notification, or null if no icon has been set.
3689         *
3690         * @return The large icon for the car notification.
3691         * @see CarExtender#setLargeIcon
3692         */
3693        public Bitmap getLargeIcon() {
3694            return mLargeIcon;
3695        }
3696
3697        /**
3698         * Sets the unread conversation in a message notification.
3699         *
3700         * @param unreadConversation The unread part of the conversation this notification conveys.
3701         * @return This object for method chaining.
3702         */
3703        public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
3704            mUnreadConversation = unreadConversation;
3705            return this;
3706        }
3707
3708        /**
3709         * Returns the unread conversation conveyed by this notification.
3710         * @see #setUnreadConversation(UnreadConversation)
3711         */
3712        public UnreadConversation getUnreadConversation() {
3713            return mUnreadConversation;
3714        }
3715
3716        /**
3717         * A class which holds the unread messages from a conversation.
3718         */
3719        public static class UnreadConversation extends NotificationCompatBase.UnreadConversation {
3720            private final String[] mMessages;
3721            private final RemoteInput mRemoteInput;
3722            private final PendingIntent mReplyPendingIntent;
3723            private final PendingIntent mReadPendingIntent;
3724            private final String[] mParticipants;
3725            private final long mLatestTimestamp;
3726
3727            UnreadConversation(String[] messages, RemoteInput remoteInput,
3728                    PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
3729                    String[] participants, long latestTimestamp) {
3730                mMessages = messages;
3731                mRemoteInput = remoteInput;
3732                mReadPendingIntent = readPendingIntent;
3733                mReplyPendingIntent = replyPendingIntent;
3734                mParticipants = participants;
3735                mLatestTimestamp = latestTimestamp;
3736            }
3737
3738            /**
3739             * Gets the list of messages conveyed by this notification.
3740             */
3741            @Override
3742            public String[] getMessages() {
3743                return mMessages;
3744            }
3745
3746            /**
3747             * Gets the remote input that will be used to convey the response to a message list, or
3748             * null if no such remote input exists.
3749             */
3750            @Override
3751            public RemoteInput getRemoteInput() {
3752                return mRemoteInput;
3753            }
3754
3755            /**
3756             * Gets the pending intent that will be triggered when the user replies to this
3757             * notification.
3758             */
3759            @Override
3760            public PendingIntent getReplyPendingIntent() {
3761                return mReplyPendingIntent;
3762            }
3763
3764            /**
3765             * Gets the pending intent that Android Auto will send after it reads aloud all messages
3766             * in this object's message list.
3767             */
3768            @Override
3769            public PendingIntent getReadPendingIntent() {
3770                return mReadPendingIntent;
3771            }
3772
3773            /**
3774             * Gets the participants in the conversation.
3775             */
3776            @Override
3777            public String[] getParticipants() {
3778                return mParticipants;
3779            }
3780
3781            /**
3782             * Gets the firs participant in the conversation.
3783             */
3784            @Override
3785            public String getParticipant() {
3786                return mParticipants.length > 0 ? mParticipants[0] : null;
3787            }
3788
3789            /**
3790             * Gets the timestamp of the conversation.
3791             */
3792            @Override
3793            public long getLatestTimestamp() {
3794                return mLatestTimestamp;
3795            }
3796
3797            /** @hide */
3798            static final Factory FACTORY = new Factory() {
3799                @Override
3800                public UnreadConversation build(
3801                        String[] messages, RemoteInputCompatBase.RemoteInput remoteInput,
3802                        PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
3803                        String[] participants, long latestTimestamp) {
3804                    return new UnreadConversation(
3805                            messages, (RemoteInput) remoteInput, replyPendingIntent,
3806                            readPendingIntent,
3807                            participants, latestTimestamp);
3808                }
3809            };
3810
3811            /**
3812             * Builder class for {@link CarExtender.UnreadConversation} objects.
3813             */
3814            public static class Builder {
3815                private final List<String> mMessages = new ArrayList<String>();
3816                private final String mParticipant;
3817                private RemoteInput mRemoteInput;
3818                private PendingIntent mReadPendingIntent;
3819                private PendingIntent mReplyPendingIntent;
3820                private long mLatestTimestamp;
3821
3822                /**
3823                 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
3824                 *
3825                 * @param name The name of the other participant in the conversation.
3826                 */
3827                public Builder(String name) {
3828                    mParticipant = name;
3829                }
3830
3831                /**
3832                 * Appends a new unread message to the list of messages for this conversation.
3833                 *
3834                 * The messages should be added from oldest to newest.
3835                 *
3836                 * @param message The text of the new unread message.
3837                 * @return This object for method chaining.
3838                 */
3839                public Builder addMessage(String message) {
3840                    mMessages.add(message);
3841                    return this;
3842                }
3843
3844                /**
3845                 * Sets the pending intent and remote input which will convey the reply to this
3846                 * notification.
3847                 *
3848                 * @param pendingIntent The pending intent which will be triggered on a reply.
3849                 * @param remoteInput The remote input parcelable which will carry the reply.
3850                 * @return This object for method chaining.
3851                 *
3852                 * @see CarExtender.UnreadConversation#getRemoteInput
3853                 * @see CarExtender.UnreadConversation#getReplyPendingIntent
3854                 */
3855                public Builder setReplyAction(
3856                        PendingIntent pendingIntent, RemoteInput remoteInput) {
3857                    mRemoteInput = remoteInput;
3858                    mReplyPendingIntent = pendingIntent;
3859
3860                    return this;
3861                }
3862
3863                /**
3864                 * Sets the pending intent that will be sent once the messages in this notification
3865                 * are read.
3866                 *
3867                 * @param pendingIntent The pending intent to use.
3868                 * @return This object for method chaining.
3869                 */
3870                public Builder setReadPendingIntent(PendingIntent pendingIntent) {
3871                    mReadPendingIntent = pendingIntent;
3872                    return this;
3873                }
3874
3875                /**
3876                 * Sets the timestamp of the most recent message in an unread conversation.
3877                 *
3878                 * If a messaging notification has been posted by your application and has not
3879                 * yet been cancelled, posting a later notification with the same id and tag
3880                 * but without a newer timestamp may result in Android Auto not displaying a
3881                 * heads up notification for the later notification.
3882                 *
3883                 * @param timestamp The timestamp of the most recent message in the conversation.
3884                 * @return This object for method chaining.
3885                 */
3886                public Builder setLatestTimestamp(long timestamp) {
3887                    mLatestTimestamp = timestamp;
3888                    return this;
3889                }
3890
3891                /**
3892                 * Builds a new unread conversation object.
3893                 *
3894                 * @return The new unread conversation object.
3895                 */
3896                public UnreadConversation build() {
3897                    String[] messages = mMessages.toArray(new String[mMessages.size()]);
3898                    String[] participants = { mParticipant };
3899                    return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
3900                            mReadPendingIntent, participants, mLatestTimestamp);
3901                }
3902            }
3903        }
3904    }
3905
3906
3907    /**
3908     * Get an array of Notification objects from a parcelable array bundle field.
3909     * Update the bundle to have a typed array so fetches in the future don't need
3910     * to do an array copy.
3911     */
3912    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
3913        Parcelable[] array = bundle.getParcelableArray(key);
3914        if (array instanceof Notification[] || array == null) {
3915            return (Notification[]) array;
3916        }
3917        Notification[] typedArray = new Notification[array.length];
3918        for (int i = 0; i < array.length; i++) {
3919            typedArray[i] = (Notification) array[i];
3920        }
3921        bundle.putParcelableArray(key, typedArray);
3922        return typedArray;
3923    }
3924
3925    /**
3926     * Gets the {@link Notification#extras} field from a notification in a backwards
3927     * compatible manner. Extras field was supported from JellyBean (Api level 16)
3928     * forwards. This function will return null on older api levels.
3929     */
3930    public static Bundle getExtras(Notification notif) {
3931        return IMPL.getExtras(notif);
3932    }
3933
3934    /**
3935     * Get the number of actions in this notification in a backwards compatible
3936     * manner. Actions were supported from JellyBean (Api level 16) forwards.
3937     */
3938    public static int getActionCount(Notification notif) {
3939        return IMPL.getActionCount(notif);
3940    }
3941
3942    /**
3943     * Get an action on this notification in a backwards compatible
3944     * manner. Actions were supported from JellyBean (Api level 16) forwards.
3945     * @param notif The notification to inspect.
3946     * @param actionIndex The index of the action to retrieve.
3947     */
3948    public static Action getAction(Notification notif, int actionIndex) {
3949        return IMPL.getAction(notif, actionIndex);
3950    }
3951
3952    /**
3953     * Get the category of this notification in a backwards compatible
3954     * manner.
3955     * @param notif The notification to inspect.
3956     */
3957    public static String getCategory(Notification notif) {
3958        return IMPL.getCategory(notif);
3959    }
3960
3961    /**
3962     * Get whether or not this notification is only relevant to the current device.
3963     *
3964     * <p>Some notifications can be bridged to other devices for remote display.
3965     * If this hint is set, it is recommend that this notification not be bridged.
3966     */
3967    public static boolean getLocalOnly(Notification notif) {
3968        return IMPL.getLocalOnly(notif);
3969    }
3970
3971    /**
3972     * Get the key used to group this notification into a cluster or stack
3973     * with other notifications on devices which support such rendering.
3974     */
3975    public static String getGroup(Notification notif) {
3976        return IMPL.getGroup(notif);
3977    }
3978
3979    /**
3980     * Get whether this notification to be the group summary for a group of notifications.
3981     * Grouped notifications may display in a cluster or stack on devices which
3982     * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
3983     * @return Whether this notification is a group summary.
3984     */
3985    public static boolean isGroupSummary(Notification notif) {
3986        return IMPL.isGroupSummary(notif);
3987    }
3988
3989    /**
3990     * Get a sort key that orders this notification among other notifications from the
3991     * same package. This can be useful if an external sort was already applied and an app
3992     * would like to preserve this. Notifications will be sorted lexicographically using this
3993     * value, although providing different priorities in addition to providing sort key may
3994     * cause this value to be ignored.
3995     *
3996     * <p>This sort key can also be used to order members of a notification group. See
3997     * {@link Builder#setGroup}.
3998     *
3999     * @see String#compareTo(String)
4000     */
4001    public static String getSortKey(Notification notif) {
4002        return IMPL.getSortKey(notif);
4003    }
4004}
4005