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