NotificationCompat.java revision c9e4b6fcfcc361f65f2f579b2c001ad99a194638
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    }
457
458    static class NotificationCompatImplBase implements NotificationCompatImpl {
459        @Override
460        public Notification build(Builder b) {
461            Notification result = b.mNotification;
462            result.setLatestEventInfo(b.mContext, b.mContentTitle,
463                    b.mContentText, b.mContentIntent);
464            // translate high priority requests into legacy flag
465            if (b.mPriority > PRIORITY_DEFAULT) {
466                result.flags |= FLAG_HIGH_PRIORITY;
467            }
468            return result;
469        }
470
471        @Override
472        public Bundle getExtras(Notification n) {
473            return null;
474        }
475
476        @Override
477        public int getActionCount(Notification n) {
478            return 0;
479        }
480
481        @Override
482        public Action getAction(Notification n, int actionIndex) {
483            return null;
484        }
485
486        @Override
487        public Action[] getActionsFromParcelableArrayList(
488                ArrayList<Parcelable> parcelables) {
489            return null;
490        }
491
492        @Override
493        public ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions) {
494            return null;
495        }
496
497        @Override
498        public String getCategory(Notification n) {
499            return null;
500        }
501
502        @Override
503        public boolean getLocalOnly(Notification n) {
504            return false;
505        }
506
507        @Override
508        public String getGroup(Notification n) {
509            return null;
510        }
511
512        @Override
513        public boolean isGroupSummary(Notification n) {
514            return false;
515        }
516
517        @Override
518        public String getSortKey(Notification n) {
519            return null;
520        }
521    }
522
523    static class NotificationCompatImplGingerbread extends NotificationCompatImplBase {
524        @Override
525        public Notification build(Builder b) {
526            Notification result = b.mNotification;
527            result.setLatestEventInfo(b.mContext, b.mContentTitle,
528                    b.mContentText, b.mContentIntent);
529            result = NotificationCompatGingerbread.add(result, b.mContext,
530                    b.mContentTitle, b.mContentText, b.mContentIntent, b.mFullScreenIntent);
531            // translate high priority requests into legacy flag
532            if (b.mPriority > PRIORITY_DEFAULT) {
533                result.flags |= FLAG_HIGH_PRIORITY;
534            }
535            return result;
536        }
537    }
538
539    static class NotificationCompatImplHoneycomb extends NotificationCompatImplBase {
540        @Override
541        public Notification build(Builder b) {
542            return NotificationCompatHoneycomb.add(b.mContext, b.mNotification,
543                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
544                    b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon);
545        }
546    }
547
548    static class NotificationCompatImplIceCreamSandwich extends NotificationCompatImplBase {
549        @Override
550        public Notification build(Builder b) {
551            return NotificationCompatIceCreamSandwich.add(b.mContext, b.mNotification,
552                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
553                    b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
554                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate);
555        }
556    }
557
558    static class NotificationCompatImplJellybean extends NotificationCompatImplBase {
559        @Override
560        public Notification build(Builder b) {
561            NotificationCompatJellybean.Builder builder = new NotificationCompatJellybean.Builder(
562                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
563                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
564                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate,
565                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mExtras,
566                    b.mGroupKey, b.mGroupSummary, b.mSortKey);
567            addActionsToBuilder(builder, b.mActions);
568            addStyleToBuilderJellybean(builder, b.mStyle);
569            return builder.build();
570        }
571
572        @Override
573        public Bundle getExtras(Notification n) {
574            return NotificationCompatJellybean.getExtras(n);
575        }
576
577        @Override
578        public int getActionCount(Notification n) {
579            return NotificationCompatJellybean.getActionCount(n);
580        }
581
582        @Override
583        public Action getAction(Notification n, int actionIndex) {
584            return (Action) NotificationCompatJellybean.getAction(n, actionIndex, Action.FACTORY,
585                    RemoteInput.FACTORY);
586        }
587
588        @Override
589        public Action[] getActionsFromParcelableArrayList(
590                ArrayList<Parcelable> parcelables) {
591            return (Action[]) NotificationCompatJellybean.getActionsFromParcelableArrayList(
592                    parcelables, Action.FACTORY, RemoteInput.FACTORY);
593        }
594
595        @Override
596        public ArrayList<Parcelable> getParcelableArrayListForActions(
597                Action[] actions) {
598            return NotificationCompatJellybean.getParcelableArrayListForActions(actions);
599        }
600
601        @Override
602        public boolean getLocalOnly(Notification n) {
603            return NotificationCompatJellybean.getLocalOnly(n);
604        }
605
606        @Override
607        public String getGroup(Notification n) {
608            return NotificationCompatJellybean.getGroup(n);
609        }
610
611        @Override
612        public boolean isGroupSummary(Notification n) {
613            return NotificationCompatJellybean.isGroupSummary(n);
614        }
615
616        @Override
617        public String getSortKey(Notification n) {
618            return NotificationCompatJellybean.getSortKey(n);
619        }
620    }
621
622    static class NotificationCompatImplKitKat extends NotificationCompatImplJellybean {
623        @Override
624        public Notification build(Builder b) {
625            NotificationCompatKitKat.Builder builder = new NotificationCompatKitKat.Builder(
626                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
627                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
628                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen,
629                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly,
630                    b.mPeople, b.mExtras, b.mGroupKey, b.mGroupSummary, b.mSortKey);
631            addActionsToBuilder(builder, b.mActions);
632            addStyleToBuilderJellybean(builder, b.mStyle);
633            return builder.build();
634        }
635
636        @Override
637        public Bundle getExtras(Notification n) {
638            return NotificationCompatKitKat.getExtras(n);
639        }
640
641        @Override
642        public int getActionCount(Notification n) {
643            return NotificationCompatKitKat.getActionCount(n);
644        }
645
646        @Override
647        public Action getAction(Notification n, int actionIndex) {
648            return (Action) NotificationCompatKitKat.getAction(n, actionIndex, Action.FACTORY,
649                    RemoteInput.FACTORY);
650        }
651
652        @Override
653        public boolean getLocalOnly(Notification n) {
654            return NotificationCompatKitKat.getLocalOnly(n);
655        }
656
657        @Override
658        public String getGroup(Notification n) {
659            return NotificationCompatKitKat.getGroup(n);
660        }
661
662        @Override
663        public boolean isGroupSummary(Notification n) {
664            return NotificationCompatKitKat.isGroupSummary(n);
665        }
666
667        @Override
668        public String getSortKey(Notification n) {
669            return NotificationCompatKitKat.getSortKey(n);
670        }
671    }
672
673    static class NotificationCompatImplApi20 extends NotificationCompatImplKitKat {
674        @Override
675        public Notification build(Builder b) {
676            NotificationCompatApi20.Builder builder = new NotificationCompatApi20.Builder(
677                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
678                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
679                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen,
680                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mPeople, b.mExtras,
681                    b.mGroupKey, b.mGroupSummary, b.mSortKey);
682            addActionsToBuilder(builder, b.mActions);
683            addStyleToBuilderJellybean(builder, b.mStyle);
684            return builder.build();
685        }
686
687        @Override
688        public Action getAction(Notification n, int actionIndex) {
689            return (Action) NotificationCompatApi20.getAction(n, actionIndex, Action.FACTORY,
690                    RemoteInput.FACTORY);
691        }
692
693        @Override
694        public Action[] getActionsFromParcelableArrayList(
695                ArrayList<Parcelable> parcelables) {
696            return (Action[]) NotificationCompatApi20.getActionsFromParcelableArrayList(
697                    parcelables, Action.FACTORY, RemoteInput.FACTORY);
698        }
699
700        @Override
701        public ArrayList<Parcelable> getParcelableArrayListForActions(
702                Action[] actions) {
703            return NotificationCompatApi20.getParcelableArrayListForActions(actions);
704        }
705
706        @Override
707        public boolean getLocalOnly(Notification n) {
708            return NotificationCompatApi20.getLocalOnly(n);
709        }
710
711        @Override
712        public String getGroup(Notification n) {
713            return NotificationCompatApi20.getGroup(n);
714        }
715
716        @Override
717        public boolean isGroupSummary(Notification n) {
718            return NotificationCompatApi20.isGroupSummary(n);
719        }
720
721        @Override
722        public String getSortKey(Notification n) {
723            return NotificationCompatApi20.getSortKey(n);
724        }
725    }
726
727    static class NotificationCompatImplApi21 extends NotificationCompatImplApi20 {
728        @Override
729        public Notification build(Builder b) {
730            NotificationCompatApi21.Builder builder = new NotificationCompatApi21.Builder(
731                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
732                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
733                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen,
734                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory,
735                    b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion,
736                    b.mGroupKey, b.mGroupSummary, b.mSortKey);
737            addActionsToBuilder(builder, b.mActions);
738            addStyleToBuilderJellybean(builder, b.mStyle);
739            return builder.build();
740        }
741
742        @Override
743        public String getCategory(Notification notif) {
744            return NotificationCompatApi21.getCategory(notif);
745        }
746    }
747
748    private static void addActionsToBuilder(NotificationBuilderWithActions builder,
749            ArrayList<Action> actions) {
750        for (Action action : actions) {
751            builder.addAction(action);
752        }
753    }
754
755    private static void addStyleToBuilderJellybean(NotificationBuilderWithBuilderAccessor builder,
756            Style style) {
757        if (style != null) {
758            if (style instanceof BigTextStyle) {
759                BigTextStyle bigTextStyle = (BigTextStyle) style;
760                NotificationCompatJellybean.addBigTextStyle(builder,
761                        bigTextStyle.mBigContentTitle,
762                        bigTextStyle.mSummaryTextSet,
763                        bigTextStyle.mSummaryText,
764                        bigTextStyle.mBigText);
765            } else if (style instanceof InboxStyle) {
766                InboxStyle inboxStyle = (InboxStyle) style;
767                NotificationCompatJellybean.addInboxStyle(builder,
768                        inboxStyle.mBigContentTitle,
769                        inboxStyle.mSummaryTextSet,
770                        inboxStyle.mSummaryText,
771                        inboxStyle.mTexts);
772            } else if (style instanceof BigPictureStyle) {
773                BigPictureStyle bigPictureStyle = (BigPictureStyle) style;
774                NotificationCompatJellybean.addBigPictureStyle(builder,
775                        bigPictureStyle.mBigContentTitle,
776                        bigPictureStyle.mSummaryTextSet,
777                        bigPictureStyle.mSummaryText,
778                        bigPictureStyle.mPicture,
779                        bigPictureStyle.mBigLargeIcon,
780                        bigPictureStyle.mBigLargeIconSet);
781            }
782        }
783    }
784
785    static {
786        if (Build.VERSION.SDK_INT >= 21) {
787            IMPL = new NotificationCompatImplApi21();
788        } else if (Build.VERSION.SDK_INT >= 20) {
789            IMPL = new NotificationCompatImplApi20();
790        } else if (Build.VERSION.SDK_INT >= 19) {
791            IMPL = new NotificationCompatImplKitKat();
792        } else if (Build.VERSION.SDK_INT >= 16) {
793            IMPL = new NotificationCompatImplJellybean();
794        } else if (Build.VERSION.SDK_INT >= 14) {
795            IMPL = new NotificationCompatImplIceCreamSandwich();
796        } else if (Build.VERSION.SDK_INT >= 11) {
797            IMPL = new NotificationCompatImplHoneycomb();
798        } else if (Build.VERSION.SDK_INT >= 9) {
799            IMPL = new NotificationCompatImplGingerbread();
800        } else {
801            IMPL = new NotificationCompatImplBase();
802        }
803    }
804
805    /**
806     * Builder class for {@link NotificationCompat} objects.  Allows easier control over
807     * all the flags, as well as help constructing the typical notification layouts.
808     * <p>
809     * On platform versions that don't offer expanded notifications, methods that depend on
810     * expanded notifications have no effect.
811     * </p>
812     * <p>
813     * For example, action buttons won't appear on platforms prior to Android 4.1. Action
814     * buttons depend on expanded notifications, which are only available in Android 4.1
815     * and later.
816     * <p>
817     * For this reason, you should always ensure that UI controls in a notification are also
818     * available in an {@link android.app.Activity} in your app, and you should always start that
819     * {@link android.app.Activity} when users click the notification. To do this, use the
820     * {@link NotificationCompat.Builder#setContentIntent setContentIntent()}
821     * method.
822     * </p>
823     *
824     */
825    public static class Builder {
826        /**
827         * Maximum length of CharSequences accepted by Builder and friends.
828         *
829         * <p>
830         * Avoids spamming the system with overly large strings such as full e-mails.
831         */
832        private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
833
834        Context mContext;
835
836        CharSequence mContentTitle;
837        CharSequence mContentText;
838        PendingIntent mContentIntent;
839        PendingIntent mFullScreenIntent;
840        RemoteViews mTickerView;
841        Bitmap mLargeIcon;
842        CharSequence mContentInfo;
843        int mNumber;
844        int mPriority;
845        boolean mShowWhen = true;
846        boolean mUseChronometer;
847        Style mStyle;
848        CharSequence mSubText;
849        int mProgressMax;
850        int mProgress;
851        boolean mProgressIndeterminate;
852        String mGroupKey;
853        boolean mGroupSummary;
854        String mSortKey;
855        ArrayList<Action> mActions = new ArrayList<Action>();
856        boolean mLocalOnly = false;
857        String mCategory;
858        Bundle mExtras;
859        int mColor = COLOR_DEFAULT;
860        int mVisibility = VISIBILITY_PRIVATE;
861        Notification mPublicVersion;
862
863        Notification mNotification = new Notification();
864        public ArrayList<String> mPeople;
865
866        /**
867         * Constructor.
868         *
869         * Automatically sets the when field to {@link System#currentTimeMillis()
870         * System.currentTimeMillis()} and the audio stream to the
871         * {@link Notification#STREAM_DEFAULT}.
872         *
873         * @param context A {@link Context} that will be used to construct the
874         *      RemoteViews. The Context will not be held past the lifetime of this
875         *      Builder object.
876         */
877        public Builder(Context context) {
878            mContext = context;
879
880            // Set defaults to match the defaults of a Notification
881            mNotification.when = System.currentTimeMillis();
882            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
883            mPriority = PRIORITY_DEFAULT;
884            mPeople = new ArrayList<String>();
885        }
886
887        /**
888         * Set the time that the event occurred.  Notifications in the panel are
889         * sorted by this time.
890         */
891        public Builder setWhen(long when) {
892            mNotification.when = when;
893            return this;
894        }
895
896        /**
897         * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
898         * in the content view.
899         */
900        public Builder setShowWhen(boolean show) {
901            mShowWhen = show;
902            return this;
903        }
904
905        /**
906         * Show the {@link Notification#when} field as a stopwatch.
907         *
908         * Instead of presenting <code>when</code> as a timestamp, the notification will show an
909         * automatically updating display of the minutes and seconds since <code>when</code>.
910         *
911         * Useful when showing an elapsed time (like an ongoing phone call).
912         *
913         * @see android.widget.Chronometer
914         * @see Notification#when
915         */
916        public Builder setUsesChronometer(boolean b) {
917            mUseChronometer = b;
918            return this;
919        }
920
921        /**
922         * Set the small icon to use in the notification layouts.  Different classes of devices
923         * may return different sizes.  See the UX guidelines for more information on how to
924         * design these icons.
925         *
926         * @param icon A resource ID in the application's package of the drawble to use.
927         */
928        public Builder setSmallIcon(int icon) {
929            mNotification.icon = icon;
930            return this;
931        }
932
933        /**
934         * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
935         * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
936         * LevelListDrawable}.
937         *
938         * @param icon A resource ID in the application's package of the drawble to use.
939         * @param level The level to use for the icon.
940         *
941         * @see android.graphics.drawable.LevelListDrawable
942         */
943        public Builder setSmallIcon(int icon, int level) {
944            mNotification.icon = icon;
945            mNotification.iconLevel = level;
946            return this;
947        }
948
949        /**
950         * Set the title (first row) of the notification, in a standard notification.
951         */
952        public Builder setContentTitle(CharSequence title) {
953            mContentTitle = limitCharSequenceLength(title);
954            return this;
955        }
956
957        /**
958         * Set the text (second row) of the notification, in a standard notification.
959         */
960        public Builder setContentText(CharSequence text) {
961            mContentText = limitCharSequenceLength(text);
962            return this;
963        }
964
965        /**
966         * Set the third line of text in the platform notification template.
967         * Don't use if you're also using {@link #setProgress(int, int, boolean)};
968         * they occupy the same location in the standard template.
969         * <br>
970         * If the platform does not provide large-format notifications, this method has no effect.
971         * The third line of text only appears in expanded view.
972         * <br>
973         */
974        public Builder setSubText(CharSequence text) {
975            mSubText = limitCharSequenceLength(text);
976            return this;
977        }
978
979        /**
980         * Set the large number at the right-hand side of the notification.  This is
981         * equivalent to setContentInfo, although it might show the number in a different
982         * font size for readability.
983         */
984        public Builder setNumber(int number) {
985            mNumber = number;
986            return this;
987        }
988
989        /**
990         * Set the large text at the right-hand side of the notification.
991         */
992        public Builder setContentInfo(CharSequence info) {
993            mContentInfo = limitCharSequenceLength(info);
994            return this;
995        }
996
997        /**
998         * Set the progress this notification represents, which may be
999         * represented as a {@link android.widget.ProgressBar}.
1000         */
1001        public Builder setProgress(int max, int progress, boolean indeterminate) {
1002            mProgressMax = max;
1003            mProgress = progress;
1004            mProgressIndeterminate = indeterminate;
1005            return this;
1006        }
1007
1008        /**
1009         * Supply a custom RemoteViews to use instead of the standard one.
1010         */
1011        public Builder setContent(RemoteViews views) {
1012            mNotification.contentView = views;
1013            return this;
1014        }
1015
1016        /**
1017         * Supply a {@link PendingIntent} to send when the notification is clicked.
1018         * If you do not supply an intent, you can now add PendingIntents to individual
1019         * views to be launched when clicked by calling {@link RemoteViews#setOnClickPendingIntent
1020         * RemoteViews.setOnClickPendingIntent(int,PendingIntent)}.  Be sure to
1021         * read {@link Notification#contentIntent Notification.contentIntent} for
1022         * how to correctly use this.
1023         */
1024        public Builder setContentIntent(PendingIntent intent) {
1025            mContentIntent = intent;
1026            return this;
1027        }
1028
1029        /**
1030         * Supply a {@link PendingIntent} to send when the notification is cleared by the user
1031         * directly from the notification panel.  For example, this intent is sent when the user
1032         * clicks the "Clear all" button, or the individual "X" buttons on notifications.  This
1033         * intent is not sent when the application calls
1034         * {@link android.app.NotificationManager#cancel NotificationManager.cancel(int)}.
1035         */
1036        public Builder setDeleteIntent(PendingIntent intent) {
1037            mNotification.deleteIntent = intent;
1038            return this;
1039        }
1040
1041        /**
1042         * An intent to launch instead of posting the notification to the status bar.
1043         * Only for use with extremely high-priority notifications demanding the user's
1044         * <strong>immediate</strong> attention, such as an incoming phone call or
1045         * alarm clock that the user has explicitly set to a particular time.
1046         * If this facility is used for something else, please give the user an option
1047         * to turn it off and use a normal notification, as this can be extremely
1048         * disruptive.
1049         *
1050         * <p>
1051         * On some platforms, the system UI may choose to display a heads-up notification,
1052         * instead of launching this intent, while the user is using the device.
1053         * </p>
1054         *
1055         * @param intent The pending intent to launch.
1056         * @param highPriority Passing true will cause this notification to be sent
1057         *          even if other notifications are suppressed.
1058         */
1059        public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
1060            mFullScreenIntent = intent;
1061            setFlag(FLAG_HIGH_PRIORITY, highPriority);
1062            return this;
1063        }
1064
1065        /**
1066         * Set the text that is displayed in the status bar when the notification first
1067         * arrives.
1068         */
1069        public Builder setTicker(CharSequence tickerText) {
1070            mNotification.tickerText = limitCharSequenceLength(tickerText);
1071            return this;
1072        }
1073
1074        /**
1075         * Set the text that is displayed in the status bar when the notification first
1076         * arrives, and also a RemoteViews object that may be displayed instead on some
1077         * devices.
1078         */
1079        public Builder setTicker(CharSequence tickerText, RemoteViews views) {
1080            mNotification.tickerText = limitCharSequenceLength(tickerText);
1081            mTickerView = views;
1082            return this;
1083        }
1084
1085        /**
1086         * Set the large icon that is shown in the ticker and notification.
1087         */
1088        public Builder setLargeIcon(Bitmap icon) {
1089            mLargeIcon = icon;
1090            return this;
1091        }
1092
1093        /**
1094         * Set the sound to play.  It will play on the default stream.
1095         *
1096         * <p>
1097         * On some platforms, a notification that is noisy is more likely to be presented
1098         * as a heads-up notification.
1099         * </p>
1100         */
1101        public Builder setSound(Uri sound) {
1102            mNotification.sound = sound;
1103            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
1104            return this;
1105        }
1106
1107        /**
1108         * Set the sound to play.  It will play on the stream you supply.
1109         *
1110         * <p>
1111         * On some platforms, a notification that is noisy is more likely to be presented
1112         * as a heads-up notification.
1113         * </p>
1114         *
1115         * @see Notification#STREAM_DEFAULT
1116         * @see AudioManager for the <code>STREAM_</code> constants.
1117         */
1118        public Builder setSound(Uri sound, int streamType) {
1119            mNotification.sound = sound;
1120            mNotification.audioStreamType = streamType;
1121            return this;
1122        }
1123
1124        /**
1125         * Set the vibration pattern to use.
1126         *
1127         * <p>
1128         * On some platforms, a notification that vibrates is more likely to be presented
1129         * as a heads-up notification.
1130         * </p>
1131         *
1132         * @see android.os.Vibrator for a discussion of the <code>pattern</code>
1133         * parameter.
1134         */
1135        public Builder setVibrate(long[] pattern) {
1136            mNotification.vibrate = pattern;
1137            return this;
1138        }
1139
1140        /**
1141         * Set the argb value that you would like the LED on the device to blnk, as well as the
1142         * rate.  The rate is specified in terms of the number of milliseconds to be on
1143         * and then the number of milliseconds to be off.
1144         */
1145        public Builder setLights(int argb, int onMs, int offMs) {
1146            mNotification.ledARGB = argb;
1147            mNotification.ledOnMS = onMs;
1148            mNotification.ledOffMS = offMs;
1149            boolean showLights = mNotification.ledOnMS != 0 && mNotification.ledOffMS != 0;
1150            mNotification.flags = (mNotification.flags & ~Notification.FLAG_SHOW_LIGHTS) |
1151                    (showLights ? Notification.FLAG_SHOW_LIGHTS : 0);
1152            return this;
1153        }
1154
1155        /**
1156         * Set whether this is an ongoing notification.
1157         *
1158         * <p>Ongoing notifications differ from regular notifications in the following ways:
1159         * <ul>
1160         *   <li>Ongoing notifications are sorted above the regular notifications in the
1161         *   notification panel.</li>
1162         *   <li>Ongoing notifications do not have an 'X' close button, and are not affected
1163         *   by the "Clear all" button.
1164         * </ul>
1165         */
1166        public Builder setOngoing(boolean ongoing) {
1167            setFlag(Notification.FLAG_ONGOING_EVENT, ongoing);
1168            return this;
1169        }
1170
1171        /**
1172         * Set this flag if you would only like the sound, vibrate
1173         * and ticker to be played if the notification is not already showing.
1174         */
1175        public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
1176            setFlag(Notification.FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
1177            return this;
1178        }
1179
1180        /**
1181         * Setting this flag will make it so the notification is automatically
1182         * canceled when the user clicks it in the panel.  The PendingIntent
1183         * set with {@link #setDeleteIntent} will be broadcast when the notification
1184         * is canceled.
1185         */
1186        public Builder setAutoCancel(boolean autoCancel) {
1187            setFlag(Notification.FLAG_AUTO_CANCEL, autoCancel);
1188            return this;
1189        }
1190
1191        /**
1192         * Set whether or not this notification is only relevant to the current device.
1193         *
1194         * <p>Some notifications can be bridged to other devices for remote display.
1195         * This hint can be set to recommend this notification not be bridged.
1196         */
1197        public Builder setLocalOnly(boolean b) {
1198            mLocalOnly = b;
1199            return this;
1200        }
1201
1202        /**
1203         * Set the notification category.
1204         *
1205         * <p>Must be one of the predefined notification categories (see the <code>CATEGORY_*</code>
1206         * constants in {@link Notification}) that best describes this notification.
1207         * May be used by the system for ranking and filtering.
1208         */
1209        public Builder setCategory(String category) {
1210            mCategory = category;
1211            return this;
1212        }
1213
1214        /**
1215         * Set the default notification options that will be used.
1216         * <p>
1217         * The value should be one or more of the following fields combined with
1218         * bitwise-or:
1219         * {@link Notification#DEFAULT_SOUND}, {@link Notification#DEFAULT_VIBRATE},
1220         * {@link Notification#DEFAULT_LIGHTS}.
1221         * <p>
1222         * For all default values, use {@link Notification#DEFAULT_ALL}.
1223         */
1224        public Builder setDefaults(int defaults) {
1225            mNotification.defaults = defaults;
1226            if ((defaults & Notification.DEFAULT_LIGHTS) != 0) {
1227                mNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
1228            }
1229            return this;
1230        }
1231
1232        private void setFlag(int mask, boolean value) {
1233            if (value) {
1234                mNotification.flags |= mask;
1235            } else {
1236                mNotification.flags &= ~mask;
1237            }
1238        }
1239
1240        /**
1241         * Set the relative priority for this notification.
1242         *
1243         * Priority is an indication of how much of the user's
1244         * valuable attention should be consumed by this
1245         * notification. Low-priority notifications may be hidden from
1246         * the user in certain situations, while the user might be
1247         * interrupted for a higher-priority notification.
1248         * The system sets a notification's priority based on various factors including the
1249         * setPriority value. The effect may differ slightly on different platforms.
1250         *
1251         * @param pri Relative priority for this notification. Must be one of
1252         *     the priority constants defined by {@link NotificationCompat}.
1253         *     Acceptable values range from {@link
1254         *     NotificationCompat#PRIORITY_MIN} (-2) to {@link
1255         *     NotificationCompat#PRIORITY_MAX} (2).
1256         */
1257        public Builder setPriority(int pri) {
1258            mPriority = pri;
1259            return this;
1260        }
1261
1262        /**
1263         * Add a person that is relevant to this notification.
1264         *
1265         * <P>
1266         * Depending on user preferences, this annotation may allow the notification to pass
1267         * through interruption filters, and to appear more prominently in the user interface.
1268         * </P>
1269         *
1270         * <P>
1271         * The person should be specified by the {@code String} representation of a
1272         * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
1273         * </P>
1274         *
1275         * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
1276         * URIs.  The path part of these URIs must exist in the contacts database, in the
1277         * appropriate column, or the reference will be discarded as invalid. Telephone schema
1278         * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
1279         * </P>
1280         *
1281         * @param uri A URI for the person.
1282         * @see Notification#EXTRA_PEOPLE
1283         */
1284        public Builder addPerson(String uri) {
1285            mPeople.add(uri);
1286            return this;
1287        }
1288
1289        /**
1290         * Set this notification to be part of a group of notifications sharing the same key.
1291         * Grouped notifications may display in a cluster or stack on devices which
1292         * support such rendering.
1293         *
1294         * <p>To make this notification the summary for its group, also call
1295         * {@link #setGroupSummary}. A sort order can be specified for group members by using
1296         * {@link #setSortKey}.
1297         * @param groupKey The group key of the group.
1298         * @return this object for method chaining
1299         */
1300        public Builder setGroup(String groupKey) {
1301            mGroupKey = groupKey;
1302            return this;
1303        }
1304
1305        /**
1306         * Set this notification to be the group summary for a group of notifications.
1307         * Grouped notifications may display in a cluster or stack on devices which
1308         * support such rendering. Requires a group key also be set using {@link #setGroup}.
1309         * @param isGroupSummary Whether this notification should be a group summary.
1310         * @return this object for method chaining
1311         */
1312        public Builder setGroupSummary(boolean isGroupSummary) {
1313            mGroupSummary = isGroupSummary;
1314            return this;
1315        }
1316
1317        /**
1318         * Set a sort key that orders this notification among other notifications from the
1319         * same package. This can be useful if an external sort was already applied and an app
1320         * would like to preserve this. Notifications will be sorted lexicographically using this
1321         * value, although providing different priorities in addition to providing sort key may
1322         * cause this value to be ignored.
1323         *
1324         * <p>This sort key can also be used to order members of a notification group. See
1325         * {@link Builder#setGroup}.
1326         *
1327         * @see String#compareTo(String)
1328         */
1329        public Builder setSortKey(String sortKey) {
1330            mSortKey = sortKey;
1331            return this;
1332        }
1333
1334        /**
1335         * Merge additional metadata into this notification.
1336         *
1337         * <p>Values within the Bundle will replace existing extras values in this Builder.
1338         *
1339         * @see Notification#extras
1340         */
1341        public Builder addExtras(Bundle extras) {
1342            if (extras != null) {
1343                if (mExtras == null) {
1344                    mExtras = new Bundle(extras);
1345                } else {
1346                    mExtras.putAll(extras);
1347                }
1348            }
1349            return this;
1350        }
1351
1352        /**
1353         * Set metadata for this notification.
1354         *
1355         * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
1356         * current contents are copied into the Notification each time {@link #build()} is
1357         * called.
1358         *
1359         * <p>Replaces any existing extras values with those from the provided Bundle.
1360         * Use {@link #addExtras} to merge in metadata instead.
1361         *
1362         * @see Notification#extras
1363         */
1364        public Builder setExtras(Bundle extras) {
1365            mExtras = extras;
1366            return this;
1367        }
1368
1369        /**
1370         * Get the current metadata Bundle used by this notification Builder.
1371         *
1372         * <p>The returned Bundle is shared with this Builder.
1373         *
1374         * <p>The current contents of this Bundle are copied into the Notification each time
1375         * {@link #build()} is called.
1376         *
1377         * @see Notification#extras
1378         */
1379        public Bundle getExtras() {
1380            if (mExtras == null) {
1381                mExtras = new Bundle();
1382            }
1383            return mExtras;
1384        }
1385
1386        /**
1387         * Add an action to this notification. Actions are typically displayed by
1388         * the system as a button adjacent to the notification content.
1389         * <br>
1390         * Action buttons won't appear on platforms prior to Android 4.1. Action
1391         * buttons depend on expanded notifications, which are only available in Android 4.1
1392         * and later. To ensure that an action button's functionality is always available, first
1393         * implement the functionality in the {@link android.app.Activity} that starts when a user
1394         * clicks the  notification (see {@link #setContentIntent setContentIntent()}), and then
1395         * enhance the notification by implementing the same functionality with
1396         * {@link #addAction addAction()}.
1397         *
1398         * @param icon Resource ID of a drawable that represents the action.
1399         * @param title Text describing the action.
1400         * @param intent {@link android.app.PendingIntent} to be fired when the action is invoked.
1401         */
1402        public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
1403            mActions.add(new Action(icon, title, intent));
1404            return this;
1405        }
1406
1407        /**
1408         * Add an action to this notification. Actions are typically displayed by
1409         * the system as a button adjacent to the notification content.
1410         * <br>
1411         * Action buttons won't appear on platforms prior to Android 4.1. Action
1412         * buttons depend on expanded notifications, which are only available in Android 4.1
1413         * and later. To ensure that an action button's functionality is always available, first
1414         * implement the functionality in the {@link android.app.Activity} that starts when a user
1415         * clicks the  notification (see {@link #setContentIntent setContentIntent()}), and then
1416         * enhance the notification by implementing the same functionality with
1417         * {@link #addAction addAction()}.
1418         *
1419         * @param action The action to add.
1420         */
1421        public Builder addAction(Action action) {
1422            mActions.add(action);
1423            return this;
1424        }
1425
1426        /**
1427         * Add a rich notification style to be applied at build time.
1428         * <br>
1429         * If the platform does not provide rich notification styles, this method has no effect. The
1430         * user will always see the normal notification style.
1431         *
1432         * @param style Object responsible for modifying the notification style.
1433         */
1434        public Builder setStyle(Style style) {
1435            if (mStyle != style) {
1436                mStyle = style;
1437                if (mStyle != null) {
1438                    mStyle.setBuilder(this);
1439                }
1440            }
1441            return this;
1442        }
1443
1444        /**
1445         * Sets {@link Notification#color}.
1446         *
1447         * @param argb The accent color to use
1448         *
1449         * @return The same Builder.
1450         */
1451        public Builder setColor(int argb) {
1452            mColor = argb;
1453            return this;
1454        }
1455
1456        /**
1457         * Sets {@link Notification#visibility}.
1458         *
1459         * @param visibility One of {@link Notification#VISIBILITY_PRIVATE} (the default),
1460         *                   {@link Notification#VISIBILITY_PUBLIC}, or
1461         *                   {@link Notification#VISIBILITY_SECRET}.
1462         */
1463        public Builder setVisibility(int visibility) {
1464            mVisibility = visibility;
1465            return this;
1466        }
1467
1468        /**
1469         * Supply a replacement Notification whose contents should be shown in insecure contexts
1470         * (i.e. atop the secure lockscreen). See {@link Notification#visibility} and
1471         * {@link #VISIBILITY_PUBLIC}.
1472         *
1473         * @param n A replacement notification, presumably with some or all info redacted.
1474         * @return The same Builder.
1475         */
1476        public Builder setPublicVersion(Notification n) {
1477            mPublicVersion = n;
1478            return this;
1479        }
1480
1481        /**
1482         * Apply an extender to this notification builder. Extenders may be used to add
1483         * metadata or change options on this builder.
1484         */
1485        public Builder extend(Extender extender) {
1486            extender.extend(this);
1487            return this;
1488        }
1489
1490        /**
1491         * @deprecated Use {@link #build()} instead.
1492         */
1493        @Deprecated
1494        public Notification getNotification() {
1495            return IMPL.build(this);
1496        }
1497
1498        /**
1499         * Combine all of the options that have been set and return a new {@link Notification}
1500         * object.
1501         */
1502        public Notification build() {
1503            return IMPL.build(this);
1504        }
1505
1506        protected static CharSequence limitCharSequenceLength(CharSequence cs) {
1507            if (cs == null) return cs;
1508            if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1509                cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1510            }
1511            return cs;
1512        }
1513    }
1514
1515    /**
1516     * An object that can apply a rich notification style to a {@link Notification.Builder}
1517     * object.
1518     * <br>
1519     * If the platform does not provide rich notification styles, methods in this class have no
1520     * effect.
1521     */
1522    public static abstract class Style {
1523        Builder mBuilder;
1524        CharSequence mBigContentTitle;
1525        CharSequence mSummaryText;
1526        boolean mSummaryTextSet = false;
1527
1528        public void setBuilder(Builder builder) {
1529            if (mBuilder != builder) {
1530                mBuilder = builder;
1531                if (mBuilder != null) {
1532                    mBuilder.setStyle(this);
1533                }
1534            }
1535        }
1536
1537        public Notification build() {
1538            Notification notification = null;
1539            if (mBuilder != null) {
1540                notification = mBuilder.build();
1541            }
1542            return notification;
1543        }
1544    }
1545
1546    /**
1547     * Helper class for generating large-format notifications that include a large image attachment.
1548     * <br>
1549     * If the platform does not provide large-format notifications, this method has no effect. The
1550     * user will always see the normal notification view.
1551     * <br>
1552     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1553     * <pre class="prettyprint">
1554     * Notification notif = new Notification.Builder(mContext)
1555     *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
1556     *     .setContentText(subject)
1557     *     .setSmallIcon(R.drawable.new_post)
1558     *     .setLargeIcon(aBitmap)
1559     *     .setStyle(new Notification.BigPictureStyle()
1560     *         .bigPicture(aBigBitmap))
1561     *     .build();
1562     * </pre>
1563     *
1564     * @see Notification#bigContentView
1565     */
1566    public static class BigPictureStyle extends Style {
1567        Bitmap mPicture;
1568        Bitmap mBigLargeIcon;
1569        boolean mBigLargeIconSet;
1570
1571        public BigPictureStyle() {
1572        }
1573
1574        public BigPictureStyle(Builder builder) {
1575            setBuilder(builder);
1576        }
1577
1578        /**
1579         * Overrides ContentTitle in the big form of the template.
1580         * This defaults to the value passed to setContentTitle().
1581         */
1582        public BigPictureStyle setBigContentTitle(CharSequence title) {
1583            mBigContentTitle = Builder.limitCharSequenceLength(title);
1584            return this;
1585        }
1586
1587        /**
1588         * Set the first line of text after the detail section in the big form of the template.
1589         */
1590        public BigPictureStyle setSummaryText(CharSequence cs) {
1591            mSummaryText = Builder.limitCharSequenceLength(cs);
1592            mSummaryTextSet = true;
1593            return this;
1594        }
1595
1596        /**
1597         * Provide the bitmap to be used as the payload for the BigPicture notification.
1598         */
1599        public BigPictureStyle bigPicture(Bitmap b) {
1600            mPicture = b;
1601            return this;
1602        }
1603
1604        /**
1605         * Override the large icon when the big notification is shown.
1606         */
1607        public BigPictureStyle bigLargeIcon(Bitmap b) {
1608            mBigLargeIcon = b;
1609            mBigLargeIconSet = true;
1610            return this;
1611        }
1612    }
1613
1614    /**
1615     * Helper class for generating large-format notifications that include a lot of text.
1616     *
1617     * <br>
1618     * If the platform does not provide large-format notifications, this method has no effect. The
1619     * user will always see the normal notification view.
1620     * <br>
1621     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1622     * <pre class="prettyprint">
1623     * Notification notif = new Notification.Builder(mContext)
1624     *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
1625     *     .setContentText(subject)
1626     *     .setSmallIcon(R.drawable.new_mail)
1627     *     .setLargeIcon(aBitmap)
1628     *     .setStyle(new Notification.BigTextStyle()
1629     *         .bigText(aVeryLongString))
1630     *     .build();
1631     * </pre>
1632     *
1633     * @see Notification#bigContentView
1634     */
1635    public static class BigTextStyle extends Style {
1636        CharSequence mBigText;
1637
1638        public BigTextStyle() {
1639        }
1640
1641        public BigTextStyle(Builder builder) {
1642            setBuilder(builder);
1643        }
1644
1645        /**
1646         * Overrides ContentTitle in the big form of the template.
1647         * This defaults to the value passed to setContentTitle().
1648         */
1649        public BigTextStyle setBigContentTitle(CharSequence title) {
1650            mBigContentTitle = Builder.limitCharSequenceLength(title);
1651            return this;
1652        }
1653
1654        /**
1655         * Set the first line of text after the detail section in the big form of the template.
1656         */
1657        public BigTextStyle setSummaryText(CharSequence cs) {
1658            mSummaryText = Builder.limitCharSequenceLength(cs);
1659            mSummaryTextSet = true;
1660            return this;
1661        }
1662
1663        /**
1664         * Provide the longer text to be displayed in the big form of the
1665         * template in place of the content text.
1666         */
1667        public BigTextStyle bigText(CharSequence cs) {
1668            mBigText = Builder.limitCharSequenceLength(cs);
1669            return this;
1670        }
1671    }
1672
1673    /**
1674     * Helper class for generating large-format notifications that include a list of (up to 5) strings.
1675     *
1676     * <br>
1677     * If the platform does not provide large-format notifications, this method has no effect. The
1678     * user will always see the normal notification view.
1679     * <br>
1680     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
1681     * <pre class="prettyprint">
1682     * Notification noti = new Notification.Builder()
1683     *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
1684     *     .setContentText(subject)
1685     *     .setSmallIcon(R.drawable.new_mail)
1686     *     .setLargeIcon(aBitmap)
1687     *     .setStyle(new Notification.InboxStyle()
1688     *         .addLine(str1)
1689     *         .addLine(str2)
1690     *         .setContentTitle(&quot;&quot;)
1691     *         .setSummaryText(&quot;+3 more&quot;))
1692     *     .build();
1693     * </pre>
1694     *
1695     * @see Notification#bigContentView
1696     */
1697    public static class InboxStyle extends Style {
1698        ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
1699
1700        public InboxStyle() {
1701        }
1702
1703        public InboxStyle(Builder builder) {
1704            setBuilder(builder);
1705        }
1706
1707        /**
1708         * Overrides ContentTitle in the big form of the template.
1709         * This defaults to the value passed to setContentTitle().
1710         */
1711        public InboxStyle setBigContentTitle(CharSequence title) {
1712            mBigContentTitle = Builder.limitCharSequenceLength(title);
1713            return this;
1714        }
1715
1716        /**
1717         * Set the first line of text after the detail section in the big form of the template.
1718         */
1719        public InboxStyle setSummaryText(CharSequence cs) {
1720            mSummaryText = Builder.limitCharSequenceLength(cs);
1721            mSummaryTextSet = true;
1722            return this;
1723        }
1724
1725        /**
1726         * Append a line to the digest section of the Inbox notification.
1727         */
1728        public InboxStyle addLine(CharSequence cs) {
1729            mTexts.add(Builder.limitCharSequenceLength(cs));
1730            return this;
1731        }
1732    }
1733
1734    /**
1735     * Structure to encapsulate a named action that can be shown as part of this notification.
1736     * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1737     * selected by the user. Action buttons won't appear on platforms prior to Android 4.1.
1738     * <p>
1739     * Apps should use {@link NotificationCompat.Builder#addAction(int, CharSequence, PendingIntent)}
1740     * or {@link NotificationCompat.Builder#addAction(NotificationCompat.Action)}
1741     * to attach actions.
1742     */
1743    public static class Action extends NotificationCompatBase.Action {
1744        private final Bundle mExtras;
1745        private final RemoteInput[] mRemoteInputs;
1746
1747        /**
1748         * Small icon representing the action.
1749         */
1750        public int icon;
1751        /**
1752         * Title of the action.
1753         */
1754        public CharSequence title;
1755        /**
1756         * Intent to send when the user invokes this action. May be null, in which case the action
1757         * may be rendered in a disabled presentation.
1758         */
1759        public PendingIntent actionIntent;
1760
1761        public Action(int icon, CharSequence title, PendingIntent intent) {
1762            this(icon, title, intent, new Bundle(), null);
1763        }
1764
1765        private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
1766                RemoteInput[] remoteInputs) {
1767            this.icon = icon;
1768            this.title = NotificationCompat.Builder.limitCharSequenceLength(title);
1769            this.actionIntent = intent;
1770            this.mExtras = extras != null ? extras : new Bundle();
1771            this.mRemoteInputs = remoteInputs;
1772        }
1773
1774        @Override
1775        protected int getIcon() {
1776            return icon;
1777        }
1778
1779        @Override
1780        protected CharSequence getTitle() {
1781            return title;
1782        }
1783
1784        @Override
1785        protected PendingIntent getActionIntent() {
1786            return actionIntent;
1787        }
1788
1789        /**
1790         * Get additional metadata carried around with this Action.
1791         */
1792        public Bundle getExtras() {
1793            return mExtras;
1794        }
1795
1796        /**
1797         * Get the list of inputs to be collected from the user when this action is sent.
1798         * May return null if no remote inputs were added.
1799         */
1800        public RemoteInput[] getRemoteInputs() {
1801            return mRemoteInputs;
1802        }
1803
1804        /**
1805         * Builder class for {@link Action} objects.
1806         */
1807        public static final class Builder {
1808            private final int mIcon;
1809            private final CharSequence mTitle;
1810            private final PendingIntent mIntent;
1811            private final Bundle mExtras;
1812            private ArrayList<RemoteInput> mRemoteInputs;
1813
1814            /**
1815             * Construct a new builder for {@link Action} object.
1816             * @param icon icon to show for this action
1817             * @param title the title of the action
1818             * @param intent the {@link PendingIntent} to fire when users trigger this action
1819             */
1820            public Builder(int icon, CharSequence title, PendingIntent intent) {
1821                this(icon, title, intent, new Bundle());
1822            }
1823
1824            /**
1825             * Construct a new builder for {@link Action} object using the fields from an
1826             * {@link Action}.
1827             * @param action the action to read fields from.
1828             */
1829            public Builder(Action action) {
1830                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras));
1831            }
1832
1833            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
1834                mIcon = icon;
1835                mTitle = NotificationCompat.Builder.limitCharSequenceLength(title);
1836                mIntent = intent;
1837                mExtras = extras;
1838            }
1839
1840            /**
1841             * Merge additional metadata into this builder.
1842             *
1843             * <p>Values within the Bundle will replace existing extras values in this Builder.
1844             *
1845             * @see NotificationCompat.Action#getExtras
1846             */
1847            public Builder addExtras(Bundle extras) {
1848                if (extras != null) {
1849                    mExtras.putAll(extras);
1850                }
1851                return this;
1852            }
1853
1854            /**
1855             * Get the metadata Bundle used by this Builder.
1856             *
1857             * <p>The returned Bundle is shared with this Builder.
1858             */
1859            public Bundle getExtras() {
1860                return mExtras;
1861            }
1862
1863            /**
1864             * Add an input to be collected from the user when this action is sent.
1865             * Response values can be retrieved from the fired intent by using the
1866             * {@link RemoteInput#getResultsFromIntent} function.
1867             * @param remoteInput a {@link RemoteInput} to add to the action
1868             * @return this object for method chaining
1869             */
1870            public Builder addRemoteInput(RemoteInput remoteInput) {
1871                if (mRemoteInputs == null) {
1872                    mRemoteInputs = new ArrayList<RemoteInput>();
1873                }
1874                mRemoteInputs.add(remoteInput);
1875                return this;
1876            }
1877
1878            /**
1879             * Apply an extender to this action builder. Extenders may be used to add
1880             * metadata or change options on this builder.
1881             */
1882            public Builder extend(Extender extender) {
1883                extender.extend(this);
1884                return this;
1885            }
1886
1887            /**
1888             * Combine all of the options that have been set and return a new {@link Action}
1889             * object.
1890             * @return the built action
1891             */
1892            public Action build() {
1893                RemoteInput[] remoteInputs = mRemoteInputs != null
1894                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1895                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
1896            }
1897        }
1898
1899
1900        /**
1901         * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1902         * metadata or change options on an action builder.
1903         */
1904        public interface Extender {
1905            /**
1906             * Apply this extender to a notification action builder.
1907             * @param builder the builder to be modified.
1908             * @return the build object for chaining.
1909             */
1910            public Builder extend(Builder builder);
1911        }
1912
1913        /**
1914         * Wearable extender for notification actions. To add extensions to an action,
1915         * create a new {@link NotificationCompat.Action.WearableExtender} object using
1916         * the {@code WearableExtender()} constructor and apply it to a
1917         * {@link NotificationCompat.Action.Builder} using
1918         * {@link NotificationCompat.Action.Builder#extend}.
1919         *
1920         * <pre class="prettyprint">
1921         * NotificationCompat.Action action = new NotificationCompat.Action.Builder(
1922         *         R.drawable.archive_all, "Archive all", actionIntent)
1923         *         .extend(new NotificationCompat.Action.WearableExtender()
1924         *                 .setAvailableOffline(false))
1925         *         .build();</pre>
1926         */
1927        public static final class WearableExtender implements Extender {
1928            /** Notification action extra which contains wearable extensions */
1929            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1930
1931            // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
1932            private static final String KEY_FLAGS = "flags";
1933            private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1934            private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1935            private static final String KEY_CANCEL_LABEL = "cancelLabel";
1936
1937            // Flags bitwise-ored to mFlags
1938            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1939
1940            // Default value for flags integer
1941            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1942
1943            private int mFlags = DEFAULT_FLAGS;
1944
1945            private CharSequence mInProgressLabel;
1946            private CharSequence mConfirmLabel;
1947            private CharSequence mCancelLabel;
1948
1949            /**
1950             * Create a {@link NotificationCompat.Action.WearableExtender} with default
1951             * options.
1952             */
1953            public WearableExtender() {
1954            }
1955
1956            /**
1957             * Create a {@link NotificationCompat.Action.WearableExtender} by reading
1958             * wearable options present in an existing notification action.
1959             * @param action the notification action to inspect.
1960             */
1961            public WearableExtender(Action action) {
1962                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1963                if (wearableBundle != null) {
1964                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1965                    mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1966                    mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1967                    mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
1968                }
1969            }
1970
1971            /**
1972             * Apply wearable extensions to a notification action that is being built. This is
1973             * typically called by the {@link NotificationCompat.Action.Builder#extend}
1974             * method of {@link NotificationCompat.Action.Builder}.
1975             */
1976            @Override
1977            public Action.Builder extend(Action.Builder builder) {
1978                Bundle wearableBundle = new Bundle();
1979
1980                if (mFlags != DEFAULT_FLAGS) {
1981                    wearableBundle.putInt(KEY_FLAGS, mFlags);
1982                }
1983                if (mInProgressLabel != null) {
1984                    wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1985                }
1986                if (mConfirmLabel != null) {
1987                    wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1988                }
1989                if (mCancelLabel != null) {
1990                    wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1991                }
1992
1993                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1994                return builder;
1995            }
1996
1997            @Override
1998            public WearableExtender clone() {
1999                WearableExtender that = new WearableExtender();
2000                that.mFlags = this.mFlags;
2001                that.mInProgressLabel = this.mInProgressLabel;
2002                that.mConfirmLabel = this.mConfirmLabel;
2003                that.mCancelLabel = this.mCancelLabel;
2004                return that;
2005            }
2006
2007            /**
2008             * Set whether this action is available when the wearable device is not connected to
2009             * a companion device. The user can still trigger this action when the wearable device
2010             * is offline, but a visual hint will indicate that the action may not be available.
2011             * Defaults to true.
2012             */
2013            public WearableExtender setAvailableOffline(boolean availableOffline) {
2014                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
2015                return this;
2016            }
2017
2018            /**
2019             * Get whether this action is available when the wearable device is not connected to
2020             * a companion device. The user can still trigger this action when the wearable device
2021             * is offline, but a visual hint will indicate that the action may not be available.
2022             * Defaults to true.
2023             */
2024            public boolean isAvailableOffline() {
2025                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
2026            }
2027
2028            private void setFlag(int mask, boolean value) {
2029                if (value) {
2030                    mFlags |= mask;
2031                } else {
2032                    mFlags &= ~mask;
2033                }
2034            }
2035
2036            /**
2037             * Set a label to display while the wearable is preparing to automatically execute the
2038             * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
2039             *
2040             * @param label the label to display while the action is being prepared to execute
2041             * @return this object for method chaining
2042             */
2043            public WearableExtender setInProgressLabel(CharSequence label) {
2044                mInProgressLabel = label;
2045                return this;
2046            }
2047
2048            /**
2049             * Get the label to display while the wearable is preparing to automatically execute
2050             * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
2051             *
2052             * @return the label to display while the action is being prepared to execute
2053             */
2054            public CharSequence getInProgressLabel() {
2055                return mInProgressLabel;
2056            }
2057
2058            /**
2059             * Set a label to display to confirm that the action should be executed.
2060             * This is usually an imperative verb like "Send".
2061             *
2062             * @param label the label to confirm the action should be executed
2063             * @return this object for method chaining
2064             */
2065            public WearableExtender setConfirmLabel(CharSequence label) {
2066                mConfirmLabel = label;
2067                return this;
2068            }
2069
2070            /**
2071             * Get the label to display to confirm that the action should be executed.
2072             * This is usually an imperative verb like "Send".
2073             *
2074             * @return the label to confirm the action should be executed
2075             */
2076            public CharSequence getConfirmLabel() {
2077                return mConfirmLabel;
2078            }
2079
2080            /**
2081             * Set a label to display to cancel the action.
2082             * This is usually an imperative verb, like "Cancel".
2083             *
2084             * @param label the label to display to cancel the action
2085             * @return this object for method chaining
2086             */
2087            public WearableExtender setCancelLabel(CharSequence label) {
2088                mCancelLabel = label;
2089                return this;
2090            }
2091
2092            /**
2093             * Get the label to display to cancel the action.
2094             * This is usually an imperative verb like "Cancel".
2095             *
2096             * @return the label to display to cancel the action
2097             */
2098            public CharSequence getCancelLabel() {
2099                return mCancelLabel;
2100            }
2101        }
2102
2103        /** @hide */
2104        public static final Factory FACTORY = new Factory() {
2105            @Override
2106            public Action build(int icon, CharSequence title,
2107                    PendingIntent actionIntent, Bundle extras,
2108                    RemoteInputCompatBase.RemoteInput[] remoteInputs) {
2109                return new Action(icon, title, actionIntent, extras,
2110                        (RemoteInput[]) remoteInputs);
2111            }
2112
2113            @Override
2114            public Action[] newArray(int length) {
2115                return new Action[length];
2116            }
2117        };
2118    }
2119
2120
2121    /**
2122     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
2123     * metadata or change options on a notification builder.
2124     */
2125    public interface Extender {
2126        /**
2127         * Apply this extender to a notification builder.
2128         * @param builder the builder to be modified.
2129         * @return the build object for chaining.
2130         */
2131        public Builder extend(Builder builder);
2132    }
2133
2134    /**
2135     * Helper class to add wearable extensions to notifications.
2136     * <p class="note"> See
2137     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
2138     * for Android Wear</a> for more information on how to use this class.
2139     * <p>
2140     * To create a notification with wearable extensions:
2141     * <ol>
2142     *   <li>Create a {@link NotificationCompat.Builder}, setting any desired
2143     *   properties.
2144     *   <li>Create a {@link NotificationCompat.WearableExtender}.
2145     *   <li>Set wearable-specific properties using the
2146     *   {@code add} and {@code set} methods of {@link NotificationCompat.WearableExtender}.
2147     *   <li>Call {@link NotificationCompat.Builder#extend} to apply the extensions to a
2148     *   notification.
2149     *   <li>Post the notification to the notification
2150     *   system with the {@code NotificationManagerCompat.notify(...)} methods
2151     *   and not the {@code NotificationManager.notify(...)} methods.
2152     * </ol>
2153     *
2154     * <pre class="prettyprint">
2155     * Notification notif = new NotificationCompat.Builder(mContext)
2156     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
2157     *         .setContentText(subject)
2158     *         .setSmallIcon(R.drawable.new_mail)
2159     *         .extend(new NotificationCompat.WearableExtender()
2160     *                 .setContentIcon(R.drawable.new_mail))
2161     *         .build();
2162     * NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
2163     *
2164     * <p>Wearable extensions can be accessed on an existing notification by using the
2165     * {@code WearableExtender(Notification)} constructor,
2166     * and then using the {@code get} methods to access values.
2167     *
2168     * <pre class="prettyprint">
2169     * NotificationCompat.WearableExtender wearableExtender =
2170     *         new NotificationCompat.WearableExtender(notification);
2171     * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
2172     */
2173    public static final class WearableExtender implements Extender {
2174        /**
2175         * Sentinel value for an action index that is unset.
2176         */
2177        public static final int UNSET_ACTION_INDEX = -1;
2178
2179        /**
2180         * Size value for use with {@link #setCustomSizePreset} to show this notification with
2181         * default sizing.
2182         * <p>For custom display notifications created using {@link #setDisplayIntent},
2183         * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
2184         * on their content.
2185         */
2186        public static final int SIZE_DEFAULT = 0;
2187
2188        /**
2189         * Size value for use with {@link #setCustomSizePreset} to show this notification
2190         * with an extra small size.
2191         * <p>This value is only applicable for custom display notifications created using
2192         * {@link #setDisplayIntent}.
2193         */
2194        public static final int SIZE_XSMALL = 1;
2195
2196        /**
2197         * Size value for use with {@link #setCustomSizePreset} to show this notification
2198         * with a small size.
2199         * <p>This value is only applicable for custom display notifications created using
2200         * {@link #setDisplayIntent}.
2201         */
2202        public static final int SIZE_SMALL = 2;
2203
2204        /**
2205         * Size value for use with {@link #setCustomSizePreset} to show this notification
2206         * with a medium size.
2207         * <p>This value is only applicable for custom display notifications created using
2208         * {@link #setDisplayIntent}.
2209         */
2210        public static final int SIZE_MEDIUM = 3;
2211
2212        /**
2213         * Size value for use with {@link #setCustomSizePreset} to show this notification
2214         * with a large size.
2215         * <p>This value is only applicable for custom display notifications created using
2216         * {@link #setDisplayIntent}.
2217         */
2218        public static final int SIZE_LARGE = 4;
2219
2220        /**
2221         * Size value for use with {@link #setCustomSizePreset} to show this notification
2222         * full screen.
2223         * <p>This value is only applicable for custom display notifications created using
2224         * {@link #setDisplayIntent}.
2225         */
2226        public static final int SIZE_FULL_SCREEN = 5;
2227
2228        /**
2229         * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
2230         * short amount of time when this notification is displayed on the screen. This
2231         * is the default value.
2232         */
2233        public static final int SCREEN_TIMEOUT_SHORT = 0;
2234
2235        /**
2236         * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
2237         * for a longer amount of time when this notification is displayed on the screen.
2238         */
2239        public static final int SCREEN_TIMEOUT_LONG = -1;
2240
2241        /** Notification extra which contains wearable extensions */
2242        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
2243
2244        // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
2245        private static final String KEY_ACTIONS = "actions";
2246        private static final String KEY_FLAGS = "flags";
2247        private static final String KEY_DISPLAY_INTENT = "displayIntent";
2248        private static final String KEY_PAGES = "pages";
2249        private static final String KEY_BACKGROUND = "background";
2250        private static final String KEY_CONTENT_ICON = "contentIcon";
2251        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
2252        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
2253        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
2254        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
2255        private static final String KEY_GRAVITY = "gravity";
2256        private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
2257
2258        // Flags bitwise-ored to mFlags
2259        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
2260        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
2261        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
2262        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
2263        private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
2264
2265        // Default value for flags integer
2266        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
2267
2268        private static final int DEFAULT_CONTENT_ICON_GRAVITY = GravityCompat.END;
2269        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
2270
2271        private ArrayList<Action> mActions = new ArrayList<Action>();
2272        private int mFlags = DEFAULT_FLAGS;
2273        private PendingIntent mDisplayIntent;
2274        private ArrayList<Notification> mPages = new ArrayList<Notification>();
2275        private Bitmap mBackground;
2276        private int mContentIcon;
2277        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
2278        private int mContentActionIndex = UNSET_ACTION_INDEX;
2279        private int mCustomSizePreset = SIZE_DEFAULT;
2280        private int mCustomContentHeight;
2281        private int mGravity = DEFAULT_GRAVITY;
2282        private int mHintScreenTimeout;
2283
2284        /**
2285         * Create a {@link NotificationCompat.WearableExtender} with default
2286         * options.
2287         */
2288        public WearableExtender() {
2289        }
2290
2291        public WearableExtender(Notification notif) {
2292            Bundle extras = getExtras(notif);
2293            Bundle wearableBundle = extras != null ? extras.getBundle(EXTRA_WEARABLE_EXTENSIONS)
2294                    : null;
2295            if (wearableBundle != null) {
2296                Action[] actions = IMPL.getActionsFromParcelableArrayList(
2297                        wearableBundle.getParcelableArrayList(KEY_ACTIONS));
2298                if (actions != null) {
2299                    Collections.addAll(mActions, actions);
2300                }
2301
2302                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
2303                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
2304
2305                Notification[] pages = getNotificationArrayFromBundle(
2306                        wearableBundle, KEY_PAGES);
2307                if (pages != null) {
2308                    Collections.addAll(mPages, pages);
2309                }
2310
2311                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
2312                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
2313                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
2314                        DEFAULT_CONTENT_ICON_GRAVITY);
2315                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
2316                        UNSET_ACTION_INDEX);
2317                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
2318                        SIZE_DEFAULT);
2319                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
2320                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
2321                mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
2322            }
2323        }
2324
2325        /**
2326         * Apply wearable extensions to a notification that is being built. This is typically
2327         * called by the {@link NotificationCompat.Builder#extend} method of
2328         * {@link NotificationCompat.Builder}.
2329         */
2330        @Override
2331        public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
2332            Bundle wearableBundle = new Bundle();
2333
2334            if (!mActions.isEmpty()) {
2335                wearableBundle.putParcelableArrayList(KEY_ACTIONS,
2336                        IMPL.getParcelableArrayListForActions(mActions.toArray(
2337                                new Action[mActions.size()])));
2338            }
2339            if (mFlags != DEFAULT_FLAGS) {
2340                wearableBundle.putInt(KEY_FLAGS, mFlags);
2341            }
2342            if (mDisplayIntent != null) {
2343                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
2344            }
2345            if (!mPages.isEmpty()) {
2346                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
2347                        new Notification[mPages.size()]));
2348            }
2349            if (mBackground != null) {
2350                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
2351            }
2352            if (mContentIcon != 0) {
2353                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
2354            }
2355            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
2356                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
2357            }
2358            if (mContentActionIndex != UNSET_ACTION_INDEX) {
2359                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
2360                        mContentActionIndex);
2361            }
2362            if (mCustomSizePreset != SIZE_DEFAULT) {
2363                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
2364            }
2365            if (mCustomContentHeight != 0) {
2366                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
2367            }
2368            if (mGravity != DEFAULT_GRAVITY) {
2369                wearableBundle.putInt(KEY_GRAVITY, mGravity);
2370            }
2371            if (mHintScreenTimeout != 0) {
2372                wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
2373            }
2374
2375            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
2376            return builder;
2377        }
2378
2379        @Override
2380        public WearableExtender clone() {
2381            WearableExtender that = new WearableExtender();
2382            that.mActions = new ArrayList<Action>(this.mActions);
2383            that.mFlags = this.mFlags;
2384            that.mDisplayIntent = this.mDisplayIntent;
2385            that.mPages = new ArrayList<Notification>(this.mPages);
2386            that.mBackground = this.mBackground;
2387            that.mContentIcon = this.mContentIcon;
2388            that.mContentIconGravity = this.mContentIconGravity;
2389            that.mContentActionIndex = this.mContentActionIndex;
2390            that.mCustomSizePreset = this.mCustomSizePreset;
2391            that.mCustomContentHeight = this.mCustomContentHeight;
2392            that.mGravity = this.mGravity;
2393            that.mHintScreenTimeout = this.mHintScreenTimeout;
2394            return that;
2395        }
2396
2397        /**
2398         * Add a wearable action to this notification.
2399         *
2400         * <p>When wearable actions are added using this method, the set of actions that
2401         * show on a wearable device splits from devices that only show actions added
2402         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
2403         * of which actions display on different devices.
2404         *
2405         * @param action the action to add to this notification
2406         * @return this object for method chaining
2407         * @see NotificationCompat.Action
2408         */
2409        public WearableExtender addAction(Action action) {
2410            mActions.add(action);
2411            return this;
2412        }
2413
2414        /**
2415         * Adds wearable actions to this notification.
2416         *
2417         * <p>When wearable actions are added using this method, the set of actions that
2418         * show on a wearable device splits from devices that only show actions added
2419         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
2420         * of which actions display on different devices.
2421         *
2422         * @param actions the actions to add to this notification
2423         * @return this object for method chaining
2424         * @see NotificationCompat.Action
2425         */
2426        public WearableExtender addActions(List<Action> actions) {
2427            mActions.addAll(actions);
2428            return this;
2429        }
2430
2431        /**
2432         * Clear all wearable actions present on this builder.
2433         * @return this object for method chaining.
2434         * @see #addAction
2435         */
2436        public WearableExtender clearActions() {
2437            mActions.clear();
2438            return this;
2439        }
2440
2441        /**
2442         * Get the wearable actions present on this notification.
2443         */
2444        public List<Action> getActions() {
2445            return mActions;
2446        }
2447
2448        /**
2449         * Set an intent to launch inside of an activity view when displaying
2450         * this notification. The {@link PendingIntent} provided should be for an activity.
2451         *
2452         * <pre class="prettyprint">
2453         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
2454         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
2455         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
2456         * Notification notif = new NotificationCompat.Builder(context)
2457         *         .extend(new NotificationCompat.WearableExtender()
2458         *                 .setDisplayIntent(displayPendingIntent)
2459         *                 .setCustomSizePreset(NotificationCompat.WearableExtender.SIZE_MEDIUM))
2460         *         .build();</pre>
2461         *
2462         * <p>The activity to launch needs to allow embedding, must be exported, and
2463         * should have an empty task affinity. It is also recommended to use the device
2464         * default light theme.
2465         *
2466         * <p>Example AndroidManifest.xml entry:
2467         * <pre class="prettyprint">
2468         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
2469         *     android:exported=&quot;true&quot;
2470         *     android:allowEmbedded=&quot;true&quot;
2471         *     android:taskAffinity=&quot;&quot;
2472         *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
2473         *
2474         * @param intent the {@link PendingIntent} for an activity
2475         * @return this object for method chaining
2476         * @see NotificationCompat.WearableExtender#getDisplayIntent
2477         */
2478        public WearableExtender setDisplayIntent(PendingIntent intent) {
2479            mDisplayIntent = intent;
2480            return this;
2481        }
2482
2483        /**
2484         * Get the intent to launch inside of an activity view when displaying this
2485         * notification. This {@code PendingIntent} should be for an activity.
2486         */
2487        public PendingIntent getDisplayIntent() {
2488            return mDisplayIntent;
2489        }
2490
2491        /**
2492         * Add an additional page of content to display with this notification. The current
2493         * notification forms the first page, and pages added using this function form
2494         * subsequent pages. This field can be used to separate a notification into multiple
2495         * sections.
2496         *
2497         * @param page the notification to add as another page
2498         * @return this object for method chaining
2499         * @see NotificationCompat.WearableExtender#getPages
2500         */
2501        public WearableExtender addPage(Notification page) {
2502            mPages.add(page);
2503            return this;
2504        }
2505
2506        /**
2507         * Add additional pages of content to display with this notification. The current
2508         * notification forms the first page, and pages added using this function form
2509         * subsequent pages. This field can be used to separate a notification into multiple
2510         * sections.
2511         *
2512         * @param pages a list of notifications
2513         * @return this object for method chaining
2514         * @see NotificationCompat.WearableExtender#getPages
2515         */
2516        public WearableExtender addPages(List<Notification> pages) {
2517            mPages.addAll(pages);
2518            return this;
2519        }
2520
2521        /**
2522         * Clear all additional pages present on this builder.
2523         * @return this object for method chaining.
2524         * @see #addPage
2525         */
2526        public WearableExtender clearPages() {
2527            mPages.clear();
2528            return this;
2529        }
2530
2531        /**
2532         * Get the array of additional pages of content for displaying this notification. The
2533         * current notification forms the first page, and elements within this array form
2534         * subsequent pages. This field can be used to separate a notification into multiple
2535         * sections.
2536         * @return the pages for this notification
2537         */
2538        public List<Notification> getPages() {
2539            return mPages;
2540        }
2541
2542        /**
2543         * Set a background image to be displayed behind the notification content.
2544         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
2545         * will work with any notification style.
2546         *
2547         * @param background the background bitmap
2548         * @return this object for method chaining
2549         * @see NotificationCompat.WearableExtender#getBackground
2550         */
2551        public WearableExtender setBackground(Bitmap background) {
2552            mBackground = background;
2553            return this;
2554        }
2555
2556        /**
2557         * Get a background image to be displayed behind the notification content.
2558         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
2559         * will work with any notification style.
2560         *
2561         * @return the background image
2562         * @see NotificationCompat.WearableExtender#setBackground
2563         */
2564        public Bitmap getBackground() {
2565            return mBackground;
2566        }
2567
2568        /**
2569         * Set an icon that goes with the content of this notification.
2570         */
2571        public WearableExtender setContentIcon(int icon) {
2572            mContentIcon = icon;
2573            return this;
2574        }
2575
2576        /**
2577         * Get an icon that goes with the content of this notification.
2578         */
2579        public int getContentIcon() {
2580            return mContentIcon;
2581        }
2582
2583        /**
2584         * Set the gravity that the content icon should have within the notification display.
2585         * Supported values include {@link android.view.Gravity#START} and
2586         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
2587         * @see #setContentIcon
2588         */
2589        public WearableExtender setContentIconGravity(int contentIconGravity) {
2590            mContentIconGravity = contentIconGravity;
2591            return this;
2592        }
2593
2594        /**
2595         * Get the gravity that the content icon should have within the notification display.
2596         * Supported values include {@link android.view.Gravity#START} and
2597         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
2598         * @see #getContentIcon
2599         */
2600        public int getContentIconGravity() {
2601            return mContentIconGravity;
2602        }
2603
2604        /**
2605         * Set an action from this notification's actions to be clickable with the content of
2606         * this notification. This action will no longer display separately from the
2607         * notification's content.
2608         *
2609         * <p>For notifications with multiple pages, child pages can also have content actions
2610         * set, although the list of available actions comes from the main notification and not
2611         * from the child page's notification.
2612         *
2613         * @param actionIndex The index of the action to hoist onto the current notification page.
2614         *                    If wearable actions were added to the main notification, this index
2615         *                    will apply to that list, otherwise it will apply to the regular
2616         *                    actions list.
2617         */
2618        public WearableExtender setContentAction(int actionIndex) {
2619            mContentActionIndex = actionIndex;
2620            return this;
2621        }
2622
2623        /**
2624         * Get the index of the notification action, if any, that was specified as being clickable
2625         * with the content of this notification. This action will no longer display separately
2626         * from the notification's content.
2627         *
2628         * <p>For notifications with multiple pages, child pages can also have content actions
2629         * set, although the list of available actions comes from the main notification and not
2630         * from the child page's notification.
2631         *
2632         * <p>If wearable specific actions were added to the main notification, this index will
2633         * apply to that list, otherwise it will apply to the regular actions list.
2634         *
2635         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
2636         */
2637        public int getContentAction() {
2638            return mContentActionIndex;
2639        }
2640
2641        /**
2642         * Set the gravity that this notification should have within the available viewport space.
2643         * Supported values include {@link android.view.Gravity#TOP},
2644         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
2645         * The default value is {@link android.view.Gravity#BOTTOM}.
2646         */
2647        public WearableExtender setGravity(int gravity) {
2648            mGravity = gravity;
2649            return this;
2650        }
2651
2652        /**
2653         * Get the gravity that this notification should have within the available viewport space.
2654         * Supported values include {@link android.view.Gravity#TOP},
2655         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
2656         * The default value is {@link android.view.Gravity#BOTTOM}.
2657         */
2658        public int getGravity() {
2659            return mGravity;
2660        }
2661
2662        /**
2663         * Set the custom size preset for the display of this notification out of the available
2664         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
2665         * {@link #SIZE_LARGE}.
2666         * <p>Some custom size presets are only applicable for custom display notifications created
2667         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. Check the
2668         * documentation for the preset in question. See also
2669         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
2670         */
2671        public WearableExtender setCustomSizePreset(int sizePreset) {
2672            mCustomSizePreset = sizePreset;
2673            return this;
2674        }
2675
2676        /**
2677         * Get the custom size preset for the display of this notification out of the available
2678         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
2679         * {@link #SIZE_LARGE}.
2680         * <p>Some custom size presets are only applicable for custom display notifications created
2681         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
2682         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
2683         */
2684        public int getCustomSizePreset() {
2685            return mCustomSizePreset;
2686        }
2687
2688        /**
2689         * Set the custom height in pixels for the display of this notification's content.
2690         * <p>This option is only available for custom display notifications created
2691         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. See also
2692         * {@link NotificationCompat.WearableExtender#setCustomSizePreset} and
2693         * {@link #getCustomContentHeight}.
2694         */
2695        public WearableExtender setCustomContentHeight(int height) {
2696            mCustomContentHeight = height;
2697            return this;
2698        }
2699
2700        /**
2701         * Get the custom height in pixels for the display of this notification's content.
2702         * <p>This option is only available for custom display notifications created
2703         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
2704         * {@link #setCustomContentHeight}.
2705         */
2706        public int getCustomContentHeight() {
2707            return mCustomContentHeight;
2708        }
2709
2710        /**
2711         * Set whether the scrolling position for the contents of this notification should start
2712         * at the bottom of the contents instead of the top when the contents are too long to
2713         * display within the screen.  Default is false (start scroll at the top).
2714         */
2715        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
2716            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
2717            return this;
2718        }
2719
2720        /**
2721         * Get whether the scrolling position for the contents of this notification should start
2722         * at the bottom of the contents instead of the top when the contents are too long to
2723         * display within the screen. Default is false (start scroll at the top).
2724         */
2725        public boolean getStartScrollBottom() {
2726            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
2727        }
2728
2729        /**
2730         * Set whether the content intent is available when the wearable device is not connected
2731         * to a companion device.  The user can still trigger this intent when the wearable device
2732         * is offline, but a visual hint will indicate that the content intent may not be available.
2733         * Defaults to true.
2734         */
2735        public WearableExtender setContentIntentAvailableOffline(
2736                boolean contentIntentAvailableOffline) {
2737            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
2738            return this;
2739        }
2740
2741        /**
2742         * Get whether the content intent is available when the wearable device is not connected
2743         * to a companion device.  The user can still trigger this intent when the wearable device
2744         * is offline, but a visual hint will indicate that the content intent may not be available.
2745         * Defaults to true.
2746         */
2747        public boolean getContentIntentAvailableOffline() {
2748            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
2749        }
2750
2751        /**
2752         * Set a hint that this notification's icon should not be displayed.
2753         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
2754         * @return this object for method chaining
2755         */
2756        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
2757            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
2758            return this;
2759        }
2760
2761        /**
2762         * Get a hint that this notification's icon should not be displayed.
2763         * @return {@code true} if this icon should not be displayed, false otherwise.
2764         * The default value is {@code false} if this was never set.
2765         */
2766        public boolean getHintHideIcon() {
2767            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
2768        }
2769
2770        /**
2771         * Set a visual hint that only the background image of this notification should be
2772         * displayed, and other semantic content should be hidden. This hint is only applicable
2773         * to sub-pages added using {@link #addPage}.
2774         */
2775        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
2776            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
2777            return this;
2778        }
2779
2780        /**
2781         * Get a visual hint that only the background image of this notification should be
2782         * displayed, and other semantic content should be hidden. This hint is only applicable
2783         * to sub-pages added using {@link NotificationCompat.WearableExtender#addPage}.
2784         */
2785        public boolean getHintShowBackgroundOnly() {
2786            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
2787        }
2788
2789        /**
2790         * Set a hint that this notification's background should not be clipped if possible.
2791         * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
2792         * @return this object for method chaining
2793         */
2794        public WearableExtender setHintAvoidBackgroundClipping(
2795                boolean hintAvoidBackgroundClipping) {
2796            setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
2797            return this;
2798        }
2799
2800        /**
2801         * Get a hint that this notification's background should not be clipped if possible.
2802         * @return {@code true} if it's ok if the background is clipped on the screen, false
2803         * otherwise. The default value is {@code false} if this was never set.
2804         */
2805        public boolean getHintAvoidBackgroundClipping() {
2806            return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
2807        }
2808
2809        /**
2810         * Set a hint that the screen should remain on for at least this duration when
2811         * this notification is displayed on the screen.
2812         * @param timeout The requested screen timeout in milliseconds. Can also be either
2813         *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
2814         * @return this object for method chaining
2815         */
2816        public WearableExtender setHintScreenTimeout(int timeout) {
2817            mHintScreenTimeout = timeout;
2818            return this;
2819        }
2820
2821        /**
2822         * Get the duration, in milliseconds, that the screen should remain on for
2823         * when this notification is displayed.
2824         * @return the duration in milliseconds if > 0, or either one of the sentinel values
2825         *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
2826         */
2827        public int getHintScreenTimeout() {
2828            return mHintScreenTimeout;
2829        }
2830
2831        private void setFlag(int mask, boolean value) {
2832            if (value) {
2833                mFlags |= mask;
2834            } else {
2835                mFlags &= ~mask;
2836            }
2837        }
2838    }
2839
2840    /**
2841     * Get an array of Notification objects from a parcelable array bundle field.
2842     * Update the bundle to have a typed array so fetches in the future don't need
2843     * to do an array copy.
2844     */
2845    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
2846        Parcelable[] array = bundle.getParcelableArray(key);
2847        if (array instanceof Notification[] || array == null) {
2848            return (Notification[]) array;
2849        }
2850        Notification[] typedArray = new Notification[array.length];
2851        for (int i = 0; i < array.length; i++) {
2852            typedArray[i] = (Notification) array[i];
2853        }
2854        bundle.putParcelableArray(key, typedArray);
2855        return typedArray;
2856    }
2857
2858    /**
2859     * Gets the {@link Notification#extras} field from a notification in a backwards
2860     * compatible manner. Extras field was supported from JellyBean (Api level 16)
2861     * forwards. This function will return null on older api levels.
2862     */
2863    public static Bundle getExtras(Notification notif) {
2864        return IMPL.getExtras(notif);
2865    }
2866
2867    /**
2868     * Get the number of actions in this notification in a backwards compatible
2869     * manner. Actions were supported from JellyBean (Api level 16) forwards.
2870     */
2871    public static int getActionCount(Notification notif) {
2872        return IMPL.getActionCount(notif);
2873    }
2874
2875    /**
2876     * Get an action on this notification in a backwards compatible
2877     * manner. Actions were supported from JellyBean (Api level 16) forwards.
2878     * @param notif The notification to inspect.
2879     * @param actionIndex The index of the action to retrieve.
2880     */
2881    public static Action getAction(Notification notif, int actionIndex) {
2882        return IMPL.getAction(notif, actionIndex);
2883    }
2884
2885    /**
2886    * Get the category of this notification in a backwards compatible
2887    * manner.
2888    * @param notif The notification to inspect.
2889    */
2890    public static String getCategory(Notification notif) {
2891        return IMPL.getCategory(notif);
2892    }
2893
2894    /**
2895     * Get whether or not this notification is only relevant to the current device.
2896     *
2897     * <p>Some notifications can be bridged to other devices for remote display.
2898     * If this hint is set, it is recommend that this notification not be bridged.
2899     */
2900    public static boolean getLocalOnly(Notification notif) {
2901        return IMPL.getLocalOnly(notif);
2902    }
2903
2904    /**
2905     * Get the key used to group this notification into a cluster or stack
2906     * with other notifications on devices which support such rendering.
2907     */
2908    public static String getGroup(Notification notif) {
2909        return IMPL.getGroup(notif);
2910    }
2911
2912    /**
2913     * Get whether this notification to be the group summary for a group of notifications.
2914     * Grouped notifications may display in a cluster or stack on devices which
2915     * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
2916     * @return Whether this notification is a group summary.
2917     */
2918    public static boolean isGroupSummary(Notification notif) {
2919        return IMPL.isGroupSummary(notif);
2920    }
2921
2922    /**
2923     * Get a sort key that orders this notification among other notifications from the
2924     * same package. This can be useful if an external sort was already applied and an app
2925     * would like to preserve this. Notifications will be sorted lexicographically using this
2926     * value, although providing different priorities in addition to providing sort key may
2927     * cause this value to be ignored.
2928     *
2929     * <p>This sort key can also be used to order members of a notification group. See
2930     * {@link Builder#setGroup}.
2931     *
2932     * @see String#compareTo(String)
2933     */
2934    public static String getSortKey(Notification notif) {
2935        return IMPL.getSortKey(notif);
2936    }
2937}
2938