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