Notification.java revision ef72a198afc4ded0b75534a97142fae2e91b46f5
1/*
2 * Copyright (C) 2007 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.app;
18
19import android.annotation.IntDef;
20import android.annotation.SdkConstant;
21import android.annotation.SdkConstant.SdkConstantType;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.ApplicationInfo;
25import android.content.pm.PackageManager.NameNotFoundException;
26import android.content.res.ColorStateList;
27import android.graphics.Bitmap;
28import android.graphics.Canvas;
29import android.graphics.PorterDuff;
30import android.graphics.drawable.Drawable;
31import android.media.AudioAttributes;
32import android.media.AudioManager;
33import android.media.session.MediaSession;
34import android.net.Uri;
35import android.os.BadParcelableException;
36import android.os.Build;
37import android.os.Bundle;
38import android.os.Parcel;
39import android.os.Parcelable;
40import android.os.SystemClock;
41import android.os.UserHandle;
42import android.text.TextUtils;
43import android.util.Log;
44import android.util.MathUtils;
45import android.util.TypedValue;
46import android.view.Gravity;
47import android.view.View;
48import android.widget.ProgressBar;
49import android.widget.RemoteViews;
50
51import com.android.internal.R;
52import com.android.internal.util.NotificationColorUtil;
53
54import java.lang.annotation.Retention;
55import java.lang.annotation.RetentionPolicy;
56import java.lang.reflect.Constructor;
57import java.text.NumberFormat;
58import java.util.ArrayList;
59import java.util.Arrays;
60import java.util.Collections;
61import java.util.List;
62
63/**
64 * A class that represents how a persistent notification is to be presented to
65 * the user using the {@link android.app.NotificationManager}.
66 *
67 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
68 * easier to construct Notifications.</p>
69 *
70 * <div class="special reference">
71 * <h3>Developer Guides</h3>
72 * <p>For a guide to creating notifications, read the
73 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
74 * developer guide.</p>
75 * </div>
76 */
77public class Notification implements Parcelable
78{
79    private static final String TAG = "Notification";
80
81    /**
82     * An activity that provides a user interface for adjusting notification preferences for its
83     * containing application. Optional but recommended for apps that post
84     * {@link android.app.Notification Notifications}.
85     */
86    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
87    public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
88            = "android.intent.category.NOTIFICATION_PREFERENCES";
89
90    /**
91     * Use all default values (where applicable).
92     */
93    public static final int DEFAULT_ALL = ~0;
94
95    /**
96     * Use the default notification sound. This will ignore any given
97     * {@link #sound}.
98     *
99     * <p>
100     * A notification that is noisy is more likely to be presented as a heads-up notification.
101     * </p>
102     *
103     * @see #defaults
104     */
105
106    public static final int DEFAULT_SOUND = 1;
107
108    /**
109     * Use the default notification vibrate. This will ignore any given
110     * {@link #vibrate}. Using phone vibration requires the
111     * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
112     *
113     * <p>
114     * A notification that vibrates is more likely to be presented as a heads-up notification.
115     * </p>
116     *
117     * @see #defaults
118     */
119
120    public static final int DEFAULT_VIBRATE = 2;
121
122    /**
123     * Use the default notification lights. This will ignore the
124     * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
125     * {@link #ledOnMS}.
126     *
127     * @see #defaults
128     */
129
130    public static final int DEFAULT_LIGHTS = 4;
131
132    /**
133     * Maximum length of CharSequences accepted by Builder and friends.
134     *
135     * <p>
136     * Avoids spamming the system with overly large strings such as full e-mails.
137     */
138    private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
139
140    /**
141     * A timestamp related to this notification, in milliseconds since the epoch.
142     *
143     * Default value: {@link System#currentTimeMillis() Now}.
144     *
145     * Choose a timestamp that will be most relevant to the user. For most finite events, this
146     * corresponds to the time the event happened (or will happen, in the case of events that have
147     * yet to occur but about which the user is being informed). Indefinite events should be
148     * timestamped according to when the activity began.
149     *
150     * Some examples:
151     *
152     * <ul>
153     *   <li>Notification of a new chat message should be stamped when the message was received.</li>
154     *   <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
155     *   <li>Notification of a completed file download should be stamped when the download finished.</li>
156     *   <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
157     *   <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
158     *   <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
159     * </ul>
160     *
161     */
162    public long when;
163
164    /**
165     * The resource id of a drawable to use as the icon in the status bar.
166     * This is required; notifications with an invalid icon resource will not be shown.
167     */
168    public int icon;
169
170    /**
171     * If the icon in the status bar is to have more than one level, you can set this.  Otherwise,
172     * leave it at its default value of 0.
173     *
174     * @see android.widget.ImageView#setImageLevel
175     * @see android.graphics.drawable.Drawable#setLevel
176     */
177    public int iconLevel;
178
179    /**
180     * The number of events that this notification represents. For example, in a new mail
181     * notification, this could be the number of unread messages.
182     *
183     * The system may or may not use this field to modify the appearance of the notification. For
184     * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
185     * superimposed over the icon in the status bar. Starting with
186     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
187     * {@link Notification.Builder} has displayed the number in the expanded notification view.
188     *
189     * If the number is 0 or negative, it is never shown.
190     */
191    public int number;
192
193    /**
194     * The intent to execute when the expanded status entry is clicked.  If
195     * this is an activity, it must include the
196     * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
197     * that you take care of task management as described in the
198     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
199     * Stack</a> document.  In particular, make sure to read the notification section
200     * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
201     * Notifications</a> for the correct ways to launch an application from a
202     * notification.
203     */
204    public PendingIntent contentIntent;
205
206    /**
207     * The intent to execute when the notification is explicitly dismissed by the user, either with
208     * the "Clear All" button or by swiping it away individually.
209     *
210     * This probably shouldn't be launching an activity since several of those will be sent
211     * at the same time.
212     */
213    public PendingIntent deleteIntent;
214
215    /**
216     * An intent to launch instead of posting the notification to the status bar.
217     *
218     * <p>
219     * The system UI may choose to display a heads-up notification, instead of
220     * launching this intent, while the user is using the device.
221     * </p>
222     *
223     * @see Notification.Builder#setFullScreenIntent
224     */
225    public PendingIntent fullScreenIntent;
226
227    /**
228     * Text that summarizes this notification for accessibility services.
229     *
230     * As of the L release, this text is no longer shown on screen, but it is still useful to
231     * accessibility services (where it serves as an audible announcement of the notification's
232     * appearance).
233     *
234     * @see #tickerView
235     */
236    public CharSequence tickerText;
237
238    /**
239     * Formerly, a view showing the {@link #tickerText}.
240     *
241     * No longer displayed in the status bar as of API 21.
242     */
243    @Deprecated
244    public RemoteViews tickerView;
245
246    /**
247     * The view that will represent this notification in the expanded status bar.
248     */
249    public RemoteViews contentView;
250
251    /**
252     * A large-format version of {@link #contentView}, giving the Notification an
253     * opportunity to show more detail. The system UI may choose to show this
254     * instead of the normal content view at its discretion.
255     */
256    public RemoteViews bigContentView;
257
258
259    /**
260     * A medium-format version of {@link #contentView}, providing the Notification an
261     * opportunity to add action buttons to contentView. At its discretion, the system UI may
262     * choose to show this as a heads-up notification, which will pop up so the user can see
263     * it without leaving their current activity.
264     */
265    public RemoteViews headsUpContentView;
266
267    /**
268     * The bitmap that may escape the bounds of the panel and bar.
269     */
270    public Bitmap largeIcon;
271
272    /**
273     * The sound to play.
274     *
275     * <p>
276     * A notification that is noisy is more likely to be presented as a heads-up notification.
277     * </p>
278     *
279     * <p>
280     * To play the default notification sound, see {@link #defaults}.
281     * </p>
282     */
283    public Uri sound;
284
285    /**
286     * Use this constant as the value for audioStreamType to request that
287     * the default stream type for notifications be used.  Currently the
288     * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
289     *
290     * @deprecated Use {@link #audioAttributes} instead.
291     */
292    @Deprecated
293    public static final int STREAM_DEFAULT = -1;
294
295    /**
296     * The audio stream type to use when playing the sound.
297     * Should be one of the STREAM_ constants from
298     * {@link android.media.AudioManager}.
299     *
300     * @deprecated Use {@link #audioAttributes} instead.
301     */
302    @Deprecated
303    public int audioStreamType = STREAM_DEFAULT;
304
305    /**
306     * The default value of {@link #audioAttributes}.
307     */
308    public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
309            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
310            .setUsage(AudioAttributes.USAGE_NOTIFICATION)
311            .build();
312
313    /**
314     * The {@link AudioAttributes audio attributes} to use when playing the sound.
315     */
316    public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
317
318    /**
319     * The pattern with which to vibrate.
320     *
321     * <p>
322     * To vibrate the default pattern, see {@link #defaults}.
323     * </p>
324     *
325     * <p>
326     * A notification that vibrates is more likely to be presented as a heads-up notification.
327     * </p>
328     *
329     * @see android.os.Vibrator#vibrate(long[],int)
330     */
331    public long[] vibrate;
332
333    /**
334     * The color of the led.  The hardware will do its best approximation.
335     *
336     * @see #FLAG_SHOW_LIGHTS
337     * @see #flags
338     */
339    public int ledARGB;
340
341    /**
342     * The number of milliseconds for the LED to be on while it's flashing.
343     * The hardware will do its best approximation.
344     *
345     * @see #FLAG_SHOW_LIGHTS
346     * @see #flags
347     */
348    public int ledOnMS;
349
350    /**
351     * The number of milliseconds for the LED to be off while it's flashing.
352     * The hardware will do its best approximation.
353     *
354     * @see #FLAG_SHOW_LIGHTS
355     * @see #flags
356     */
357    public int ledOffMS;
358
359    /**
360     * Specifies which values should be taken from the defaults.
361     * <p>
362     * To set, OR the desired from {@link #DEFAULT_SOUND},
363     * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
364     * values, use {@link #DEFAULT_ALL}.
365     * </p>
366     */
367    public int defaults;
368
369    /**
370     * Bit to be bitwise-ored into the {@link #flags} field that should be
371     * set if you want the LED on for this notification.
372     * <ul>
373     * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
374     *      or 0 for both ledOnMS and ledOffMS.</li>
375     * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
376     * <li>To flash the LED, pass the number of milliseconds that it should
377     *      be on and off to ledOnMS and ledOffMS.</li>
378     * </ul>
379     * <p>
380     * Since hardware varies, you are not guaranteed that any of the values
381     * you pass are honored exactly.  Use the system defaults (TODO) if possible
382     * because they will be set to values that work on any given hardware.
383     * <p>
384     * The alpha channel must be set for forward compatibility.
385     *
386     */
387    public static final int FLAG_SHOW_LIGHTS        = 0x00000001;
388
389    /**
390     * Bit to be bitwise-ored into the {@link #flags} field that should be
391     * set if this notification is in reference to something that is ongoing,
392     * like a phone call.  It should not be set if this notification is in
393     * reference to something that happened at a particular point in time,
394     * like a missed phone call.
395     */
396    public static final int FLAG_ONGOING_EVENT      = 0x00000002;
397
398    /**
399     * Bit to be bitwise-ored into the {@link #flags} field that if set,
400     * the audio will be repeated until the notification is
401     * cancelled or the notification window is opened.
402     */
403    public static final int FLAG_INSISTENT          = 0x00000004;
404
405    /**
406     * Bit to be bitwise-ored into the {@link #flags} field that should be
407     * set if you would only like the sound, vibrate and ticker to be played
408     * if the notification was not already showing.
409     */
410    public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;
411
412    /**
413     * Bit to be bitwise-ored into the {@link #flags} field that should be
414     * set if the notification should be canceled when it is clicked by the
415     * user.
416
417     */
418    public static final int FLAG_AUTO_CANCEL        = 0x00000010;
419
420    /**
421     * Bit to be bitwise-ored into the {@link #flags} field that should be
422     * set if the notification should not be canceled when the user clicks
423     * the Clear all button.
424     */
425    public static final int FLAG_NO_CLEAR           = 0x00000020;
426
427    /**
428     * Bit to be bitwise-ored into the {@link #flags} field that should be
429     * set if this notification represents a currently running service.  This
430     * will normally be set for you by {@link Service#startForeground}.
431     */
432    public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
433
434    /**
435     * Obsolete flag indicating high-priority notifications; use the priority field instead.
436     *
437     * @deprecated Use {@link #priority} with a positive value.
438     */
439    public static final int FLAG_HIGH_PRIORITY      = 0x00000080;
440
441    /**
442     * Bit to be bitswise-ored into the {@link #flags} field that should be
443     * set if this notification is relevant to the current device only
444     * and it is not recommended that it bridge to other devices.
445     */
446    public static final int FLAG_LOCAL_ONLY         = 0x00000100;
447
448    /**
449     * Bit to be bitswise-ored into the {@link #flags} field that should be
450     * set if this notification is the group summary for a group of notifications.
451     * Grouped notifications may display in a cluster or stack on devices which
452     * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
453     */
454    public static final int FLAG_GROUP_SUMMARY      = 0x00000200;
455
456    public int flags;
457
458    /** @hide */
459    @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
460    @Retention(RetentionPolicy.SOURCE)
461    public @interface Priority {}
462
463    /**
464     * Default notification {@link #priority}. If your application does not prioritize its own
465     * notifications, use this value for all notifications.
466     */
467    public static final int PRIORITY_DEFAULT = 0;
468
469    /**
470     * Lower {@link #priority}, for items that are less important. The UI may choose to show these
471     * items smaller, or at a different position in the list, compared with your app's
472     * {@link #PRIORITY_DEFAULT} items.
473     */
474    public static final int PRIORITY_LOW = -1;
475
476    /**
477     * Lowest {@link #priority}; these items might not be shown to the user except under special
478     * circumstances, such as detailed notification logs.
479     */
480    public static final int PRIORITY_MIN = -2;
481
482    /**
483     * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
484     * show these items larger, or at a different position in notification lists, compared with
485     * your app's {@link #PRIORITY_DEFAULT} items.
486     */
487    public static final int PRIORITY_HIGH = 1;
488
489    /**
490     * Highest {@link #priority}, for your application's most important items that require the
491     * user's prompt attention or input.
492     */
493    public static final int PRIORITY_MAX = 2;
494
495    /**
496     * Relative priority for this notification.
497     *
498     * Priority is an indication of how much of the user's valuable attention should be consumed by
499     * this notification. Low-priority notifications may be hidden from the user in certain
500     * situations, while the user might be interrupted for a higher-priority notification. The
501     * system will make a determination about how to interpret this priority when presenting
502     * the notification.
503     *
504     * <p>
505     * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
506     * as a heads-up notification.
507     * </p>
508     *
509     */
510    @Priority
511    public int priority;
512
513    /**
514     * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
515     * to be applied by the standard Style templates when presenting this notification.
516     *
517     * The current template design constructs a colorful header image by overlaying the
518     * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
519     * ignored.
520     */
521    public int color = COLOR_DEFAULT;
522
523    /**
524     * Special value of {@link #color} telling the system not to decorate this notification with
525     * any special color but instead use default colors when presenting this notification.
526     */
527    public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
528
529    /**
530     * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
531     * the notification's presence and contents in untrusted situations (namely, on the secure
532     * lockscreen).
533     *
534     * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
535     * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
536     * shown in all situations, but the contents are only available if the device is unlocked for
537     * the appropriate user.
538     *
539     * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
540     * can be read even in an "insecure" context (that is, above a secure lockscreen).
541     * To modify the public version of this notification—for example, to redact some portions—see
542     * {@link Builder#setPublicVersion(Notification)}.
543     *
544     * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
545     * and ticker until the user has bypassed the lockscreen.
546     */
547    public int visibility;
548
549    /**
550     * Notification visibility: Show this notification in its entirety on all lockscreens.
551     *
552     * {@see #visibility}
553     */
554    public static final int VISIBILITY_PUBLIC = 1;
555
556    /**
557     * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
558     * private information on secure lockscreens.
559     *
560     * {@see #visibility}
561     */
562    public static final int VISIBILITY_PRIVATE = 0;
563
564    /**
565     * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
566     *
567     * {@see #visibility}
568     */
569    public static final int VISIBILITY_SECRET = -1;
570
571    /**
572     * Notification category: incoming call (voice or video) or similar synchronous communication request.
573     */
574    public static final String CATEGORY_CALL = "call";
575
576    /**
577     * Notification category: incoming direct message (SMS, instant message, etc.).
578     */
579    public static final String CATEGORY_MESSAGE = "msg";
580
581    /**
582     * Notification category: asynchronous bulk message (email).
583     */
584    public static final String CATEGORY_EMAIL = "email";
585
586    /**
587     * Notification category: calendar event.
588     */
589    public static final String CATEGORY_EVENT = "event";
590
591    /**
592     * Notification category: promotion or advertisement.
593     */
594    public static final String CATEGORY_PROMO = "promo";
595
596    /**
597     * Notification category: alarm or timer.
598     */
599    public static final String CATEGORY_ALARM = "alarm";
600
601    /**
602     * Notification category: progress of a long-running background operation.
603     */
604    public static final String CATEGORY_PROGRESS = "progress";
605
606    /**
607     * Notification category: social network or sharing update.
608     */
609    public static final String CATEGORY_SOCIAL = "social";
610
611    /**
612     * Notification category: error in background operation or authentication status.
613     */
614    public static final String CATEGORY_ERROR = "err";
615
616    /**
617     * Notification category: media transport control for playback.
618     */
619    public static final String CATEGORY_TRANSPORT = "transport";
620
621    /**
622     * Notification category: system or device status update.  Reserved for system use.
623     */
624    public static final String CATEGORY_SYSTEM = "sys";
625
626    /**
627     * Notification category: indication of running background service.
628     */
629    public static final String CATEGORY_SERVICE = "service";
630
631    /**
632     * Notification category: a specific, timely recommendation for a single thing.
633     * For example, a news app might want to recommend a news story it believes the user will
634     * want to read next.
635     */
636    public static final String CATEGORY_RECOMMENDATION = "recommendation";
637
638    /**
639     * Notification category: ongoing information about device or contextual status.
640     */
641    public static final String CATEGORY_STATUS = "status";
642
643    /**
644     * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
645     * that best describes this Notification.  May be used by the system for ranking and filtering.
646     */
647    public String category;
648
649    private String mGroupKey;
650
651    /**
652     * Get the key used to group this notification into a cluster or stack
653     * with other notifications on devices which support such rendering.
654     */
655    public String getGroup() {
656        return mGroupKey;
657    }
658
659    private String mSortKey;
660
661    /**
662     * Get a sort key that orders this notification among other notifications from the
663     * same package. This can be useful if an external sort was already applied and an app
664     * would like to preserve this. Notifications will be sorted lexicographically using this
665     * value, although providing different priorities in addition to providing sort key may
666     * cause this value to be ignored.
667     *
668     * <p>This sort key can also be used to order members of a notification group. See
669     * {@link Builder#setGroup}.
670     *
671     * @see String#compareTo(String)
672     */
673    public String getSortKey() {
674        return mSortKey;
675    }
676
677    /**
678     * Additional semantic data to be carried around with this Notification.
679     * <p>
680     * The extras keys defined here are intended to capture the original inputs to {@link Builder}
681     * APIs, and are intended to be used by
682     * {@link android.service.notification.NotificationListenerService} implementations to extract
683     * detailed information from notification objects.
684     */
685    public Bundle extras = new Bundle();
686
687    /**
688     * {@link #extras} key: this is the title of the notification,
689     * as supplied to {@link Builder#setContentTitle(CharSequence)}.
690     */
691    public static final String EXTRA_TITLE = "android.title";
692
693    /**
694     * {@link #extras} key: this is the title of the notification when shown in expanded form,
695     * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
696     */
697    public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
698
699    /**
700     * {@link #extras} key: this is the main text payload, as supplied to
701     * {@link Builder#setContentText(CharSequence)}.
702     */
703    public static final String EXTRA_TEXT = "android.text";
704
705    /**
706     * {@link #extras} key: this is a third line of text, as supplied to
707     * {@link Builder#setSubText(CharSequence)}.
708     */
709    public static final String EXTRA_SUB_TEXT = "android.subText";
710
711    /**
712     * {@link #extras} key: this is a small piece of additional text as supplied to
713     * {@link Builder#setContentInfo(CharSequence)}.
714     */
715    public static final String EXTRA_INFO_TEXT = "android.infoText";
716
717    /**
718     * {@link #extras} key: this is a line of summary information intended to be shown
719     * alongside expanded notifications, as supplied to (e.g.)
720     * {@link BigTextStyle#setSummaryText(CharSequence)}.
721     */
722    public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
723
724    /**
725     * {@link #extras} key: this is the longer text shown in the big form of a
726     * {@link BigTextStyle} notification, as supplied to
727     * {@link BigTextStyle#bigText(CharSequence)}.
728     */
729    public static final String EXTRA_BIG_TEXT = "android.bigText";
730
731    /**
732     * {@link #extras} key: this is the resource ID of the notification's main small icon, as
733     * supplied to {@link Builder#setSmallIcon(int)}.
734     */
735    public static final String EXTRA_SMALL_ICON = "android.icon";
736
737    /**
738     * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
739     * notification payload, as
740     * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
741     */
742    public static final String EXTRA_LARGE_ICON = "android.largeIcon";
743
744    /**
745     * {@link #extras} key: this is a bitmap to be used instead of the one from
746     * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
747     * shown in its expanded form, as supplied to
748     * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
749     */
750    public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
751
752    /**
753     * {@link #extras} key: this is the progress value supplied to
754     * {@link Builder#setProgress(int, int, boolean)}.
755     */
756    public static final String EXTRA_PROGRESS = "android.progress";
757
758    /**
759     * {@link #extras} key: this is the maximum value supplied to
760     * {@link Builder#setProgress(int, int, boolean)}.
761     */
762    public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
763
764    /**
765     * {@link #extras} key: whether the progress bar is indeterminate, supplied to
766     * {@link Builder#setProgress(int, int, boolean)}.
767     */
768    public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
769
770    /**
771     * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
772     * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
773     * {@link Builder#setUsesChronometer(boolean)}.
774     */
775    public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
776
777    /**
778     * {@link #extras} key: whether {@link #when} should be shown,
779     * as supplied to {@link Builder#setShowWhen(boolean)}.
780     */
781    public static final String EXTRA_SHOW_WHEN = "android.showWhen";
782
783    /**
784     * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
785     * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
786     */
787    public static final String EXTRA_PICTURE = "android.picture";
788
789    /**
790     * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
791     * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
792     */
793    public static final String EXTRA_TEXT_LINES = "android.textLines";
794
795    /**
796     * {@link #extras} key: A string representing the name of the specific
797     * {@link android.app.Notification.Style} used to create this notification.
798     */
799    public static final String EXTRA_TEMPLATE = "android.template";
800
801    /**
802     * {@link #extras} key: A String array containing the people that this notification relates to,
803     * each of which was supplied to {@link Builder#addPerson(String)}.
804     */
805    public static final String EXTRA_PEOPLE = "android.people";
806
807    /**
808     * {@link #extras} key: used to provide hints about the appropriateness of
809     * displaying this notification as a heads-up notification.
810     * @hide
811     */
812    public static final String EXTRA_AS_HEADS_UP = "headsup";
813
814    /**
815     * Allow certain system-generated notifications to appear before the device is provisioned.
816     * Only available to notifications coming from the android package.
817     * @hide
818     */
819    public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
820
821    /**
822     * {@link #extras} key: A
823     * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
824     * in the background when the notification is selected. The URI must point to an image stream
825     * suitable for passing into
826     * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
827     * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
828     * URI used for this purpose must require no permissions to read the image data.
829     */
830    public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
831
832    /**
833     * {@link #extras} key: A
834     * {@link android.media.session.MediaSession.Token} associated with a
835     * {@link android.app.Notification.MediaStyle} notification.
836     */
837    public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
838
839    /**
840     * {@link #extras} key: the indices of actions to be shown in the compact view,
841     * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
842     */
843    public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
844
845    /**
846     * {@link #extras} key: the user that built the notification.
847     *
848     * @hide
849     */
850    public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
851
852    /**
853     * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
854     * displayed in the heads up space.
855     *
856     * <p>
857     * If this notification has a {@link #fullScreenIntent}, then it will always launch the
858     * full-screen intent when posted.
859     * </p>
860     * @hide
861     */
862    public static final int HEADS_UP_NEVER = 0;
863
864    /**
865     * Default value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification may be
866     * displayed as a heads up.
867     * @hide
868     */
869    public static final int HEADS_UP_ALLOWED = 1;
870
871    /**
872     * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification is a
873     * good candidate for display as a heads up.
874     * @hide
875     */
876    public static final int HEADS_UP_REQUESTED = 2;
877
878    /**
879     * Structure to encapsulate a named action that can be shown as part of this notification.
880     * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
881     * selected by the user.
882     * <p>
883     * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
884     * or {@link Notification.Builder#addAction(Notification.Action)}
885     * to attach actions.
886     */
887    public static class Action implements Parcelable {
888        private final Bundle mExtras;
889        private final RemoteInput[] mRemoteInputs;
890
891        /**
892         * Small icon representing the action.
893         */
894        public int icon;
895
896        /**
897         * Title of the action.
898         */
899        public CharSequence title;
900
901        /**
902         * Intent to send when the user invokes this action. May be null, in which case the action
903         * may be rendered in a disabled presentation by the system UI.
904         */
905        public PendingIntent actionIntent;
906
907        private Action(Parcel in) {
908            icon = in.readInt();
909            title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
910            if (in.readInt() == 1) {
911                actionIntent = PendingIntent.CREATOR.createFromParcel(in);
912            }
913            mExtras = in.readBundle();
914            mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
915        }
916
917        /**
918         * Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}.
919         */
920        public Action(int icon, CharSequence title, PendingIntent intent) {
921            this(icon, title, intent, new Bundle(), null);
922        }
923
924        private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
925                RemoteInput[] remoteInputs) {
926            this.icon = icon;
927            this.title = title;
928            this.actionIntent = intent;
929            this.mExtras = extras != null ? extras : new Bundle();
930            this.mRemoteInputs = remoteInputs;
931        }
932
933        /**
934         * Get additional metadata carried around with this Action.
935         */
936        public Bundle getExtras() {
937            return mExtras;
938        }
939
940        /**
941         * Get the list of inputs to be collected from the user when this action is sent.
942         * May return null if no remote inputs were added.
943         */
944        public RemoteInput[] getRemoteInputs() {
945            return mRemoteInputs;
946        }
947
948        /**
949         * Builder class for {@link Action} objects.
950         */
951        public static final class Builder {
952            private final int mIcon;
953            private final CharSequence mTitle;
954            private final PendingIntent mIntent;
955            private final Bundle mExtras;
956            private ArrayList<RemoteInput> mRemoteInputs;
957
958            /**
959             * Construct a new builder for {@link Action} object.
960             * @param icon icon to show for this action
961             * @param title the title of the action
962             * @param intent the {@link PendingIntent} to fire when users trigger this action
963             */
964            public Builder(int icon, CharSequence title, PendingIntent intent) {
965                this(icon, title, intent, new Bundle(), null);
966            }
967
968            /**
969             * Construct a new builder for {@link Action} object using the fields from an
970             * {@link Action}.
971             * @param action the action to read fields from.
972             */
973            public Builder(Action action) {
974                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
975                        action.getRemoteInputs());
976            }
977
978            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
979                    RemoteInput[] remoteInputs) {
980                mIcon = icon;
981                mTitle = title;
982                mIntent = intent;
983                mExtras = extras;
984                if (remoteInputs != null) {
985                    mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
986                    Collections.addAll(mRemoteInputs, remoteInputs);
987                }
988            }
989
990            /**
991             * Merge additional metadata into this builder.
992             *
993             * <p>Values within the Bundle will replace existing extras values in this Builder.
994             *
995             * @see Notification.Action#extras
996             */
997            public Builder addExtras(Bundle extras) {
998                if (extras != null) {
999                    mExtras.putAll(extras);
1000                }
1001                return this;
1002            }
1003
1004            /**
1005             * Get the metadata Bundle used by this Builder.
1006             *
1007             * <p>The returned Bundle is shared with this Builder.
1008             */
1009            public Bundle getExtras() {
1010                return mExtras;
1011            }
1012
1013            /**
1014             * Add an input to be collected from the user when this action is sent.
1015             * Response values can be retrieved from the fired intent by using the
1016             * {@link RemoteInput#getResultsFromIntent} function.
1017             * @param remoteInput a {@link RemoteInput} to add to the action
1018             * @return this object for method chaining
1019             */
1020            public Builder addRemoteInput(RemoteInput remoteInput) {
1021                if (mRemoteInputs == null) {
1022                    mRemoteInputs = new ArrayList<RemoteInput>();
1023                }
1024                mRemoteInputs.add(remoteInput);
1025                return this;
1026            }
1027
1028            /**
1029             * Apply an extender to this action builder. Extenders may be used to add
1030             * metadata or change options on this builder.
1031             */
1032            public Builder extend(Extender extender) {
1033                extender.extend(this);
1034                return this;
1035            }
1036
1037            /**
1038             * Combine all of the options that have been set and return a new {@link Action}
1039             * object.
1040             * @return the built action
1041             */
1042            public Action build() {
1043                RemoteInput[] remoteInputs = mRemoteInputs != null
1044                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
1045                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
1046            }
1047        }
1048
1049        @Override
1050        public Action clone() {
1051            return new Action(
1052                    icon,
1053                    title,
1054                    actionIntent, // safe to alias
1055                    new Bundle(mExtras),
1056                    getRemoteInputs());
1057        }
1058        @Override
1059        public int describeContents() {
1060            return 0;
1061        }
1062        @Override
1063        public void writeToParcel(Parcel out, int flags) {
1064            out.writeInt(icon);
1065            TextUtils.writeToParcel(title, out, flags);
1066            if (actionIntent != null) {
1067                out.writeInt(1);
1068                actionIntent.writeToParcel(out, flags);
1069            } else {
1070                out.writeInt(0);
1071            }
1072            out.writeBundle(mExtras);
1073            out.writeTypedArray(mRemoteInputs, flags);
1074        }
1075        public static final Parcelable.Creator<Action> CREATOR =
1076                new Parcelable.Creator<Action>() {
1077            public Action createFromParcel(Parcel in) {
1078                return new Action(in);
1079            }
1080            public Action[] newArray(int size) {
1081                return new Action[size];
1082            }
1083        };
1084
1085        /**
1086         * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1087         * metadata or change options on an action builder.
1088         */
1089        public interface Extender {
1090            /**
1091             * Apply this extender to a notification action builder.
1092             * @param builder the builder to be modified.
1093             * @return the build object for chaining.
1094             */
1095            public Builder extend(Builder builder);
1096        }
1097
1098        /**
1099         * Wearable extender for notification actions. To add extensions to an action,
1100         * create a new {@link android.app.Notification.Action.WearableExtender} object using
1101         * the {@code WearableExtender()} constructor and apply it to a
1102         * {@link android.app.Notification.Action.Builder} using
1103         * {@link android.app.Notification.Action.Builder#extend}.
1104         *
1105         * <pre class="prettyprint">
1106         * Notification.Action action = new Notification.Action.Builder(
1107         *         R.drawable.archive_all, "Archive all", actionIntent)
1108         *         .extend(new Notification.Action.WearableExtender()
1109         *                 .setAvailableOffline(false))
1110         *         .build();</pre>
1111         */
1112        public static final class WearableExtender implements Extender {
1113            /** Notification action extra which contains wearable extensions */
1114            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1115
1116            private static final String KEY_FLAGS = "flags";
1117
1118            // Flags bitwise-ored to mFlags
1119            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1120
1121            // Default value for flags integer
1122            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1123
1124            private int mFlags = DEFAULT_FLAGS;
1125
1126            /**
1127             * Create a {@link android.app.Notification.Action.WearableExtender} with default
1128             * options.
1129             */
1130            public WearableExtender() {
1131            }
1132
1133            /**
1134             * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1135             * wearable options present in an existing notification action.
1136             * @param action the notification action to inspect.
1137             */
1138            public WearableExtender(Action action) {
1139                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1140                if (wearableBundle != null) {
1141                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1142                }
1143            }
1144
1145            /**
1146             * Apply wearable extensions to a notification action that is being built. This is
1147             * typically called by the {@link android.app.Notification.Action.Builder#extend}
1148             * method of {@link android.app.Notification.Action.Builder}.
1149             */
1150            @Override
1151            public Action.Builder extend(Action.Builder builder) {
1152                Bundle wearableBundle = new Bundle();
1153
1154                if (mFlags != DEFAULT_FLAGS) {
1155                    wearableBundle.putInt(KEY_FLAGS, mFlags);
1156                }
1157
1158                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1159                return builder;
1160            }
1161
1162            @Override
1163            public WearableExtender clone() {
1164                WearableExtender that = new WearableExtender();
1165                that.mFlags = this.mFlags;
1166                return that;
1167            }
1168
1169            /**
1170             * Set whether this action is available when the wearable device is not connected to
1171             * a companion device. The user can still trigger this action when the wearable device is
1172             * offline, but a visual hint will indicate that the action may not be available.
1173             * Defaults to true.
1174             */
1175            public WearableExtender setAvailableOffline(boolean availableOffline) {
1176                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1177                return this;
1178            }
1179
1180            /**
1181             * Get whether this action is available when the wearable device is not connected to
1182             * a companion device. The user can still trigger this action when the wearable device is
1183             * offline, but a visual hint will indicate that the action may not be available.
1184             * Defaults to true.
1185             */
1186            public boolean isAvailableOffline() {
1187                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1188            }
1189
1190            private void setFlag(int mask, boolean value) {
1191                if (value) {
1192                    mFlags |= mask;
1193                } else {
1194                    mFlags &= ~mask;
1195                }
1196            }
1197        }
1198    }
1199
1200    /**
1201     * Array of all {@link Action} structures attached to this notification by
1202     * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1203     * {@link android.service.notification.NotificationListenerService} that provide an alternative
1204     * interface for invoking actions.
1205     */
1206    public Action[] actions;
1207
1208    /**
1209     * Replacement version of this notification whose content will be shown
1210     * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1211     * and {@link #VISIBILITY_PUBLIC}.
1212     */
1213    public Notification publicVersion;
1214
1215    /**
1216     * Constructs a Notification object with default values.
1217     * You might want to consider using {@link Builder} instead.
1218     */
1219    public Notification()
1220    {
1221        this.when = System.currentTimeMillis();
1222        this.priority = PRIORITY_DEFAULT;
1223    }
1224
1225    /**
1226     * @hide
1227     */
1228    public Notification(Context context, int icon, CharSequence tickerText, long when,
1229            CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1230    {
1231        this.when = when;
1232        this.icon = icon;
1233        this.tickerText = tickerText;
1234        setLatestEventInfo(context, contentTitle, contentText,
1235                PendingIntent.getActivity(context, 0, contentIntent, 0));
1236    }
1237
1238    /**
1239     * Constructs a Notification object with the information needed to
1240     * have a status bar icon without the standard expanded view.
1241     *
1242     * @param icon          The resource id of the icon to put in the status bar.
1243     * @param tickerText    The text that flows by in the status bar when the notification first
1244     *                      activates.
1245     * @param when          The time to show in the time field.  In the System.currentTimeMillis
1246     *                      timebase.
1247     *
1248     * @deprecated Use {@link Builder} instead.
1249     */
1250    @Deprecated
1251    public Notification(int icon, CharSequence tickerText, long when)
1252    {
1253        this.icon = icon;
1254        this.tickerText = tickerText;
1255        this.when = when;
1256    }
1257
1258    /**
1259     * Unflatten the notification from a parcel.
1260     */
1261    public Notification(Parcel parcel)
1262    {
1263        int version = parcel.readInt();
1264
1265        when = parcel.readLong();
1266        icon = parcel.readInt();
1267        number = parcel.readInt();
1268        if (parcel.readInt() != 0) {
1269            contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1270        }
1271        if (parcel.readInt() != 0) {
1272            deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1273        }
1274        if (parcel.readInt() != 0) {
1275            tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1276        }
1277        if (parcel.readInt() != 0) {
1278            tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
1279        }
1280        if (parcel.readInt() != 0) {
1281            contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1282        }
1283        if (parcel.readInt() != 0) {
1284            largeIcon = Bitmap.CREATOR.createFromParcel(parcel);
1285        }
1286        defaults = parcel.readInt();
1287        flags = parcel.readInt();
1288        if (parcel.readInt() != 0) {
1289            sound = Uri.CREATOR.createFromParcel(parcel);
1290        }
1291
1292        audioStreamType = parcel.readInt();
1293        if (parcel.readInt() != 0) {
1294            audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1295        }
1296        vibrate = parcel.createLongArray();
1297        ledARGB = parcel.readInt();
1298        ledOnMS = parcel.readInt();
1299        ledOffMS = parcel.readInt();
1300        iconLevel = parcel.readInt();
1301
1302        if (parcel.readInt() != 0) {
1303            fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1304        }
1305
1306        priority = parcel.readInt();
1307
1308        category = parcel.readString();
1309
1310        mGroupKey = parcel.readString();
1311
1312        mSortKey = parcel.readString();
1313
1314        extras = parcel.readBundle(); // may be null
1315
1316        actions = parcel.createTypedArray(Action.CREATOR); // may be null
1317
1318        if (parcel.readInt() != 0) {
1319            bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1320        }
1321
1322        if (parcel.readInt() != 0) {
1323            headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1324        }
1325
1326        visibility = parcel.readInt();
1327
1328        if (parcel.readInt() != 0) {
1329            publicVersion = Notification.CREATOR.createFromParcel(parcel);
1330        }
1331
1332        color = parcel.readInt();
1333    }
1334
1335    @Override
1336    public Notification clone() {
1337        Notification that = new Notification();
1338        cloneInto(that, true);
1339        return that;
1340    }
1341
1342    /**
1343     * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1344     * of this into that.
1345     * @hide
1346     */
1347    public void cloneInto(Notification that, boolean heavy) {
1348        that.when = this.when;
1349        that.icon = this.icon;
1350        that.number = this.number;
1351
1352        // PendingIntents are global, so there's no reason (or way) to clone them.
1353        that.contentIntent = this.contentIntent;
1354        that.deleteIntent = this.deleteIntent;
1355        that.fullScreenIntent = this.fullScreenIntent;
1356
1357        if (this.tickerText != null) {
1358            that.tickerText = this.tickerText.toString();
1359        }
1360        if (heavy && this.tickerView != null) {
1361            that.tickerView = this.tickerView.clone();
1362        }
1363        if (heavy && this.contentView != null) {
1364            that.contentView = this.contentView.clone();
1365        }
1366        if (heavy && this.largeIcon != null) {
1367            that.largeIcon = Bitmap.createBitmap(this.largeIcon);
1368        }
1369        that.iconLevel = this.iconLevel;
1370        that.sound = this.sound; // android.net.Uri is immutable
1371        that.audioStreamType = this.audioStreamType;
1372        if (this.audioAttributes != null) {
1373            that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1374        }
1375
1376        final long[] vibrate = this.vibrate;
1377        if (vibrate != null) {
1378            final int N = vibrate.length;
1379            final long[] vib = that.vibrate = new long[N];
1380            System.arraycopy(vibrate, 0, vib, 0, N);
1381        }
1382
1383        that.ledARGB = this.ledARGB;
1384        that.ledOnMS = this.ledOnMS;
1385        that.ledOffMS = this.ledOffMS;
1386        that.defaults = this.defaults;
1387
1388        that.flags = this.flags;
1389
1390        that.priority = this.priority;
1391
1392        that.category = this.category;
1393
1394        that.mGroupKey = this.mGroupKey;
1395
1396        that.mSortKey = this.mSortKey;
1397
1398        if (this.extras != null) {
1399            try {
1400                that.extras = new Bundle(this.extras);
1401                // will unparcel
1402                that.extras.size();
1403            } catch (BadParcelableException e) {
1404                Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1405                that.extras = null;
1406            }
1407        }
1408
1409        if (this.actions != null) {
1410            that.actions = new Action[this.actions.length];
1411            for(int i=0; i<this.actions.length; i++) {
1412                that.actions[i] = this.actions[i].clone();
1413            }
1414        }
1415
1416        if (heavy && this.bigContentView != null) {
1417            that.bigContentView = this.bigContentView.clone();
1418        }
1419
1420        if (heavy && this.headsUpContentView != null) {
1421            that.headsUpContentView = this.headsUpContentView.clone();
1422        }
1423
1424        that.visibility = this.visibility;
1425
1426        if (this.publicVersion != null) {
1427            that.publicVersion = new Notification();
1428            this.publicVersion.cloneInto(that.publicVersion, heavy);
1429        }
1430
1431        that.color = this.color;
1432
1433        if (!heavy) {
1434            that.lightenPayload(); // will clean out extras
1435        }
1436    }
1437
1438    /**
1439     * Removes heavyweight parts of the Notification object for archival or for sending to
1440     * listeners when the full contents are not necessary.
1441     * @hide
1442     */
1443    public final void lightenPayload() {
1444        tickerView = null;
1445        contentView = null;
1446        bigContentView = null;
1447        headsUpContentView = null;
1448        largeIcon = null;
1449        if (extras != null) {
1450            extras.remove(Notification.EXTRA_LARGE_ICON);
1451            extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1452            extras.remove(Notification.EXTRA_PICTURE);
1453            extras.remove(Notification.EXTRA_BIG_TEXT);
1454            // Prevent light notifications from being rebuilt.
1455            extras.remove(Builder.EXTRA_NEEDS_REBUILD);
1456        }
1457    }
1458
1459    /**
1460     * Make sure this CharSequence is safe to put into a bundle, which basically
1461     * means it had better not be some custom Parcelable implementation.
1462     * @hide
1463     */
1464    public static CharSequence safeCharSequence(CharSequence cs) {
1465        if (cs == null) return cs;
1466        if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1467            cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1468        }
1469        if (cs instanceof Parcelable) {
1470            Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1471                    + " instance is a custom Parcelable and not allowed in Notification");
1472            return cs.toString();
1473        }
1474
1475        return cs;
1476    }
1477
1478    public int describeContents() {
1479        return 0;
1480    }
1481
1482    /**
1483     * Flatten this notification from a parcel.
1484     */
1485    public void writeToParcel(Parcel parcel, int flags)
1486    {
1487        parcel.writeInt(1);
1488
1489        parcel.writeLong(when);
1490        parcel.writeInt(icon);
1491        parcel.writeInt(number);
1492        if (contentIntent != null) {
1493            parcel.writeInt(1);
1494            contentIntent.writeToParcel(parcel, 0);
1495        } else {
1496            parcel.writeInt(0);
1497        }
1498        if (deleteIntent != null) {
1499            parcel.writeInt(1);
1500            deleteIntent.writeToParcel(parcel, 0);
1501        } else {
1502            parcel.writeInt(0);
1503        }
1504        if (tickerText != null) {
1505            parcel.writeInt(1);
1506            TextUtils.writeToParcel(tickerText, parcel, flags);
1507        } else {
1508            parcel.writeInt(0);
1509        }
1510        if (tickerView != null) {
1511            parcel.writeInt(1);
1512            tickerView.writeToParcel(parcel, 0);
1513        } else {
1514            parcel.writeInt(0);
1515        }
1516        if (contentView != null) {
1517            parcel.writeInt(1);
1518            contentView.writeToParcel(parcel, 0);
1519        } else {
1520            parcel.writeInt(0);
1521        }
1522        if (largeIcon != null) {
1523            parcel.writeInt(1);
1524            largeIcon.writeToParcel(parcel, 0);
1525        } else {
1526            parcel.writeInt(0);
1527        }
1528
1529        parcel.writeInt(defaults);
1530        parcel.writeInt(this.flags);
1531
1532        if (sound != null) {
1533            parcel.writeInt(1);
1534            sound.writeToParcel(parcel, 0);
1535        } else {
1536            parcel.writeInt(0);
1537        }
1538        parcel.writeInt(audioStreamType);
1539
1540        if (audioAttributes != null) {
1541            parcel.writeInt(1);
1542            audioAttributes.writeToParcel(parcel, 0);
1543        } else {
1544            parcel.writeInt(0);
1545        }
1546
1547        parcel.writeLongArray(vibrate);
1548        parcel.writeInt(ledARGB);
1549        parcel.writeInt(ledOnMS);
1550        parcel.writeInt(ledOffMS);
1551        parcel.writeInt(iconLevel);
1552
1553        if (fullScreenIntent != null) {
1554            parcel.writeInt(1);
1555            fullScreenIntent.writeToParcel(parcel, 0);
1556        } else {
1557            parcel.writeInt(0);
1558        }
1559
1560        parcel.writeInt(priority);
1561
1562        parcel.writeString(category);
1563
1564        parcel.writeString(mGroupKey);
1565
1566        parcel.writeString(mSortKey);
1567
1568        parcel.writeBundle(extras); // null ok
1569
1570        parcel.writeTypedArray(actions, 0); // null ok
1571
1572        if (bigContentView != null) {
1573            parcel.writeInt(1);
1574            bigContentView.writeToParcel(parcel, 0);
1575        } else {
1576            parcel.writeInt(0);
1577        }
1578
1579        if (headsUpContentView != null) {
1580            parcel.writeInt(1);
1581            headsUpContentView.writeToParcel(parcel, 0);
1582        } else {
1583            parcel.writeInt(0);
1584        }
1585
1586        parcel.writeInt(visibility);
1587
1588        if (publicVersion != null) {
1589            parcel.writeInt(1);
1590            publicVersion.writeToParcel(parcel, 0);
1591        } else {
1592            parcel.writeInt(0);
1593        }
1594
1595        parcel.writeInt(color);
1596    }
1597
1598    /**
1599     * Parcelable.Creator that instantiates Notification objects
1600     */
1601    public static final Parcelable.Creator<Notification> CREATOR
1602            = new Parcelable.Creator<Notification>()
1603    {
1604        public Notification createFromParcel(Parcel parcel)
1605        {
1606            return new Notification(parcel);
1607        }
1608
1609        public Notification[] newArray(int size)
1610        {
1611            return new Notification[size];
1612        }
1613    };
1614
1615    /**
1616     * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1617     * layout.
1618     *
1619     * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1620     * in the view.</p>
1621     * @param context       The context for your application / activity.
1622     * @param contentTitle The title that goes in the expanded entry.
1623     * @param contentText  The text that goes in the expanded entry.
1624     * @param contentIntent The intent to launch when the user clicks the expanded notification.
1625     * If this is an activity, it must include the
1626     * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
1627     * that you take care of task management as described in the
1628     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1629     * Stack</a> document.
1630     *
1631     * @deprecated Use {@link Builder} instead.
1632     */
1633    @Deprecated
1634    public void setLatestEventInfo(Context context,
1635            CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
1636        Notification.Builder builder = new Notification.Builder(context);
1637
1638        // First, ensure that key pieces of information that may have been set directly
1639        // are preserved
1640        builder.setWhen(this.when);
1641        builder.setSmallIcon(this.icon);
1642        builder.setPriority(this.priority);
1643        builder.setTicker(this.tickerText);
1644        builder.setNumber(this.number);
1645        builder.setColor(this.color);
1646        builder.mFlags = this.flags;
1647        builder.setSound(this.sound, this.audioStreamType);
1648        builder.setDefaults(this.defaults);
1649        builder.setVibrate(this.vibrate);
1650
1651        // now apply the latestEventInfo fields
1652        if (contentTitle != null) {
1653            builder.setContentTitle(contentTitle);
1654        }
1655        if (contentText != null) {
1656            builder.setContentText(contentText);
1657        }
1658        builder.setContentIntent(contentIntent);
1659        builder.buildInto(this);
1660    }
1661
1662    @Override
1663    public String toString() {
1664        StringBuilder sb = new StringBuilder();
1665        sb.append("Notification(pri=");
1666        sb.append(priority);
1667        sb.append(" contentView=");
1668        if (contentView != null) {
1669            sb.append(contentView.getPackage());
1670            sb.append("/0x");
1671            sb.append(Integer.toHexString(contentView.getLayoutId()));
1672        } else {
1673            sb.append("null");
1674        }
1675        sb.append(" vibrate=");
1676        if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1677            sb.append("default");
1678        } else if (this.vibrate != null) {
1679            int N = this.vibrate.length-1;
1680            sb.append("[");
1681            for (int i=0; i<N; i++) {
1682                sb.append(this.vibrate[i]);
1683                sb.append(',');
1684            }
1685            if (N != -1) {
1686                sb.append(this.vibrate[N]);
1687            }
1688            sb.append("]");
1689        } else {
1690            sb.append("null");
1691        }
1692        sb.append(" sound=");
1693        if ((this.defaults & DEFAULT_SOUND) != 0) {
1694            sb.append("default");
1695        } else if (this.sound != null) {
1696            sb.append(this.sound.toString());
1697        } else {
1698            sb.append("null");
1699        }
1700        sb.append(" defaults=0x");
1701        sb.append(Integer.toHexString(this.defaults));
1702        sb.append(" flags=0x");
1703        sb.append(Integer.toHexString(this.flags));
1704        sb.append(String.format(" color=0x%08x", this.color));
1705        if (this.category != null) {
1706            sb.append(" category=");
1707            sb.append(this.category);
1708        }
1709        if (this.mGroupKey != null) {
1710            sb.append(" groupKey=");
1711            sb.append(this.mGroupKey);
1712        }
1713        if (this.mSortKey != null) {
1714            sb.append(" sortKey=");
1715            sb.append(this.mSortKey);
1716        }
1717        if (actions != null) {
1718            sb.append(" actions=");
1719            sb.append(actions.length);
1720        }
1721        sb.append(" vis=");
1722        sb.append(visibilityToString(this.visibility));
1723        if (this.publicVersion != null) {
1724            sb.append(" publicVersion=");
1725            sb.append(publicVersion.toString());
1726        }
1727        sb.append(")");
1728        return sb.toString();
1729    }
1730
1731    /**
1732     * {@hide}
1733     */
1734    public static String visibilityToString(int vis) {
1735        switch (vis) {
1736            case VISIBILITY_PRIVATE:
1737                return "PRIVATE";
1738            case VISIBILITY_PUBLIC:
1739                return "PUBLIC";
1740            case VISIBILITY_SECRET:
1741                return "SECRET";
1742            default:
1743                return "UNKNOWN(" + String.valueOf(vis) + ")";
1744        }
1745    }
1746
1747    /**
1748     * @hide
1749     */
1750    public boolean isValid() {
1751        // Would like to check for icon!=0 here, too, but NotificationManagerService accepts that
1752        // for legacy reasons.
1753        return contentView != null || extras.getBoolean(Builder.EXTRA_REBUILD_CONTENT_VIEW);
1754    }
1755
1756    /**
1757     * @hide
1758     */
1759    public boolean isGroupSummary() {
1760        return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
1761    }
1762
1763    /**
1764     * @hide
1765     */
1766    public boolean isGroupChild() {
1767        return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
1768    }
1769
1770    /**
1771     * Builder class for {@link Notification} objects.
1772     *
1773     * Provides a convenient way to set the various fields of a {@link Notification} and generate
1774     * content views using the platform's notification layout template. If your app supports
1775     * versions of Android as old as API level 4, you can instead use
1776     * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
1777     * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
1778     * library</a>.
1779     *
1780     * <p>Example:
1781     *
1782     * <pre class="prettyprint">
1783     * Notification noti = new Notification.Builder(mContext)
1784     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
1785     *         .setContentText(subject)
1786     *         .setSmallIcon(R.drawable.new_mail)
1787     *         .setLargeIcon(aBitmap)
1788     *         .build();
1789     * </pre>
1790     */
1791    public static class Builder {
1792        private static final int MAX_ACTION_BUTTONS = 3;
1793        private static final float LARGE_TEXT_SCALE = 1.3f;
1794
1795        /**
1796         * @hide
1797         */
1798        public static final String EXTRA_NEEDS_REBUILD = "android.rebuild";
1799
1800        /**
1801         * @hide
1802         */
1803        public static final String EXTRA_REBUILD_LARGE_ICON = "android.rebuild.largeIcon";
1804        /**
1805         * @hide
1806         */
1807        public static final String EXTRA_REBUILD_CONTENT_VIEW = "android.rebuild.contentView";
1808        /**
1809         * @hide
1810         */
1811        public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
1812                "android.rebuild.contentViewActionCount";
1813        /**
1814         * @hide
1815         */
1816        public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW
1817                = "android.rebuild.bigView";
1818        /**
1819         * @hide
1820         */
1821        public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
1822                = "android.rebuild.bigViewActionCount";
1823        /**
1824         * @hide
1825         */
1826        public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW
1827                = "android.rebuild.hudView";
1828        /**
1829         * @hide
1830         */
1831        public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
1832                = "android.rebuild.hudViewActionCount";
1833
1834        /**
1835         * The ApplicationInfo of the package that created the notification, used to create
1836         * a context to rebuild the notification via a Builder.
1837         * @hide
1838         */
1839        private static final String EXTRA_REBUILD_CONTEXT_APPLICATION_INFO =
1840                "android.rebuild.applicationInfo";
1841
1842        // Whether to enable stripping (at post time) & rebuilding (at listener receive time) of
1843        // memory intensive resources.
1844        private static final boolean STRIP_AND_REBUILD = true;
1845
1846        private Context mContext;
1847
1848        private long mWhen;
1849        private int mSmallIcon;
1850        private int mSmallIconLevel;
1851        private int mNumber;
1852        private CharSequence mContentTitle;
1853        private CharSequence mContentText;
1854        private CharSequence mContentInfo;
1855        private CharSequence mSubText;
1856        private PendingIntent mContentIntent;
1857        private RemoteViews mContentView;
1858        private PendingIntent mDeleteIntent;
1859        private PendingIntent mFullScreenIntent;
1860        private CharSequence mTickerText;
1861        private RemoteViews mTickerView;
1862        private Bitmap mLargeIcon;
1863        private Uri mSound;
1864        private int mAudioStreamType;
1865        private AudioAttributes mAudioAttributes;
1866        private long[] mVibrate;
1867        private int mLedArgb;
1868        private int mLedOnMs;
1869        private int mLedOffMs;
1870        private int mDefaults;
1871        private int mFlags;
1872        private int mProgressMax;
1873        private int mProgress;
1874        private boolean mProgressIndeterminate;
1875        private String mCategory;
1876        private String mGroupKey;
1877        private String mSortKey;
1878        private Bundle mExtras;
1879        private int mPriority;
1880        private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
1881        private boolean mUseChronometer;
1882        private Style mStyle;
1883        private boolean mShowWhen = true;
1884        private int mVisibility = VISIBILITY_PRIVATE;
1885        private Notification mPublicVersion = null;
1886        private final NotificationColorUtil mColorUtil;
1887        private ArrayList<String> mPeople;
1888        private int mColor = COLOR_DEFAULT;
1889
1890        /**
1891         * The user that built the notification originally.
1892         */
1893        private int mOriginatingUserId;
1894
1895        /**
1896         * Contains extras related to rebuilding during the build phase.
1897         */
1898        private Bundle mRebuildBundle = new Bundle();
1899        /**
1900         * Contains the notification to rebuild when this Builder is in "rebuild" mode.
1901         * Null otherwise.
1902         */
1903        private Notification mRebuildNotification = null;
1904
1905        /**
1906         * Whether the build notification has three lines. This is used to make the top padding for
1907         * both the contracted and expanded layout consistent.
1908         *
1909         * <p>
1910         * This field is only valid during the build phase.
1911         */
1912        private boolean mHasThreeLines;
1913
1914        /**
1915         * Constructs a new Builder with the defaults:
1916         *
1917
1918         * <table>
1919         * <tr><th align=right>priority</th>
1920         *     <td>{@link #PRIORITY_DEFAULT}</td></tr>
1921         * <tr><th align=right>when</th>
1922         *     <td>now ({@link System#currentTimeMillis()})</td></tr>
1923         * <tr><th align=right>audio stream</th>
1924         *     <td>{@link #STREAM_DEFAULT}</td></tr>
1925         * </table>
1926         *
1927
1928         * @param context
1929         *            A {@link Context} that will be used by the Builder to construct the
1930         *            RemoteViews. The Context will not be held past the lifetime of this Builder
1931         *            object.
1932         */
1933        public Builder(Context context) {
1934            /*
1935             * Important compatibility note!
1936             * Some apps out in the wild create a Notification.Builder in their Activity subclass
1937             * constructor for later use. At this point Activities - themselves subclasses of
1938             * ContextWrapper - do not have their inner Context populated yet. This means that
1939             * any calls to Context methods from within this constructor can cause NPEs in existing
1940             * apps. Any data populated from mContext should therefore be populated lazily to
1941             * preserve compatibility.
1942             */
1943            mContext = context;
1944
1945            // Set defaults to match the defaults of a Notification
1946            mWhen = System.currentTimeMillis();
1947            mAudioStreamType = STREAM_DEFAULT;
1948            mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
1949            mPriority = PRIORITY_DEFAULT;
1950            mPeople = new ArrayList<String>();
1951
1952            mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP ?
1953                    NotificationColorUtil.getInstance(mContext) : null;
1954        }
1955
1956        /**
1957         * Creates a Builder for rebuilding the given Notification.
1958         * <p>
1959         * Call {@link #rebuild()} to retrieve the rebuilt version of 'n'.
1960         */
1961        private Builder(Context context, Notification n) {
1962            this(context);
1963            mRebuildNotification = n;
1964            restoreFromNotification(n);
1965
1966            Style style = null;
1967            Bundle extras = n.extras;
1968            String templateClass = extras.getString(EXTRA_TEMPLATE);
1969            if (!TextUtils.isEmpty(templateClass)) {
1970                Class<? extends Style> styleClass = getNotificationStyleClass(templateClass);
1971                if (styleClass == null) {
1972                    Log.d(TAG, "Unknown style class: " + styleClass);
1973                    return;
1974                }
1975
1976                try {
1977                    Constructor<? extends Style> constructor = styleClass.getConstructor();
1978                    style = constructor.newInstance();
1979                    style.restoreFromExtras(extras);
1980                } catch (Throwable t) {
1981                    Log.e(TAG, "Could not create Style", t);
1982                    return;
1983                }
1984            }
1985            if (style != null) {
1986                setStyle(style);
1987            }
1988        }
1989
1990        /**
1991         * Add a timestamp pertaining to the notification (usually the time the event occurred).
1992         * It will be shown in the notification content view by default; use
1993         * {@link #setShowWhen(boolean) setShowWhen} to control this.
1994         *
1995         * @see Notification#when
1996         */
1997        public Builder setWhen(long when) {
1998            mWhen = when;
1999            return this;
2000        }
2001
2002        /**
2003         * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
2004         * in the content view.
2005         */
2006        public Builder setShowWhen(boolean show) {
2007            mShowWhen = show;
2008            return this;
2009        }
2010
2011        /**
2012         * Show the {@link Notification#when} field as a stopwatch.
2013         *
2014         * Instead of presenting <code>when</code> as a timestamp, the notification will show an
2015         * automatically updating display of the minutes and seconds since <code>when</code>.
2016         *
2017         * Useful when showing an elapsed time (like an ongoing phone call).
2018         *
2019         * @see android.widget.Chronometer
2020         * @see Notification#when
2021         */
2022        public Builder setUsesChronometer(boolean b) {
2023            mUseChronometer = b;
2024            return this;
2025        }
2026
2027        /**
2028         * Set the small icon resource, which will be used to represent the notification in the
2029         * status bar.
2030         *
2031
2032         * The platform template for the expanded view will draw this icon in the left, unless a
2033         * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2034         * icon will be moved to the right-hand side.
2035         *
2036
2037         * @param icon
2038         *            A resource ID in the application's package of the drawable to use.
2039         * @see Notification#icon
2040         */
2041        public Builder setSmallIcon(int icon) {
2042            mSmallIcon = icon;
2043            return this;
2044        }
2045
2046        /**
2047         * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2048         * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2049         * LevelListDrawable}.
2050         *
2051         * @param icon A resource ID in the application's package of the drawable to use.
2052         * @param level The level to use for the icon.
2053         *
2054         * @see Notification#icon
2055         * @see Notification#iconLevel
2056         */
2057        public Builder setSmallIcon(int icon, int level) {
2058            mSmallIcon = icon;
2059            mSmallIconLevel = level;
2060            return this;
2061        }
2062
2063        /**
2064         * Set the first line of text in the platform notification template.
2065         */
2066        public Builder setContentTitle(CharSequence title) {
2067            mContentTitle = safeCharSequence(title);
2068            return this;
2069        }
2070
2071        /**
2072         * Set the second line of text in the platform notification template.
2073         */
2074        public Builder setContentText(CharSequence text) {
2075            mContentText = safeCharSequence(text);
2076            return this;
2077        }
2078
2079        /**
2080         * Set the third line of text in the platform notification template.
2081         * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2082         * same location in the standard template.
2083         */
2084        public Builder setSubText(CharSequence text) {
2085            mSubText = safeCharSequence(text);
2086            return this;
2087        }
2088
2089        /**
2090         * Set the large number at the right-hand side of the notification.  This is
2091         * equivalent to setContentInfo, although it might show the number in a different
2092         * font size for readability.
2093         */
2094        public Builder setNumber(int number) {
2095            mNumber = number;
2096            return this;
2097        }
2098
2099        /**
2100         * A small piece of additional information pertaining to this notification.
2101         *
2102         * The platform template will draw this on the last line of the notification, at the far
2103         * right (to the right of a smallIcon if it has been placed there).
2104         */
2105        public Builder setContentInfo(CharSequence info) {
2106            mContentInfo = safeCharSequence(info);
2107            return this;
2108        }
2109
2110        /**
2111         * Set the progress this notification represents.
2112         *
2113         * The platform template will represent this using a {@link ProgressBar}.
2114         */
2115        public Builder setProgress(int max, int progress, boolean indeterminate) {
2116            mProgressMax = max;
2117            mProgress = progress;
2118            mProgressIndeterminate = indeterminate;
2119            return this;
2120        }
2121
2122        /**
2123         * Supply a custom RemoteViews to use instead of the platform template.
2124         *
2125         * @see Notification#contentView
2126         */
2127        public Builder setContent(RemoteViews views) {
2128            mContentView = views;
2129            return this;
2130        }
2131
2132        /**
2133         * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2134         *
2135         * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2136         * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2137         * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
2138         * to assign PendingIntents to individual views in that custom layout (i.e., to create
2139         * clickable buttons inside the notification view).
2140         *
2141         * @see Notification#contentIntent Notification.contentIntent
2142         */
2143        public Builder setContentIntent(PendingIntent intent) {
2144            mContentIntent = intent;
2145            return this;
2146        }
2147
2148        /**
2149         * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2150         *
2151         * @see Notification#deleteIntent
2152         */
2153        public Builder setDeleteIntent(PendingIntent intent) {
2154            mDeleteIntent = intent;
2155            return this;
2156        }
2157
2158        /**
2159         * An intent to launch instead of posting the notification to the status bar.
2160         * Only for use with extremely high-priority notifications demanding the user's
2161         * <strong>immediate</strong> attention, such as an incoming phone call or
2162         * alarm clock that the user has explicitly set to a particular time.
2163         * If this facility is used for something else, please give the user an option
2164         * to turn it off and use a normal notification, as this can be extremely
2165         * disruptive.
2166         *
2167         * <p>
2168         * The system UI may choose to display a heads-up notification, instead of
2169         * launching this intent, while the user is using the device.
2170         * </p>
2171         *
2172         * @param intent The pending intent to launch.
2173         * @param highPriority Passing true will cause this notification to be sent
2174         *          even if other notifications are suppressed.
2175         *
2176         * @see Notification#fullScreenIntent
2177         */
2178        public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
2179            mFullScreenIntent = intent;
2180            setFlag(FLAG_HIGH_PRIORITY, highPriority);
2181            return this;
2182        }
2183
2184        /**
2185         * Set the "ticker" text which is sent to accessibility services.
2186         *
2187         * @see Notification#tickerText
2188         */
2189        public Builder setTicker(CharSequence tickerText) {
2190            mTickerText = safeCharSequence(tickerText);
2191            return this;
2192        }
2193
2194        /**
2195         * Obsolete version of {@link #setTicker(CharSequence)}.
2196         *
2197         */
2198        @Deprecated
2199        public Builder setTicker(CharSequence tickerText, RemoteViews views) {
2200            mTickerText = safeCharSequence(tickerText);
2201            mTickerView = views; // we'll save it for you anyway
2202            return this;
2203        }
2204
2205        /**
2206         * Add a large icon to the notification (and the ticker on some devices).
2207         *
2208         * In the platform template, this image will be shown on the left of the notification view
2209         * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side).
2210         *
2211         * @see Notification#largeIcon
2212         */
2213        public Builder setLargeIcon(Bitmap icon) {
2214            mLargeIcon = icon;
2215            return this;
2216        }
2217
2218        /**
2219         * Set the sound to play.
2220         *
2221         * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2222         * for notifications.
2223         *
2224         * <p>
2225         * A notification that is noisy is more likely to be presented as a heads-up notification.
2226         * </p>
2227         *
2228         * @see Notification#sound
2229         */
2230        public Builder setSound(Uri sound) {
2231            mSound = sound;
2232            mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
2233            return this;
2234        }
2235
2236        /**
2237         * Set the sound to play, along with a specific stream on which to play it.
2238         *
2239         * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2240         *
2241         * <p>
2242         * A notification that is noisy is more likely to be presented as a heads-up notification.
2243         * </p>
2244         * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
2245         * @see Notification#sound
2246         */
2247        @Deprecated
2248        public Builder setSound(Uri sound, int streamType) {
2249            mSound = sound;
2250            mAudioStreamType = streamType;
2251            return this;
2252        }
2253
2254        /**
2255         * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2256         * use during playback.
2257         *
2258         * <p>
2259         * A notification that is noisy is more likely to be presented as a heads-up notification.
2260         * </p>
2261         *
2262         * @see Notification#sound
2263         */
2264        public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
2265            mSound = sound;
2266            mAudioAttributes = audioAttributes;
2267            return this;
2268        }
2269
2270        /**
2271         * Set the vibration pattern to use.
2272         *
2273         * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2274         * <code>pattern</code> parameter.
2275         *
2276         * <p>
2277         * A notification that vibrates is more likely to be presented as a heads-up notification.
2278         * </p>
2279         *
2280         * @see Notification#vibrate
2281         */
2282        public Builder setVibrate(long[] pattern) {
2283            mVibrate = pattern;
2284            return this;
2285        }
2286
2287        /**
2288         * Set the desired color for the indicator LED on the device, as well as the
2289         * blink duty cycle (specified in milliseconds).
2290         *
2291
2292         * Not all devices will honor all (or even any) of these values.
2293         *
2294
2295         * @see Notification#ledARGB
2296         * @see Notification#ledOnMS
2297         * @see Notification#ledOffMS
2298         */
2299        public Builder setLights(int argb, int onMs, int offMs) {
2300            mLedArgb = argb;
2301            mLedOnMs = onMs;
2302            mLedOffMs = offMs;
2303            return this;
2304        }
2305
2306        /**
2307         * Set whether this is an "ongoing" notification.
2308         *
2309
2310         * Ongoing notifications cannot be dismissed by the user, so your application or service
2311         * must take care of canceling them.
2312         *
2313
2314         * They are typically used to indicate a background task that the user is actively engaged
2315         * with (e.g., playing music) or is pending in some way and therefore occupying the device
2316         * (e.g., a file download, sync operation, active network connection).
2317         *
2318
2319         * @see Notification#FLAG_ONGOING_EVENT
2320         * @see Service#setForeground(boolean)
2321         */
2322        public Builder setOngoing(boolean ongoing) {
2323            setFlag(FLAG_ONGOING_EVENT, ongoing);
2324            return this;
2325        }
2326
2327        /**
2328         * Set this flag if you would only like the sound, vibrate
2329         * and ticker to be played if the notification is not already showing.
2330         *
2331         * @see Notification#FLAG_ONLY_ALERT_ONCE
2332         */
2333        public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2334            setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2335            return this;
2336        }
2337
2338        /**
2339         * Make this notification automatically dismissed when the user touches it. The
2340         * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2341         *
2342         * @see Notification#FLAG_AUTO_CANCEL
2343         */
2344        public Builder setAutoCancel(boolean autoCancel) {
2345            setFlag(FLAG_AUTO_CANCEL, autoCancel);
2346            return this;
2347        }
2348
2349        /**
2350         * Set whether or not this notification should not bridge to other devices.
2351         *
2352         * <p>Some notifications can be bridged to other devices for remote display.
2353         * This hint can be set to recommend this notification not be bridged.
2354         */
2355        public Builder setLocalOnly(boolean localOnly) {
2356            setFlag(FLAG_LOCAL_ONLY, localOnly);
2357            return this;
2358        }
2359
2360        /**
2361         * Set which notification properties will be inherited from system defaults.
2362         * <p>
2363         * The value should be one or more of the following fields combined with
2364         * bitwise-or:
2365         * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2366         * <p>
2367         * For all default values, use {@link #DEFAULT_ALL}.
2368         */
2369        public Builder setDefaults(int defaults) {
2370            mDefaults = defaults;
2371            return this;
2372        }
2373
2374        /**
2375         * Set the priority of this notification.
2376         *
2377         * @see Notification#priority
2378         */
2379        public Builder setPriority(@Priority int pri) {
2380            mPriority = pri;
2381            return this;
2382        }
2383
2384        /**
2385         * Set the notification category.
2386         *
2387         * @see Notification#category
2388         */
2389        public Builder setCategory(String category) {
2390            mCategory = category;
2391            return this;
2392        }
2393
2394        /**
2395         * Add a person that is relevant to this notification.
2396         *
2397         * <P>
2398         * Depending on user preferences, this annotation may allow the notification to pass
2399         * through interruption filters, and to appear more prominently in the user interface.
2400         * </P>
2401         *
2402         * <P>
2403         * The person should be specified by the {@code String} representation of a
2404         * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2405         * </P>
2406         *
2407         * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2408         * URIs.  The path part of these URIs must exist in the contacts database, in the
2409         * appropriate column, or the reference will be discarded as invalid. Telephone schema
2410         * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2411         * </P>
2412         *
2413         * @param uri A URI for the person.
2414         * @see Notification#EXTRA_PEOPLE
2415         */
2416        public Builder addPerson(String uri) {
2417            mPeople.add(uri);
2418            return this;
2419        }
2420
2421        /**
2422         * Set this notification to be part of a group of notifications sharing the same key.
2423         * Grouped notifications may display in a cluster or stack on devices which
2424         * support such rendering.
2425         *
2426         * <p>To make this notification the summary for its group, also call
2427         * {@link #setGroupSummary}. A sort order can be specified for group members by using
2428         * {@link #setSortKey}.
2429         * @param groupKey The group key of the group.
2430         * @return this object for method chaining
2431         */
2432        public Builder setGroup(String groupKey) {
2433            mGroupKey = groupKey;
2434            return this;
2435        }
2436
2437        /**
2438         * Set this notification to be the group summary for a group of notifications.
2439         * Grouped notifications may display in a cluster or stack on devices which
2440         * support such rendering. Requires a group key also be set using {@link #setGroup}.
2441         * @param isGroupSummary Whether this notification should be a group summary.
2442         * @return this object for method chaining
2443         */
2444        public Builder setGroupSummary(boolean isGroupSummary) {
2445            setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2446            return this;
2447        }
2448
2449        /**
2450         * Set a sort key that orders this notification among other notifications from the
2451         * same package. This can be useful if an external sort was already applied and an app
2452         * would like to preserve this. Notifications will be sorted lexicographically using this
2453         * value, although providing different priorities in addition to providing sort key may
2454         * cause this value to be ignored.
2455         *
2456         * <p>This sort key can also be used to order members of a notification group. See
2457         * {@link #setGroup}.
2458         *
2459         * @see String#compareTo(String)
2460         */
2461        public Builder setSortKey(String sortKey) {
2462            mSortKey = sortKey;
2463            return this;
2464        }
2465
2466        /**
2467         * Merge additional metadata into this notification.
2468         *
2469         * <p>Values within the Bundle will replace existing extras values in this Builder.
2470         *
2471         * @see Notification#extras
2472         */
2473        public Builder addExtras(Bundle extras) {
2474            if (extras != null) {
2475                if (mExtras == null) {
2476                    mExtras = new Bundle(extras);
2477                } else {
2478                    mExtras.putAll(extras);
2479                }
2480            }
2481            return this;
2482        }
2483
2484        /**
2485         * Set metadata for this notification.
2486         *
2487         * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
2488         * current contents are copied into the Notification each time {@link #build()} is
2489         * called.
2490         *
2491         * <p>Replaces any existing extras values with those from the provided Bundle.
2492         * Use {@link #addExtras} to merge in metadata instead.
2493         *
2494         * @see Notification#extras
2495         */
2496        public Builder setExtras(Bundle extras) {
2497            mExtras = extras;
2498            return this;
2499        }
2500
2501        /**
2502         * Get the current metadata Bundle used by this notification Builder.
2503         *
2504         * <p>The returned Bundle is shared with this Builder.
2505         *
2506         * <p>The current contents of this Bundle are copied into the Notification each time
2507         * {@link #build()} is called.
2508         *
2509         * @see Notification#extras
2510         */
2511        public Bundle getExtras() {
2512            if (mExtras == null) {
2513                mExtras = new Bundle();
2514            }
2515            return mExtras;
2516        }
2517
2518        /**
2519         * Add an action to this notification. Actions are typically displayed by
2520         * the system as a button adjacent to the notification content.
2521         * <p>
2522         * Every action must have an icon (32dp square and matching the
2523         * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2524         * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2525         * <p>
2526         * A notification in its expanded form can display up to 3 actions, from left to right in
2527         * the order they were added. Actions will not be displayed when the notification is
2528         * collapsed, however, so be sure that any essential functions may be accessed by the user
2529         * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2530         *
2531         * @param icon Resource ID of a drawable that represents the action.
2532         * @param title Text describing the action.
2533         * @param intent PendingIntent to be fired when the action is invoked.
2534         */
2535        public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
2536            mActions.add(new Action(icon, safeCharSequence(title), intent));
2537            return this;
2538        }
2539
2540        /**
2541         * Add an action to this notification. Actions are typically displayed by
2542         * the system as a button adjacent to the notification content.
2543         * <p>
2544         * Every action must have an icon (32dp square and matching the
2545         * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2546         * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2547         * <p>
2548         * A notification in its expanded form can display up to 3 actions, from left to right in
2549         * the order they were added. Actions will not be displayed when the notification is
2550         * collapsed, however, so be sure that any essential functions may be accessed by the user
2551         * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2552         *
2553         * @param action The action to add.
2554         */
2555        public Builder addAction(Action action) {
2556            mActions.add(action);
2557            return this;
2558        }
2559
2560        /**
2561         * Add a rich notification style to be applied at build time.
2562         *
2563         * @param style Object responsible for modifying the notification style.
2564         */
2565        public Builder setStyle(Style style) {
2566            if (mStyle != style) {
2567                mStyle = style;
2568                if (mStyle != null) {
2569                    mStyle.setBuilder(this);
2570                }
2571            }
2572            return this;
2573        }
2574
2575        /**
2576         * Specify the value of {@link #visibility}.
2577         *
2578         * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2579         * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2580         *
2581         * @return The same Builder.
2582         */
2583        public Builder setVisibility(int visibility) {
2584            mVisibility = visibility;
2585            return this;
2586        }
2587
2588        /**
2589         * Supply a replacement Notification whose contents should be shown in insecure contexts
2590         * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2591         * @param n A replacement notification, presumably with some or all info redacted.
2592         * @return The same Builder.
2593         */
2594        public Builder setPublicVersion(Notification n) {
2595            mPublicVersion = n;
2596            return this;
2597        }
2598
2599        /**
2600         * Apply an extender to this notification builder. Extenders may be used to add
2601         * metadata or change options on this builder.
2602         */
2603        public Builder extend(Extender extender) {
2604            extender.extend(this);
2605            return this;
2606        }
2607
2608        private void setFlag(int mask, boolean value) {
2609            if (value) {
2610                mFlags |= mask;
2611            } else {
2612                mFlags &= ~mask;
2613            }
2614        }
2615
2616        /**
2617         * Sets {@link Notification#color}.
2618         *
2619         * @param argb The accent color to use
2620         *
2621         * @return The same Builder.
2622         */
2623        public Builder setColor(int argb) {
2624            mColor = argb;
2625            return this;
2626        }
2627
2628        private Drawable getProfileBadgeDrawable() {
2629            // Note: This assumes that the current user can read the profile badge of the
2630            // originating user.
2631            return mContext.getPackageManager().getUserBadgeForDensity(
2632                    new UserHandle(mOriginatingUserId), 0);
2633        }
2634
2635        private Bitmap getProfileBadge() {
2636            Drawable badge = getProfileBadgeDrawable();
2637            if (badge == null) {
2638                return null;
2639            }
2640            final int size = mContext.getResources().getDimensionPixelSize(
2641                    R.dimen.notification_badge_size);
2642            Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
2643            Canvas canvas = new Canvas(bitmap);
2644            badge.setBounds(0, 0, size, size);
2645            badge.draw(canvas);
2646            return bitmap;
2647        }
2648
2649        private boolean addProfileBadge(RemoteViews contentView, int resId) {
2650            Bitmap profileBadge = getProfileBadge();
2651
2652            contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
2653            contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
2654            contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2655
2656            if (profileBadge != null) {
2657                contentView.setImageViewBitmap(resId, profileBadge);
2658                contentView.setViewVisibility(resId, View.VISIBLE);
2659
2660                // Make sure Line 3 is visible. As badge will be here if there
2661                // is no text to display.
2662                if (resId == R.id.profile_badge_line3) {
2663                    contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2664                }
2665                return true;
2666            }
2667            return false;
2668        }
2669
2670        private void shrinkLine3Text(RemoteViews contentView) {
2671            float subTextSize = mContext.getResources().getDimensionPixelSize(
2672                    R.dimen.notification_subtext_size);
2673            contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
2674        }
2675
2676        private void unshrinkLine3Text(RemoteViews contentView) {
2677            float regularTextSize = mContext.getResources().getDimensionPixelSize(
2678                    com.android.internal.R.dimen.notification_text_size);
2679            contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
2680        }
2681
2682        private void resetStandardTemplate(RemoteViews contentView) {
2683            removeLargeIconBackground(contentView);
2684            contentView.setViewPadding(R.id.icon, 0, 0, 0, 0);
2685            contentView.setImageViewResource(R.id.icon, 0);
2686            contentView.setInt(R.id.icon, "setBackgroundResource", 0);
2687            contentView.setViewVisibility(R.id.right_icon, View.GONE);
2688            contentView.setInt(R.id.right_icon, "setBackgroundResource", 0);
2689            contentView.setImageViewResource(R.id.right_icon, 0);
2690            contentView.setImageViewResource(R.id.icon, 0);
2691            contentView.setTextViewText(R.id.title, null);
2692            contentView.setTextViewText(R.id.text, null);
2693            unshrinkLine3Text(contentView);
2694            contentView.setTextViewText(R.id.text2, null);
2695            contentView.setViewVisibility(R.id.text2, View.GONE);
2696            contentView.setViewVisibility(R.id.info, View.GONE);
2697            contentView.setViewVisibility(R.id.time, View.GONE);
2698            contentView.setViewVisibility(R.id.line3, View.GONE);
2699            contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
2700            contentView.setViewVisibility(R.id.progress, View.GONE);
2701            contentView.setViewVisibility(R.id.chronometer, View.GONE);
2702            contentView.setViewVisibility(R.id.time, View.GONE);
2703        }
2704
2705        private RemoteViews applyStandardTemplate(int resId) {
2706            return applyStandardTemplate(resId, true /* hasProgress */);
2707        }
2708
2709        /**
2710         * @param hasProgress whether the progress bar should be shown and set
2711         */
2712        private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
2713            RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
2714
2715            resetStandardTemplate(contentView);
2716
2717            boolean showLine3 = false;
2718            boolean showLine2 = false;
2719            boolean contentTextInLine2 = false;
2720
2721            if (mLargeIcon != null) {
2722                contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
2723                processLargeLegacyIcon(mLargeIcon, contentView);
2724                contentView.setImageViewResource(R.id.right_icon, mSmallIcon);
2725                contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
2726                processSmallRightIcon(mSmallIcon, contentView);
2727            } else { // small icon at left
2728                contentView.setImageViewResource(R.id.icon, mSmallIcon);
2729                contentView.setViewVisibility(R.id.icon, View.VISIBLE);
2730                processSmallIconAsLarge(mSmallIcon, contentView);
2731            }
2732            if (mContentTitle != null) {
2733                contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle));
2734            }
2735            if (mContentText != null) {
2736                contentView.setTextViewText(R.id.text, processLegacyText(mContentText));
2737                showLine3 = true;
2738            }
2739            if (mContentInfo != null) {
2740                contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo));
2741                contentView.setViewVisibility(R.id.info, View.VISIBLE);
2742                showLine3 = true;
2743            } else if (mNumber > 0) {
2744                final int tooBig = mContext.getResources().getInteger(
2745                        R.integer.status_bar_notification_info_maxnum);
2746                if (mNumber > tooBig) {
2747                    contentView.setTextViewText(R.id.info, processLegacyText(
2748                            mContext.getResources().getString(
2749                                    R.string.status_bar_notification_info_overflow)));
2750                } else {
2751                    NumberFormat f = NumberFormat.getIntegerInstance();
2752                    contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber)));
2753                }
2754                contentView.setViewVisibility(R.id.info, View.VISIBLE);
2755                showLine3 = true;
2756            } else {
2757                contentView.setViewVisibility(R.id.info, View.GONE);
2758            }
2759
2760            // Need to show three lines?
2761            if (mSubText != null) {
2762                contentView.setTextViewText(R.id.text, processLegacyText(mSubText));
2763                if (mContentText != null) {
2764                    contentView.setTextViewText(R.id.text2, processLegacyText(mContentText));
2765                    contentView.setViewVisibility(R.id.text2, View.VISIBLE);
2766                    showLine2 = true;
2767                    contentTextInLine2 = true;
2768                } else {
2769                    contentView.setViewVisibility(R.id.text2, View.GONE);
2770                }
2771            } else {
2772                contentView.setViewVisibility(R.id.text2, View.GONE);
2773                if (hasProgress && (mProgressMax != 0 || mProgressIndeterminate)) {
2774                    contentView.setViewVisibility(R.id.progress, View.VISIBLE);
2775                    contentView.setProgressBar(
2776                            R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
2777                    contentView.setProgressBackgroundTintList(
2778                            R.id.progress, ColorStateList.valueOf(mContext.getResources().getColor(
2779                                    R.color.notification_progress_background_color)));
2780                    if (mColor != COLOR_DEFAULT) {
2781                        ColorStateList colorStateList = ColorStateList.valueOf(mColor);
2782                        contentView.setProgressTintList(R.id.progress, colorStateList);
2783                        contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
2784                    }
2785                    showLine2 = true;
2786                } else {
2787                    contentView.setViewVisibility(R.id.progress, View.GONE);
2788                }
2789            }
2790            if (showLine2) {
2791
2792                // need to shrink all the type to make sure everything fits
2793                shrinkLine3Text(contentView);
2794            }
2795
2796            if (showsTimeOrChronometer()) {
2797                if (mUseChronometer) {
2798                    contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
2799                    contentView.setLong(R.id.chronometer, "setBase",
2800                            mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
2801                    contentView.setBoolean(R.id.chronometer, "setStarted", true);
2802                } else {
2803                    contentView.setViewVisibility(R.id.time, View.VISIBLE);
2804                    contentView.setLong(R.id.time, "setTime", mWhen);
2805                }
2806            }
2807
2808            // Adjust padding depending on line count and font size.
2809            contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext,
2810                    mHasThreeLines, mContext.getResources().getConfiguration().fontScale),
2811                    0, 0);
2812
2813            // We want to add badge to first line of text.
2814            boolean addedBadge = addProfileBadge(contentView,
2815                    contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
2816            // If we added the badge to line 3 then we should show line 3.
2817            if (addedBadge && !contentTextInLine2) {
2818                showLine3 = true;
2819            }
2820
2821            // Note getStandardView may hide line 3 again.
2822            contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
2823            contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE);
2824            return contentView;
2825        }
2826
2827        /**
2828         * @return true if the built notification will show the time or the chronometer; false
2829         *         otherwise
2830         */
2831        private boolean showsTimeOrChronometer() {
2832            return mWhen != 0 && mShowWhen;
2833        }
2834
2835        /**
2836         * Logic to find out whether the notification is going to have three lines in the contracted
2837         * layout. This is used to adjust the top padding.
2838         *
2839         * @return true if the notification is going to have three lines; false if the notification
2840         *         is going to have one or two lines
2841         */
2842        private boolean hasThreeLines() {
2843            boolean contentTextInLine2 = mSubText != null && mContentText != null;
2844            boolean hasProgress = mStyle == null || mStyle.hasProgress();
2845            // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
2846            boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
2847            boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0
2848                    || badgeInLine3;
2849            boolean hasLine2 = (mSubText != null && mContentText != null) ||
2850                    (hasProgress && mSubText == null
2851                            && (mProgressMax != 0 || mProgressIndeterminate));
2852            return hasLine2 && hasLine3;
2853        }
2854
2855        /**
2856         * @hide
2857         */
2858        public static int calculateTopPadding(Context ctx, boolean hasThreeLines,
2859                float fontScale) {
2860            int padding = ctx.getResources().getDimensionPixelSize(hasThreeLines
2861                    ? R.dimen.notification_top_pad_narrow
2862                    : R.dimen.notification_top_pad);
2863            int largePadding = ctx.getResources().getDimensionPixelSize(hasThreeLines
2864                    ? R.dimen.notification_top_pad_large_text_narrow
2865                    : R.dimen.notification_top_pad_large_text);
2866            float largeFactor = (MathUtils.constrain(fontScale, 1.0f, LARGE_TEXT_SCALE) - 1f)
2867                    / (LARGE_TEXT_SCALE - 1f);
2868
2869            // Linearly interpolate the padding between large and normal with the font scale ranging
2870            // from 1f to LARGE_TEXT_SCALE
2871            return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
2872        }
2873
2874        private void resetStandardTemplateWithActions(RemoteViews big) {
2875            big.setViewVisibility(R.id.actions, View.GONE);
2876            big.setViewVisibility(R.id.action_divider, View.GONE);
2877            big.removeAllViews(R.id.actions);
2878        }
2879
2880        private RemoteViews applyStandardTemplateWithActions(int layoutId) {
2881            RemoteViews big = applyStandardTemplate(layoutId);
2882
2883            resetStandardTemplateWithActions(big);
2884
2885            int N = mActions.size();
2886            if (N > 0) {
2887                big.setViewVisibility(R.id.actions, View.VISIBLE);
2888                big.setViewVisibility(R.id.action_divider, View.VISIBLE);
2889                if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
2890                for (int i=0; i<N; i++) {
2891                    final RemoteViews button = generateActionButton(mActions.get(i));
2892                    big.addView(R.id.actions, button);
2893                }
2894            }
2895            return big;
2896        }
2897
2898        private RemoteViews makeContentView() {
2899            if (mContentView != null) {
2900                return mContentView;
2901            } else {
2902                return applyStandardTemplate(getBaseLayoutResource());
2903            }
2904        }
2905
2906        private RemoteViews makeTickerView() {
2907            if (mTickerView != null) {
2908                return mTickerView;
2909            }
2910            return null; // tickers are not created by default anymore
2911        }
2912
2913        private RemoteViews makeBigContentView() {
2914            if (mActions.size() == 0) return null;
2915
2916            return applyStandardTemplateWithActions(getBigBaseLayoutResource());
2917        }
2918
2919        private RemoteViews makeHeadsUpContentView() {
2920            if (mActions.size() == 0) return null;
2921
2922            return applyStandardTemplateWithActions(getBigBaseLayoutResource());
2923        }
2924
2925
2926        private RemoteViews generateActionButton(Action action) {
2927            final boolean tombstone = (action.actionIntent == null);
2928            RemoteViews button = new RemoteViews(mContext.getPackageName(),
2929                    tombstone ? getActionTombstoneLayoutResource()
2930                              : getActionLayoutResource());
2931            button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0);
2932            button.setTextViewText(R.id.action0, processLegacyText(action.title));
2933            if (!tombstone) {
2934                button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
2935            }
2936            button.setContentDescription(R.id.action0, action.title);
2937            processLegacyAction(action, button);
2938            return button;
2939        }
2940
2941        /**
2942         * @return Whether we are currently building a notification from a legacy (an app that
2943         *         doesn't create material notifications by itself) app.
2944         */
2945        private boolean isLegacy() {
2946            return mColorUtil != null;
2947        }
2948
2949        private void processLegacyAction(Action action, RemoteViews button) {
2950            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.icon)) {
2951                button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
2952                        mContext.getResources().getColor(R.color.notification_action_color_filter),
2953                        PorterDuff.Mode.MULTIPLY);
2954            }
2955        }
2956
2957        private CharSequence processLegacyText(CharSequence charSequence) {
2958            if (isLegacy()) {
2959                return mColorUtil.invertCharSequenceColors(charSequence);
2960            } else {
2961                return charSequence;
2962            }
2963        }
2964
2965        /**
2966         * Apply any necessary background to smallIcons being used in the largeIcon spot.
2967         */
2968        private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) {
2969            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, largeIconId)) {
2970                applyLargeIconBackground(contentView);
2971            }
2972        }
2973
2974        /**
2975         * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
2976         * if it's grayscale).
2977         */
2978        // TODO: also check bounds, transparency, that sort of thing.
2979        private void processLargeLegacyIcon(Bitmap largeIcon, RemoteViews contentView) {
2980            if (isLegacy() && mColorUtil.isGrayscaleIcon(largeIcon)) {
2981                applyLargeIconBackground(contentView);
2982            } else {
2983                removeLargeIconBackground(contentView);
2984            }
2985        }
2986
2987        /**
2988         * Add a colored circle behind the largeIcon slot.
2989         */
2990        private void applyLargeIconBackground(RemoteViews contentView) {
2991            contentView.setInt(R.id.icon, "setBackgroundResource",
2992                    R.drawable.notification_icon_legacy_bg);
2993
2994            contentView.setDrawableParameters(
2995                    R.id.icon,
2996                    true,
2997                    -1,
2998                    resolveColor(),
2999                    PorterDuff.Mode.SRC_ATOP,
3000                    -1);
3001
3002            int padding = mContext.getResources().getDimensionPixelSize(
3003                    R.dimen.notification_large_icon_circle_padding);
3004            contentView.setViewPadding(R.id.icon, padding, padding, padding, padding);
3005        }
3006
3007        private void removeLargeIconBackground(RemoteViews contentView) {
3008            contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3009        }
3010
3011        /**
3012         * Recolor small icons when used in the R.id.right_icon slot.
3013         */
3014        private void processSmallRightIcon(int smallIconDrawableId,
3015                RemoteViews contentView) {
3016            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, smallIconDrawableId)) {
3017                contentView.setDrawableParameters(R.id.right_icon, false, -1,
3018                        0xFFFFFFFF,
3019                        PorterDuff.Mode.SRC_ATOP, -1);
3020
3021                contentView.setInt(R.id.right_icon,
3022                        "setBackgroundResource",
3023                        R.drawable.notification_icon_legacy_bg);
3024
3025                contentView.setDrawableParameters(
3026                        R.id.right_icon,
3027                        true,
3028                        -1,
3029                        resolveColor(),
3030                        PorterDuff.Mode.SRC_ATOP,
3031                        -1);
3032            }
3033        }
3034
3035        private int sanitizeColor() {
3036            if (mColor != COLOR_DEFAULT) {
3037                mColor |= 0xFF000000; // no alpha for custom colors
3038            }
3039            return mColor;
3040        }
3041
3042        private int resolveColor() {
3043            if (mColor == COLOR_DEFAULT) {
3044                return mContext.getResources().getColor(R.color.notification_icon_bg_color);
3045            }
3046            return mColor;
3047        }
3048
3049        /**
3050         * Apply the unstyled operations and return a new {@link Notification} object.
3051         * @hide
3052         */
3053        public Notification buildUnstyled() {
3054            Notification n = new Notification();
3055            n.when = mWhen;
3056            n.icon = mSmallIcon;
3057            n.iconLevel = mSmallIconLevel;
3058            n.number = mNumber;
3059
3060            n.color = sanitizeColor();
3061
3062            setBuilderContentView(n, makeContentView());
3063            n.contentIntent = mContentIntent;
3064            n.deleteIntent = mDeleteIntent;
3065            n.fullScreenIntent = mFullScreenIntent;
3066            n.tickerText = mTickerText;
3067            n.tickerView = makeTickerView();
3068            n.largeIcon = mLargeIcon;
3069            n.sound = mSound;
3070            n.audioStreamType = mAudioStreamType;
3071            n.audioAttributes = mAudioAttributes;
3072            n.vibrate = mVibrate;
3073            n.ledARGB = mLedArgb;
3074            n.ledOnMS = mLedOnMs;
3075            n.ledOffMS = mLedOffMs;
3076            n.defaults = mDefaults;
3077            n.flags = mFlags;
3078            setBuilderBigContentView(n, makeBigContentView());
3079            setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
3080            if (mLedOnMs != 0 || mLedOffMs != 0) {
3081                n.flags |= FLAG_SHOW_LIGHTS;
3082            }
3083            if ((mDefaults & DEFAULT_LIGHTS) != 0) {
3084                n.flags |= FLAG_SHOW_LIGHTS;
3085            }
3086            n.category = mCategory;
3087            n.mGroupKey = mGroupKey;
3088            n.mSortKey = mSortKey;
3089            n.priority = mPriority;
3090            if (mActions.size() > 0) {
3091                n.actions = new Action[mActions.size()];
3092                mActions.toArray(n.actions);
3093            }
3094            n.visibility = mVisibility;
3095
3096            if (mPublicVersion != null) {
3097                n.publicVersion = new Notification();
3098                mPublicVersion.cloneInto(n.publicVersion, true);
3099            }
3100            // Note: If you're adding new fields, also update restoreFromNotitification().
3101            return n;
3102        }
3103
3104        /**
3105         * Capture, in the provided bundle, semantic information used in the construction of
3106         * this Notification object.
3107         * @hide
3108         */
3109        public void populateExtras(Bundle extras) {
3110            // Store original information used in the construction of this object
3111            extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
3112            extras.putParcelable(EXTRA_REBUILD_CONTEXT_APPLICATION_INFO,
3113                    mContext.getApplicationInfo());
3114            extras.putCharSequence(EXTRA_TITLE, mContentTitle);
3115            extras.putCharSequence(EXTRA_TEXT, mContentText);
3116            extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
3117            extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo);
3118            extras.putInt(EXTRA_SMALL_ICON, mSmallIcon);
3119            extras.putInt(EXTRA_PROGRESS, mProgress);
3120            extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax);
3121            extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate);
3122            extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer);
3123            extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen);
3124            if (mLargeIcon != null) {
3125                extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
3126            }
3127            if (!mPeople.isEmpty()) {
3128                extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
3129            }
3130            // NOTE: If you're adding new extras also update restoreFromNotification().
3131        }
3132
3133
3134        /**
3135         * @hide
3136         */
3137        public static void stripForDelivery(Notification n) {
3138            if (!STRIP_AND_REBUILD) {
3139                return;
3140            }
3141
3142            String templateClass = n.extras.getString(EXTRA_TEMPLATE);
3143            // Only strip views for known Styles because we won't know how to
3144            // re-create them otherwise.
3145            boolean stripViews = TextUtils.isEmpty(templateClass) ||
3146                    getNotificationStyleClass(templateClass) != null;
3147
3148            boolean isStripped = false;
3149
3150            if (n.largeIcon != null && n.extras.containsKey(EXTRA_LARGE_ICON)) {
3151                // TODO: Would like to check for equality here, but if the notification
3152                // has been cloned, we can't.
3153                n.largeIcon = null;
3154                n.extras.putBoolean(EXTRA_REBUILD_LARGE_ICON, true);
3155                isStripped = true;
3156            }
3157            // Get rid of unmodified BuilderRemoteViews.
3158
3159            if (stripViews &&
3160                    n.contentView instanceof BuilderRemoteViews &&
3161                    n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
3162                            n.contentView.getSequenceNumber()) {
3163                n.contentView = null;
3164                n.extras.putBoolean(EXTRA_REBUILD_CONTENT_VIEW, true);
3165                n.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
3166                isStripped = true;
3167            }
3168            if (stripViews &&
3169                    n.bigContentView instanceof BuilderRemoteViews &&
3170                    n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
3171                            n.bigContentView.getSequenceNumber()) {
3172                n.bigContentView = null;
3173                n.extras.putBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW, true);
3174                n.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
3175                isStripped = true;
3176            }
3177            if (stripViews &&
3178                    n.headsUpContentView instanceof BuilderRemoteViews &&
3179                    n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
3180                            n.headsUpContentView.getSequenceNumber()) {
3181                n.headsUpContentView = null;
3182                n.extras.putBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW, true);
3183                n.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
3184                isStripped = true;
3185            }
3186
3187            if (isStripped) {
3188                n.extras.putBoolean(EXTRA_NEEDS_REBUILD, true);
3189            }
3190        }
3191
3192        /**
3193         * @hide
3194         */
3195        public static Notification rebuild(Context context, Notification n) {
3196            Bundle extras = n.extras;
3197            if (!extras.getBoolean(EXTRA_NEEDS_REBUILD)) return n;
3198            extras.remove(EXTRA_NEEDS_REBUILD);
3199
3200            // Re-create notification context so we can access app resources.
3201            ApplicationInfo applicationInfo = extras.getParcelable(
3202                    EXTRA_REBUILD_CONTEXT_APPLICATION_INFO);
3203            Context builderContext;
3204            try {
3205                builderContext = context.createApplicationContext(applicationInfo,
3206                        Context.CONTEXT_RESTRICTED);
3207            } catch (NameNotFoundException e) {
3208                Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3209                builderContext = context;  // try with our context
3210            }
3211
3212            Builder b = new Builder(builderContext, n);
3213            return b.rebuild();
3214        }
3215
3216        /**
3217         * Rebuilds the notification passed in to the rebuild-constructor
3218         * {@link #Builder(Context, Notification)}.
3219         *
3220         * <p>
3221         * Throws IllegalStateException when invoked on a Builder that isn't in rebuild mode.
3222         *
3223         * @hide
3224         */
3225        private Notification rebuild() {
3226            if (mRebuildNotification == null) {
3227                throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
3228            }
3229            mHasThreeLines = hasThreeLines();
3230
3231            Bundle extras = mRebuildNotification.extras;
3232
3233            if (extras.getBoolean(EXTRA_REBUILD_LARGE_ICON)) {
3234                mRebuildNotification.largeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
3235            }
3236            extras.remove(EXTRA_REBUILD_LARGE_ICON);
3237
3238            if (extras.getBoolean(EXTRA_REBUILD_CONTENT_VIEW)) {
3239                setBuilderContentView(mRebuildNotification, makeContentView());
3240                if (mStyle != null) {
3241                    mStyle.populateContentView(mRebuildNotification);
3242                }
3243            }
3244            extras.remove(EXTRA_REBUILD_CONTENT_VIEW);
3245
3246            if (extras.getBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW)) {
3247                setBuilderBigContentView(mRebuildNotification, makeBigContentView());
3248                if (mStyle != null) {
3249                    mStyle.populateBigContentView(mRebuildNotification);
3250                }
3251            }
3252            extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW);
3253
3254            if (extras.getBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW)) {
3255                setBuilderHeadsUpContentView(mRebuildNotification, makeHeadsUpContentView());
3256                if (mStyle != null) {
3257                    mStyle.populateHeadsUpContentView(mRebuildNotification);
3258                }
3259            }
3260            extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
3261
3262            mHasThreeLines = false;
3263            return mRebuildNotification;
3264        }
3265
3266        private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3267            Class<? extends Style>[] classes = new Class[]{
3268                    BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3269            for (Class<? extends Style> innerClass : classes) {
3270                if (templateClass.equals(innerClass.getName())) {
3271                    return innerClass;
3272                }
3273            }
3274            return null;
3275        }
3276
3277        private void setBuilderContentView(Notification n, RemoteViews contentView) {
3278            n.contentView = contentView;
3279            if (contentView instanceof BuilderRemoteViews) {
3280                mRebuildBundle.putInt(Builder.EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
3281                        contentView.getSequenceNumber());
3282            }
3283        }
3284
3285        private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3286            n.bigContentView = bigContentView;
3287            if (bigContentView instanceof BuilderRemoteViews) {
3288                mRebuildBundle.putInt(Builder.EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
3289                        bigContentView.getSequenceNumber());
3290            }
3291        }
3292
3293        private void setBuilderHeadsUpContentView(Notification n,
3294                RemoteViews headsUpContentView) {
3295            n.headsUpContentView = headsUpContentView;
3296            if (headsUpContentView instanceof BuilderRemoteViews) {
3297                mRebuildBundle.putInt(Builder.EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
3298                        headsUpContentView.getSequenceNumber());
3299            }
3300        }
3301
3302        private void restoreFromNotification(Notification n) {
3303
3304            // Notification fields.
3305            mWhen = n.when;
3306            mSmallIcon = n.icon;
3307            mSmallIconLevel = n.iconLevel;
3308            mNumber = n.number;
3309
3310            mColor = n.color;
3311
3312            mContentView = n.contentView;
3313            mDeleteIntent = n.deleteIntent;
3314            mFullScreenIntent = n.fullScreenIntent;
3315            mTickerText = n.tickerText;
3316            mTickerView = n.tickerView;
3317            mLargeIcon = n.largeIcon;
3318            mSound = n.sound;
3319            mAudioStreamType = n.audioStreamType;
3320            mAudioAttributes = n.audioAttributes;
3321
3322            mVibrate = n.vibrate;
3323            mLedArgb = n.ledARGB;
3324            mLedOnMs = n.ledOnMS;
3325            mLedOffMs = n.ledOffMS;
3326            mDefaults = n.defaults;
3327            mFlags = n.flags;
3328
3329            mCategory = n.category;
3330            mGroupKey = n.mGroupKey;
3331            mSortKey = n.mSortKey;
3332            mPriority = n.priority;
3333            mActions.clear();
3334            if (n.actions != null) {
3335                Collections.addAll(mActions, n.actions);
3336            }
3337            mVisibility = n.visibility;
3338
3339            mPublicVersion = n.publicVersion;
3340
3341            // Extras.
3342            Bundle extras = n.extras;
3343            mOriginatingUserId = extras.getInt(EXTRA_ORIGINATING_USERID);
3344            mContentTitle = extras.getCharSequence(EXTRA_TITLE);
3345            mContentText = extras.getCharSequence(EXTRA_TEXT);
3346            mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
3347            mContentInfo = extras.getCharSequence(EXTRA_INFO_TEXT);
3348            mSmallIcon = extras.getInt(EXTRA_SMALL_ICON);
3349            mProgress = extras.getInt(EXTRA_PROGRESS);
3350            mProgressMax = extras.getInt(EXTRA_PROGRESS_MAX);
3351            mProgressIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3352            mUseChronometer = extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
3353            mShowWhen = extras.getBoolean(EXTRA_SHOW_WHEN);
3354            if (extras.containsKey(EXTRA_LARGE_ICON)) {
3355                mLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
3356            }
3357            if (extras.containsKey(EXTRA_PEOPLE)) {
3358                mPeople.clear();
3359                Collections.addAll(mPeople, extras.getStringArray(EXTRA_PEOPLE));
3360            }
3361        }
3362
3363        /**
3364         * @deprecated Use {@link #build()} instead.
3365         */
3366        @Deprecated
3367        public Notification getNotification() {
3368            return build();
3369        }
3370
3371        /**
3372         * Combine all of the options that have been set and return a new {@link Notification}
3373         * object.
3374         */
3375        public Notification build() {
3376            mOriginatingUserId = mContext.getUserId();
3377            mHasThreeLines = hasThreeLines();
3378
3379            Notification n = buildUnstyled();
3380
3381            if (mStyle != null) {
3382                n = mStyle.buildStyled(n);
3383            }
3384
3385            if (mExtras != null) {
3386                n.extras.putAll(mExtras);
3387            }
3388
3389            if (mRebuildBundle.size() > 0) {
3390                n.extras.putAll(mRebuildBundle);
3391                mRebuildBundle.clear();
3392            }
3393
3394            populateExtras(n.extras);
3395            if (mStyle != null) {
3396                mStyle.addExtras(n.extras);
3397            }
3398
3399            mHasThreeLines = false;
3400            return n;
3401        }
3402
3403        /**
3404         * Apply this Builder to an existing {@link Notification} object.
3405         *
3406         * @hide
3407         */
3408        public Notification buildInto(Notification n) {
3409            build().cloneInto(n, true);
3410            return n;
3411        }
3412
3413        private int getBaseLayoutResource() {
3414            return R.layout.notification_template_material_base;
3415        }
3416
3417        private int getBigBaseLayoutResource() {
3418            return R.layout.notification_template_material_big_base;
3419        }
3420
3421        private int getBigPictureLayoutResource() {
3422            return R.layout.notification_template_material_big_picture;
3423        }
3424
3425        private int getBigTextLayoutResource() {
3426            return R.layout.notification_template_material_big_text;
3427        }
3428
3429        private int getInboxLayoutResource() {
3430            return R.layout.notification_template_material_inbox;
3431        }
3432
3433        private int getActionLayoutResource() {
3434            return R.layout.notification_material_action;
3435        }
3436
3437        private int getActionTombstoneLayoutResource() {
3438            return R.layout.notification_material_action_tombstone;
3439        }
3440    }
3441
3442    /**
3443     * An object that can apply a rich notification style to a {@link Notification.Builder}
3444     * object.
3445     */
3446    public static abstract class Style {
3447        private CharSequence mBigContentTitle;
3448
3449        /**
3450         * @hide
3451         */
3452        protected CharSequence mSummaryText = null;
3453
3454        /**
3455         * @hide
3456         */
3457        protected boolean mSummaryTextSet = false;
3458
3459        protected Builder mBuilder;
3460
3461        /**
3462         * Overrides ContentTitle in the big form of the template.
3463         * This defaults to the value passed to setContentTitle().
3464         */
3465        protected void internalSetBigContentTitle(CharSequence title) {
3466            mBigContentTitle = title;
3467        }
3468
3469        /**
3470         * Set the first line of text after the detail section in the big form of the template.
3471         */
3472        protected void internalSetSummaryText(CharSequence cs) {
3473            mSummaryText = cs;
3474            mSummaryTextSet = true;
3475        }
3476
3477        public void setBuilder(Builder builder) {
3478            if (mBuilder != builder) {
3479                mBuilder = builder;
3480                if (mBuilder != null) {
3481                    mBuilder.setStyle(this);
3482                }
3483            }
3484        }
3485
3486        protected void checkBuilder() {
3487            if (mBuilder == null) {
3488                throw new IllegalArgumentException("Style requires a valid Builder object");
3489            }
3490        }
3491
3492        protected RemoteViews getStandardView(int layoutId) {
3493            checkBuilder();
3494
3495            // Nasty.
3496            CharSequence oldBuilderContentTitle = mBuilder.mContentTitle;
3497            if (mBigContentTitle != null) {
3498                mBuilder.setContentTitle(mBigContentTitle);
3499            }
3500
3501            RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3502
3503            mBuilder.mContentTitle = oldBuilderContentTitle;
3504
3505            if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3506                contentView.setViewVisibility(R.id.line1, View.GONE);
3507            } else {
3508                contentView.setViewVisibility(R.id.line1, View.VISIBLE);
3509            }
3510
3511            // The last line defaults to the subtext, but can be replaced by mSummaryText
3512            final CharSequence overflowText =
3513                    mSummaryTextSet ? mSummaryText
3514                                    : mBuilder.mSubText;
3515            if (overflowText != null) {
3516                contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
3517                contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
3518                contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3519            } else {
3520                // Clear text in case we use the line to show the profile badge.
3521                contentView.setTextViewText(R.id.text, "");
3522                contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3523                contentView.setViewVisibility(R.id.line3, View.GONE);
3524            }
3525
3526            return contentView;
3527        }
3528
3529        /**
3530         * Changes the padding of the first line such that the big and small content view have the
3531         * same top padding.
3532         *
3533         * @hide
3534         */
3535        protected void applyTopPadding(RemoteViews contentView) {
3536            int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
3537                    mBuilder.mHasThreeLines,
3538                    mBuilder.mContext.getResources().getConfiguration().fontScale);
3539            contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
3540        }
3541
3542        /**
3543         * @hide
3544         */
3545        public void addExtras(Bundle extras) {
3546            if (mSummaryTextSet) {
3547                extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3548            }
3549            if (mBigContentTitle != null) {
3550                extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3551            }
3552            extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
3553        }
3554
3555        /**
3556         * @hide
3557         */
3558        protected void restoreFromExtras(Bundle extras) {
3559            if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3560                mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3561                mSummaryTextSet = true;
3562            }
3563            if (extras.containsKey(EXTRA_TITLE_BIG)) {
3564                mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3565            }
3566        }
3567
3568
3569        /**
3570         * @hide
3571         */
3572        public Notification buildStyled(Notification wip) {
3573            populateTickerView(wip);
3574            populateContentView(wip);
3575            populateBigContentView(wip);
3576            populateHeadsUpContentView(wip);
3577            return wip;
3578        }
3579
3580        // The following methods are split out so we can re-create notification partially.
3581        /**
3582         * @hide
3583         */
3584        protected void populateTickerView(Notification wip) {}
3585        /**
3586         * @hide
3587         */
3588        protected void populateContentView(Notification wip) {}
3589
3590        /**
3591         * @hide
3592         */
3593        protected void populateBigContentView(Notification wip) {}
3594
3595        /**
3596         * @hide
3597         */
3598        protected void populateHeadsUpContentView(Notification wip) {}
3599
3600        /**
3601         * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3602         * attached to.
3603         *
3604         * @return the fully constructed Notification.
3605         */
3606        public Notification build() {
3607            checkBuilder();
3608            return mBuilder.build();
3609        }
3610
3611        /**
3612         * @hide
3613         * @return true if the style positions the progress bar on the second line; false if the
3614         *         style hides the progress bar
3615         */
3616        protected boolean hasProgress() {
3617            return true;
3618        }
3619    }
3620
3621    /**
3622     * Helper class for generating large-format notifications that include a large image attachment.
3623     *
3624     * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
3625     * <pre class="prettyprint">
3626     * Notification notif = new Notification.Builder(mContext)
3627     *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
3628     *     .setContentText(subject)
3629     *     .setSmallIcon(R.drawable.new_post)
3630     *     .setLargeIcon(aBitmap)
3631     *     .setStyle(new Notification.BigPictureStyle()
3632     *         .bigPicture(aBigBitmap))
3633     *     .build();
3634     * </pre>
3635     *
3636     * @see Notification#bigContentView
3637     */
3638    public static class BigPictureStyle extends Style {
3639        private Bitmap mPicture;
3640        private Bitmap mBigLargeIcon;
3641        private boolean mBigLargeIconSet = false;
3642
3643        public BigPictureStyle() {
3644        }
3645
3646        public BigPictureStyle(Builder builder) {
3647            setBuilder(builder);
3648        }
3649
3650        /**
3651         * Overrides ContentTitle in the big form of the template.
3652         * This defaults to the value passed to setContentTitle().
3653         */
3654        public BigPictureStyle setBigContentTitle(CharSequence title) {
3655            internalSetBigContentTitle(safeCharSequence(title));
3656            return this;
3657        }
3658
3659        /**
3660         * Set the first line of text after the detail section in the big form of the template.
3661         */
3662        public BigPictureStyle setSummaryText(CharSequence cs) {
3663            internalSetSummaryText(safeCharSequence(cs));
3664            return this;
3665        }
3666
3667        /**
3668         * Provide the bitmap to be used as the payload for the BigPicture notification.
3669         */
3670        public BigPictureStyle bigPicture(Bitmap b) {
3671            mPicture = b;
3672            return this;
3673        }
3674
3675        /**
3676         * Override the large icon when the big notification is shown.
3677         */
3678        public BigPictureStyle bigLargeIcon(Bitmap b) {
3679            mBigLargeIconSet = true;
3680            mBigLargeIcon = b;
3681            return this;
3682        }
3683
3684        private RemoteViews makeBigContentView() {
3685            RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
3686
3687            contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3688
3689            applyTopPadding(contentView);
3690
3691            boolean twoTextLines = mBuilder.mSubText != null && mBuilder.mContentText != null;
3692            mBuilder.addProfileBadge(contentView,
3693                    twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
3694            return contentView;
3695        }
3696
3697        /**
3698         * @hide
3699         */
3700        public void addExtras(Bundle extras) {
3701            super.addExtras(extras);
3702
3703            if (mBigLargeIconSet) {
3704                extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3705            }
3706            extras.putParcelable(EXTRA_PICTURE, mPicture);
3707        }
3708
3709        /**
3710         * @hide
3711         */
3712        @Override
3713        protected void restoreFromExtras(Bundle extras) {
3714            super.restoreFromExtras(extras);
3715
3716            if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
3717                mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
3718            }
3719            mPicture = extras.getParcelable(EXTRA_PICTURE);
3720        }
3721
3722        /**
3723         * @hide
3724         */
3725        @Override
3726        public void populateBigContentView(Notification wip) {
3727            mBuilder.setBuilderBigContentView(wip, makeBigContentView());
3728        }
3729    }
3730
3731    /**
3732     * Helper class for generating large-format notifications that include a lot of text.
3733     *
3734     * Here's how you'd set the <code>BigTextStyle</code> on a notification:
3735     * <pre class="prettyprint">
3736     * Notification notif = new Notification.Builder(mContext)
3737     *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
3738     *     .setContentText(subject)
3739     *     .setSmallIcon(R.drawable.new_mail)
3740     *     .setLargeIcon(aBitmap)
3741     *     .setStyle(new Notification.BigTextStyle()
3742     *         .bigText(aVeryLongString))
3743     *     .build();
3744     * </pre>
3745     *
3746     * @see Notification#bigContentView
3747     */
3748    public static class BigTextStyle extends Style {
3749
3750        private static final int MAX_LINES = 13;
3751        private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3752        private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3753
3754        private CharSequence mBigText;
3755
3756        public BigTextStyle() {
3757        }
3758
3759        public BigTextStyle(Builder builder) {
3760            setBuilder(builder);
3761        }
3762
3763        /**
3764         * Overrides ContentTitle in the big form of the template.
3765         * This defaults to the value passed to setContentTitle().
3766         */
3767        public BigTextStyle setBigContentTitle(CharSequence title) {
3768            internalSetBigContentTitle(safeCharSequence(title));
3769            return this;
3770        }
3771
3772        /**
3773         * Set the first line of text after the detail section in the big form of the template.
3774         */
3775        public BigTextStyle setSummaryText(CharSequence cs) {
3776            internalSetSummaryText(safeCharSequence(cs));
3777            return this;
3778        }
3779
3780        /**
3781         * Provide the longer text to be displayed in the big form of the
3782         * template in place of the content text.
3783         */
3784        public BigTextStyle bigText(CharSequence cs) {
3785            mBigText = safeCharSequence(cs);
3786            return this;
3787        }
3788
3789        /**
3790         * @hide
3791         */
3792        public void addExtras(Bundle extras) {
3793            super.addExtras(extras);
3794
3795            extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3796        }
3797
3798        /**
3799         * @hide
3800         */
3801        @Override
3802        protected void restoreFromExtras(Bundle extras) {
3803            super.restoreFromExtras(extras);
3804
3805            mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
3806        }
3807
3808        private RemoteViews makeBigContentView() {
3809
3810            // Nasty
3811            CharSequence oldBuilderContentText = mBuilder.mContentText;
3812            mBuilder.mContentText = null;
3813
3814            RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
3815
3816            mBuilder.mContentText = oldBuilderContentText;
3817
3818            contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
3819            contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
3820            contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
3821            contentView.setViewVisibility(R.id.text2, View.GONE);
3822
3823            applyTopPadding(contentView);
3824
3825            mBuilder.shrinkLine3Text(contentView);
3826
3827            mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3828
3829            return contentView;
3830        }
3831
3832        private int calculateMaxLines() {
3833            int lineCount = MAX_LINES;
3834            boolean hasActions = mBuilder.mActions.size() > 0;
3835            boolean hasSummary = (mSummaryTextSet ? mSummaryText : mBuilder.mSubText) != null;
3836            if (hasActions) {
3837                lineCount -= LINES_CONSUMED_BY_ACTIONS;
3838            }
3839            if (hasSummary) {
3840                lineCount -= LINES_CONSUMED_BY_SUMMARY;
3841            }
3842
3843            // If we have less top padding at the top, we can fit less lines.
3844            if (!mBuilder.mHasThreeLines) {
3845                lineCount--;
3846            }
3847            return lineCount;
3848        }
3849
3850        /**
3851         * @hide
3852         */
3853        @Override
3854        public void populateBigContentView(Notification wip) {
3855            mBuilder.setBuilderBigContentView(wip, makeBigContentView());
3856        }
3857    }
3858
3859    /**
3860     * Helper class for generating large-format notifications that include a list of (up to 5) strings.
3861     *
3862     * Here's how you'd set the <code>InboxStyle</code> on a notification:
3863     * <pre class="prettyprint">
3864     * Notification notif = new Notification.Builder(mContext)
3865     *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
3866     *     .setContentText(subject)
3867     *     .setSmallIcon(R.drawable.new_mail)
3868     *     .setLargeIcon(aBitmap)
3869     *     .setStyle(new Notification.InboxStyle()
3870     *         .addLine(str1)
3871     *         .addLine(str2)
3872     *         .setContentTitle(&quot;&quot;)
3873     *         .setSummaryText(&quot;+3 more&quot;))
3874     *     .build();
3875     * </pre>
3876     *
3877     * @see Notification#bigContentView
3878     */
3879    public static class InboxStyle extends Style {
3880        private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
3881
3882        public InboxStyle() {
3883        }
3884
3885        public InboxStyle(Builder builder) {
3886            setBuilder(builder);
3887        }
3888
3889        /**
3890         * Overrides ContentTitle in the big form of the template.
3891         * This defaults to the value passed to setContentTitle().
3892         */
3893        public InboxStyle setBigContentTitle(CharSequence title) {
3894            internalSetBigContentTitle(safeCharSequence(title));
3895            return this;
3896        }
3897
3898        /**
3899         * Set the first line of text after the detail section in the big form of the template.
3900         */
3901        public InboxStyle setSummaryText(CharSequence cs) {
3902            internalSetSummaryText(safeCharSequence(cs));
3903            return this;
3904        }
3905
3906        /**
3907         * Append a line to the digest section of the Inbox notification.
3908         */
3909        public InboxStyle addLine(CharSequence cs) {
3910            mTexts.add(safeCharSequence(cs));
3911            return this;
3912        }
3913
3914        /**
3915         * @hide
3916         */
3917        public void addExtras(Bundle extras) {
3918            super.addExtras(extras);
3919
3920            CharSequence[] a = new CharSequence[mTexts.size()];
3921            extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
3922        }
3923
3924        /**
3925         * @hide
3926         */
3927        @Override
3928        protected void restoreFromExtras(Bundle extras) {
3929            super.restoreFromExtras(extras);
3930
3931            mTexts.clear();
3932            if (extras.containsKey(EXTRA_TEXT_LINES)) {
3933                Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
3934            }
3935        }
3936
3937        private RemoteViews makeBigContentView() {
3938            // Remove the content text so line3 disappears unless you have a summary
3939
3940            // Nasty
3941            CharSequence oldBuilderContentText = mBuilder.mContentText;
3942            mBuilder.mContentText = null;
3943
3944            RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
3945
3946            mBuilder.mContentText = oldBuilderContentText;
3947
3948            contentView.setViewVisibility(R.id.text2, View.GONE);
3949
3950            int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
3951                    R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
3952
3953            // Make sure all rows are gone in case we reuse a view.
3954            for (int rowId : rowIds) {
3955                contentView.setViewVisibility(rowId, View.GONE);
3956            }
3957
3958            final boolean largeText =
3959                    mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
3960            final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
3961                    R.dimen.notification_subtext_size);
3962            int i=0;
3963            while (i < mTexts.size() && i < rowIds.length) {
3964                CharSequence str = mTexts.get(i);
3965                if (str != null && !str.equals("")) {
3966                    contentView.setViewVisibility(rowIds[i], View.VISIBLE);
3967                    contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
3968                    if (largeText) {
3969                        contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
3970                                subTextSize);
3971                    }
3972                }
3973                i++;
3974            }
3975
3976            contentView.setViewVisibility(R.id.inbox_end_pad,
3977                    mTexts.size() > 0 ? View.VISIBLE : View.GONE);
3978
3979            contentView.setViewVisibility(R.id.inbox_more,
3980                    mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
3981
3982            applyTopPadding(contentView);
3983
3984            mBuilder.shrinkLine3Text(contentView);
3985
3986            mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3987
3988            return contentView;
3989        }
3990
3991        /**
3992         * @hide
3993         */
3994        @Override
3995        public void populateBigContentView(Notification wip) {
3996            mBuilder.setBuilderBigContentView(wip, makeBigContentView());
3997        }
3998    }
3999
4000    /**
4001     * Notification style for media playback notifications.
4002     *
4003     * In the expanded form, {@link Notification#bigContentView}, up to 5
4004     * {@link Notification.Action}s specified with
4005     * {@link Notification.Builder#addAction(int, CharSequence, PendingIntent) addAction} will be
4006     * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4007     * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4008     * treated as album artwork.
4009     *
4010     * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4011     * {@link Notification#contentView}; by providing action indices to
4012     * {@link #setShowActionsInCompactView(int...)} you can promote up to 2 actions to be displayed
4013     * in the standard view alongside the usual content.
4014     *
4015     * Notifications created with MediaStyle will have their category set to
4016     * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4017     * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4018     *
4019     * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4020     * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
4021     * the System UI can identify this as a notification representing an active media session
4022     * and respond accordingly (by showing album artwork in the lockscreen, for example).
4023     *
4024     * To use this style with your Notification, feed it to
4025     * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4026     * <pre class="prettyprint">
4027     * Notification noti = new Notification.Builder()
4028     *     .setSmallIcon(R.drawable.ic_stat_player)
4029     *     .setContentTitle(&quot;Track title&quot;)     // these three lines are optional
4030     *     .setContentText(&quot;Artist - Album&quot;)   // if you use
4031     *     .setLargeIcon(albumArtBitmap))      // setMediaSession(token)
4032     *     .setStyle(<b>new Notification.MediaStyle()</b>
4033     *         .setMediaSession(mySession))
4034     *     .build();
4035     * </pre>
4036     *
4037     * @see Notification#bigContentView
4038     */
4039    public static class MediaStyle extends Style {
4040        static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
4041        static final int MAX_MEDIA_BUTTONS = 5;
4042
4043        private int[] mActionsToShowInCompact = null;
4044        private MediaSession.Token mToken;
4045
4046        public MediaStyle() {
4047        }
4048
4049        public MediaStyle(Builder builder) {
4050            setBuilder(builder);
4051        }
4052
4053        /**
4054         * Request up to 3 actions (by index in the order of addition) to be shown in the compact
4055         * notification view.
4056         *
4057         * @param actions the indices of the actions to show in the compact notification view
4058         */
4059        public MediaStyle setShowActionsInCompactView(int...actions) {
4060            mActionsToShowInCompact = actions;
4061            return this;
4062        }
4063
4064        /**
4065         * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4066         * to provide additional playback information and control to the SystemUI.
4067         */
4068        public MediaStyle setMediaSession(MediaSession.Token token) {
4069            mToken = token;
4070            return this;
4071        }
4072
4073        /**
4074         * @hide
4075         */
4076        @Override
4077        public Notification buildStyled(Notification wip) {
4078            super.buildStyled(wip);
4079            if (wip.category == null) {
4080                wip.category = Notification.CATEGORY_TRANSPORT;
4081            }
4082            return wip;
4083        }
4084
4085        /**
4086         * @hide
4087         */
4088        @Override
4089        public void populateContentView(Notification wip) {
4090            mBuilder.setBuilderContentView(wip, makeMediaContentView());
4091        }
4092
4093        /**
4094         * @hide
4095         */
4096        @Override
4097        public void populateBigContentView(Notification wip) {
4098            mBuilder.setBuilderBigContentView(wip, makeMediaBigContentView());
4099        }
4100
4101        /** @hide */
4102        @Override
4103        public void addExtras(Bundle extras) {
4104            super.addExtras(extras);
4105
4106            if (mToken != null) {
4107                extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4108            }
4109            if (mActionsToShowInCompact != null) {
4110                extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4111            }
4112        }
4113
4114        /**
4115         * @hide
4116         */
4117        @Override
4118        protected void restoreFromExtras(Bundle extras) {
4119            super.restoreFromExtras(extras);
4120
4121            if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4122                mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4123            }
4124            if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4125                mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4126            }
4127        }
4128
4129        private RemoteViews generateMediaActionButton(Action action) {
4130            final boolean tombstone = (action.actionIntent == null);
4131            RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
4132                    R.layout.notification_material_media_action);
4133            button.setImageViewResource(R.id.action0, action.icon);
4134            button.setDrawableParameters(R.id.action0, false, -1,
4135                    0xFFFFFFFF,
4136                    PorterDuff.Mode.SRC_ATOP, -1);
4137            if (!tombstone) {
4138                button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4139            }
4140            button.setContentDescription(R.id.action0, action.title);
4141            return button;
4142        }
4143
4144        private RemoteViews makeMediaContentView() {
4145            RemoteViews view = mBuilder.applyStandardTemplate(
4146                    R.layout.notification_template_material_media, false /* hasProgress */);
4147
4148            final int numActions = mBuilder.mActions.size();
4149            final int N = mActionsToShowInCompact == null
4150                    ? 0
4151                    : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4152            if (N > 0) {
4153                view.removeAllViews(com.android.internal.R.id.media_actions);
4154                for (int i = 0; i < N; i++) {
4155                    if (i >= numActions) {
4156                        throw new IllegalArgumentException(String.format(
4157                                "setShowActionsInCompactView: action %d out of bounds (max %d)",
4158                                i, numActions - 1));
4159                    }
4160
4161                    final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
4162                    final RemoteViews button = generateMediaActionButton(action);
4163                    view.addView(com.android.internal.R.id.media_actions, button);
4164                }
4165            }
4166            styleText(view);
4167            hideRightIcon(view);
4168            return view;
4169        }
4170
4171        private RemoteViews makeMediaBigContentView() {
4172            final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
4173            RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount),
4174                    false /* hasProgress */);
4175
4176            if (actionCount > 0) {
4177                big.removeAllViews(com.android.internal.R.id.media_actions);
4178                for (int i = 0; i < actionCount; i++) {
4179                    final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
4180                    big.addView(com.android.internal.R.id.media_actions, button);
4181                }
4182            }
4183            styleText(big);
4184            hideRightIcon(big);
4185            applyTopPadding(big);
4186            big.setViewVisibility(android.R.id.progress, View.GONE);
4187            return big;
4188        }
4189
4190        private int getBigLayoutResource(int actionCount) {
4191            if (actionCount <= 3) {
4192                return R.layout.notification_template_material_big_media_narrow;
4193            } else {
4194                return R.layout.notification_template_material_big_media;
4195            }
4196        }
4197
4198        private void hideRightIcon(RemoteViews contentView) {
4199            contentView.setViewVisibility(R.id.right_icon, View.GONE);
4200        }
4201
4202        /**
4203         * Applies the special text colors for media notifications to all text views.
4204         */
4205        private void styleText(RemoteViews contentView) {
4206            int primaryColor = mBuilder.mContext.getResources().getColor(
4207                    R.color.notification_media_primary_color);
4208            int secondaryColor = mBuilder.mContext.getResources().getColor(
4209                    R.color.notification_media_secondary_color);
4210            contentView.setTextColor(R.id.title, primaryColor);
4211            if (mBuilder.showsTimeOrChronometer()) {
4212                if (mBuilder.mUseChronometer) {
4213                    contentView.setTextColor(R.id.chronometer, secondaryColor);
4214                } else {
4215                    contentView.setTextColor(R.id.time, secondaryColor);
4216                }
4217            }
4218            contentView.setTextColor(R.id.text2, secondaryColor);
4219            contentView.setTextColor(R.id.text, secondaryColor);
4220            contentView.setTextColor(R.id.info, secondaryColor);
4221        }
4222
4223        /**
4224         * @hide
4225         */
4226        @Override
4227        protected boolean hasProgress() {
4228            return false;
4229        }
4230    }
4231
4232    // When adding a new Style subclass here, don't forget to update
4233    // Builder.getNotificationStyleClass.
4234
4235    /**
4236     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4237     * metadata or change options on a notification builder.
4238     */
4239    public interface Extender {
4240        /**
4241         * Apply this extender to a notification builder.
4242         * @param builder the builder to be modified.
4243         * @return the build object for chaining.
4244         */
4245        public Builder extend(Builder builder);
4246    }
4247
4248    /**
4249     * Helper class to add wearable extensions to notifications.
4250     * <p class="note"> See
4251     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4252     * for Android Wear</a> for more information on how to use this class.
4253     * <p>
4254     * To create a notification with wearable extensions:
4255     * <ol>
4256     *   <li>Create a {@link android.app.Notification.Builder}, setting any desired
4257     *   properties.
4258     *   <li>Create a {@link android.app.Notification.WearableExtender}.
4259     *   <li>Set wearable-specific properties using the
4260     *   {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4261     *   <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4262     *   notification.
4263     *   <li>Post the notification to the notification system with the
4264     *   {@code NotificationManager.notify(...)} methods.
4265     * </ol>
4266     *
4267     * <pre class="prettyprint">
4268     * Notification notif = new Notification.Builder(mContext)
4269     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
4270     *         .setContentText(subject)
4271     *         .setSmallIcon(R.drawable.new_mail)
4272     *         .extend(new Notification.WearableExtender()
4273     *                 .setContentIcon(R.drawable.new_mail))
4274     *         .build();
4275     * NotificationManager notificationManger =
4276     *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4277     * notificationManger.notify(0, notif);</pre>
4278     *
4279     * <p>Wearable extensions can be accessed on an existing notification by using the
4280     * {@code WearableExtender(Notification)} constructor,
4281     * and then using the {@code get} methods to access values.
4282     *
4283     * <pre class="prettyprint">
4284     * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4285     *         notification);
4286     * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
4287     */
4288    public static final class WearableExtender implements Extender {
4289        /**
4290         * Sentinel value for an action index that is unset.
4291         */
4292        public static final int UNSET_ACTION_INDEX = -1;
4293
4294        /**
4295         * Size value for use with {@link #setCustomSizePreset} to show this notification with
4296         * default sizing.
4297         * <p>For custom display notifications created using {@link #setDisplayIntent},
4298         * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
4299         * on their content.
4300         */
4301        public static final int SIZE_DEFAULT = 0;
4302
4303        /**
4304         * Size value for use with {@link #setCustomSizePreset} to show this notification
4305         * with an extra small size.
4306         * <p>This value is only applicable for custom display notifications created using
4307         * {@link #setDisplayIntent}.
4308         */
4309        public static final int SIZE_XSMALL = 1;
4310
4311        /**
4312         * Size value for use with {@link #setCustomSizePreset} to show this notification
4313         * with a small size.
4314         * <p>This value is only applicable for custom display notifications created using
4315         * {@link #setDisplayIntent}.
4316         */
4317        public static final int SIZE_SMALL = 2;
4318
4319        /**
4320         * Size value for use with {@link #setCustomSizePreset} to show this notification
4321         * with a medium size.
4322         * <p>This value is only applicable for custom display notifications created using
4323         * {@link #setDisplayIntent}.
4324         */
4325        public static final int SIZE_MEDIUM = 3;
4326
4327        /**
4328         * Size value for use with {@link #setCustomSizePreset} to show this notification
4329         * with a large size.
4330         * <p>This value is only applicable for custom display notifications created using
4331         * {@link #setDisplayIntent}.
4332         */
4333        public static final int SIZE_LARGE = 4;
4334
4335        /**
4336         * Size value for use with {@link #setCustomSizePreset} to show this notification
4337         * full screen.
4338         * <p>This value is only applicable for custom display notifications created using
4339         * {@link #setDisplayIntent}.
4340         */
4341        public static final int SIZE_FULL_SCREEN = 5;
4342
4343        /** Notification extra which contains wearable extensions */
4344        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4345
4346        // Keys within EXTRA_WEARABLE_OPTIONS for wearable options.
4347        private static final String KEY_ACTIONS = "actions";
4348        private static final String KEY_FLAGS = "flags";
4349        private static final String KEY_DISPLAY_INTENT = "displayIntent";
4350        private static final String KEY_PAGES = "pages";
4351        private static final String KEY_BACKGROUND = "background";
4352        private static final String KEY_CONTENT_ICON = "contentIcon";
4353        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4354        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4355        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4356        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4357        private static final String KEY_GRAVITY = "gravity";
4358
4359        // Flags bitwise-ored to mFlags
4360        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4361        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4362        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4363        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
4364
4365        // Default value for flags integer
4366        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4367
4368        private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4369        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4370
4371        private ArrayList<Action> mActions = new ArrayList<Action>();
4372        private int mFlags = DEFAULT_FLAGS;
4373        private PendingIntent mDisplayIntent;
4374        private ArrayList<Notification> mPages = new ArrayList<Notification>();
4375        private Bitmap mBackground;
4376        private int mContentIcon;
4377        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4378        private int mContentActionIndex = UNSET_ACTION_INDEX;
4379        private int mCustomSizePreset = SIZE_DEFAULT;
4380        private int mCustomContentHeight;
4381        private int mGravity = DEFAULT_GRAVITY;
4382
4383        /**
4384         * Create a {@link android.app.Notification.WearableExtender} with default
4385         * options.
4386         */
4387        public WearableExtender() {
4388        }
4389
4390        public WearableExtender(Notification notif) {
4391            Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4392            if (wearableBundle != null) {
4393                List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4394                if (actions != null) {
4395                    mActions.addAll(actions);
4396                }
4397
4398                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4399                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4400
4401                Notification[] pages = getNotificationArrayFromBundle(
4402                        wearableBundle, KEY_PAGES);
4403                if (pages != null) {
4404                    Collections.addAll(mPages, pages);
4405                }
4406
4407                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4408                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4409                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4410                        DEFAULT_CONTENT_ICON_GRAVITY);
4411                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4412                        UNSET_ACTION_INDEX);
4413                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4414                        SIZE_DEFAULT);
4415                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4416                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
4417            }
4418        }
4419
4420        /**
4421         * Apply wearable extensions to a notification that is being built. This is typically
4422         * called by the {@link android.app.Notification.Builder#extend} method of
4423         * {@link android.app.Notification.Builder}.
4424         */
4425        @Override
4426        public Notification.Builder extend(Notification.Builder builder) {
4427            Bundle wearableBundle = new Bundle();
4428
4429            if (!mActions.isEmpty()) {
4430                wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4431            }
4432            if (mFlags != DEFAULT_FLAGS) {
4433                wearableBundle.putInt(KEY_FLAGS, mFlags);
4434            }
4435            if (mDisplayIntent != null) {
4436                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4437            }
4438            if (!mPages.isEmpty()) {
4439                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4440                        new Notification[mPages.size()]));
4441            }
4442            if (mBackground != null) {
4443                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4444            }
4445            if (mContentIcon != 0) {
4446                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4447            }
4448            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4449                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4450            }
4451            if (mContentActionIndex != UNSET_ACTION_INDEX) {
4452                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4453                        mContentActionIndex);
4454            }
4455            if (mCustomSizePreset != SIZE_DEFAULT) {
4456                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4457            }
4458            if (mCustomContentHeight != 0) {
4459                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4460            }
4461            if (mGravity != DEFAULT_GRAVITY) {
4462                wearableBundle.putInt(KEY_GRAVITY, mGravity);
4463            }
4464
4465            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4466            return builder;
4467        }
4468
4469        @Override
4470        public WearableExtender clone() {
4471            WearableExtender that = new WearableExtender();
4472            that.mActions = new ArrayList<Action>(this.mActions);
4473            that.mFlags = this.mFlags;
4474            that.mDisplayIntent = this.mDisplayIntent;
4475            that.mPages = new ArrayList<Notification>(this.mPages);
4476            that.mBackground = this.mBackground;
4477            that.mContentIcon = this.mContentIcon;
4478            that.mContentIconGravity = this.mContentIconGravity;
4479            that.mContentActionIndex = this.mContentActionIndex;
4480            that.mCustomSizePreset = this.mCustomSizePreset;
4481            that.mCustomContentHeight = this.mCustomContentHeight;
4482            that.mGravity = this.mGravity;
4483            return that;
4484        }
4485
4486        /**
4487         * Add a wearable action to this notification.
4488         *
4489         * <p>When wearable actions are added using this method, the set of actions that
4490         * show on a wearable device splits from devices that only show actions added
4491         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4492         * of which actions display on different devices.
4493         *
4494         * @param action the action to add to this notification
4495         * @return this object for method chaining
4496         * @see android.app.Notification.Action
4497         */
4498        public WearableExtender addAction(Action action) {
4499            mActions.add(action);
4500            return this;
4501        }
4502
4503        /**
4504         * Adds wearable actions to this notification.
4505         *
4506         * <p>When wearable actions are added using this method, the set of actions that
4507         * show on a wearable device splits from devices that only show actions added
4508         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4509         * of which actions display on different devices.
4510         *
4511         * @param actions the actions to add to this notification
4512         * @return this object for method chaining
4513         * @see android.app.Notification.Action
4514         */
4515        public WearableExtender addActions(List<Action> actions) {
4516            mActions.addAll(actions);
4517            return this;
4518        }
4519
4520        /**
4521         * Clear all wearable actions present on this builder.
4522         * @return this object for method chaining.
4523         * @see #addAction
4524         */
4525        public WearableExtender clearActions() {
4526            mActions.clear();
4527            return this;
4528        }
4529
4530        /**
4531         * Get the wearable actions present on this notification.
4532         */
4533        public List<Action> getActions() {
4534            return mActions;
4535        }
4536
4537        /**
4538         * Set an intent to launch inside of an activity view when displaying
4539         * this notification. The {@link PendingIntent} provided should be for an activity.
4540         *
4541         * <pre class="prettyprint">
4542         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4543         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4544         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4545         * Notification notif = new Notification.Builder(context)
4546         *         .extend(new Notification.WearableExtender()
4547         *                 .setDisplayIntent(displayPendingIntent)
4548         *                 .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4549         *         .build();</pre>
4550         *
4551         * <p>The activity to launch needs to allow embedding, must be exported, and
4552         * should have an empty task affinity. It is also recommended to use the device
4553         * default light theme.
4554         *
4555         * <p>Example AndroidManifest.xml entry:
4556         * <pre class="prettyprint">
4557         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4558         *     android:exported=&quot;true&quot;
4559         *     android:allowEmbedded=&quot;true&quot;
4560         *     android:taskAffinity=&quot;&quot;
4561         *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
4562         *
4563         * @param intent the {@link PendingIntent} for an activity
4564         * @return this object for method chaining
4565         * @see android.app.Notification.WearableExtender#getDisplayIntent
4566         */
4567        public WearableExtender setDisplayIntent(PendingIntent intent) {
4568            mDisplayIntent = intent;
4569            return this;
4570        }
4571
4572        /**
4573         * Get the intent to launch inside of an activity view when displaying this
4574         * notification. This {@code PendingIntent} should be for an activity.
4575         */
4576        public PendingIntent getDisplayIntent() {
4577            return mDisplayIntent;
4578        }
4579
4580        /**
4581         * Add an additional page of content to display with this notification. The current
4582         * notification forms the first page, and pages added using this function form
4583         * subsequent pages. This field can be used to separate a notification into multiple
4584         * sections.
4585         *
4586         * @param page the notification to add as another page
4587         * @return this object for method chaining
4588         * @see android.app.Notification.WearableExtender#getPages
4589         */
4590        public WearableExtender addPage(Notification page) {
4591            mPages.add(page);
4592            return this;
4593        }
4594
4595        /**
4596         * Add additional pages of content to display with this notification. The current
4597         * notification forms the first page, and pages added using this function form
4598         * subsequent pages. This field can be used to separate a notification into multiple
4599         * sections.
4600         *
4601         * @param pages a list of notifications
4602         * @return this object for method chaining
4603         * @see android.app.Notification.WearableExtender#getPages
4604         */
4605        public WearableExtender addPages(List<Notification> pages) {
4606            mPages.addAll(pages);
4607            return this;
4608        }
4609
4610        /**
4611         * Clear all additional pages present on this builder.
4612         * @return this object for method chaining.
4613         * @see #addPage
4614         */
4615        public WearableExtender clearPages() {
4616            mPages.clear();
4617            return this;
4618        }
4619
4620        /**
4621         * Get the array of additional pages of content for displaying this notification. The
4622         * current notification forms the first page, and elements within this array form
4623         * subsequent pages. This field can be used to separate a notification into multiple
4624         * sections.
4625         * @return the pages for this notification
4626         */
4627        public List<Notification> getPages() {
4628            return mPages;
4629        }
4630
4631        /**
4632         * Set a background image to be displayed behind the notification content.
4633         * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4634         * will work with any notification style.
4635         *
4636         * @param background the background bitmap
4637         * @return this object for method chaining
4638         * @see android.app.Notification.WearableExtender#getBackground
4639         */
4640        public WearableExtender setBackground(Bitmap background) {
4641            mBackground = background;
4642            return this;
4643        }
4644
4645        /**
4646         * Get a background image to be displayed behind the notification content.
4647         * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4648         * will work with any notification style.
4649         *
4650         * @return the background image
4651         * @see android.app.Notification.WearableExtender#setBackground
4652         */
4653        public Bitmap getBackground() {
4654            return mBackground;
4655        }
4656
4657        /**
4658         * Set an icon that goes with the content of this notification.
4659         */
4660        public WearableExtender setContentIcon(int icon) {
4661            mContentIcon = icon;
4662            return this;
4663        }
4664
4665        /**
4666         * Get an icon that goes with the content of this notification.
4667         */
4668        public int getContentIcon() {
4669            return mContentIcon;
4670        }
4671
4672        /**
4673         * Set the gravity that the content icon should have within the notification display.
4674         * Supported values include {@link android.view.Gravity#START} and
4675         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4676         * @see #setContentIcon
4677         */
4678        public WearableExtender setContentIconGravity(int contentIconGravity) {
4679            mContentIconGravity = contentIconGravity;
4680            return this;
4681        }
4682
4683        /**
4684         * Get the gravity that the content icon should have within the notification display.
4685         * Supported values include {@link android.view.Gravity#START} and
4686         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4687         * @see #getContentIcon
4688         */
4689        public int getContentIconGravity() {
4690            return mContentIconGravity;
4691        }
4692
4693        /**
4694         * Set an action from this notification's actions to be clickable with the content of
4695         * this notification. This action will no longer display separately from the
4696         * notification's content.
4697         *
4698         * <p>For notifications with multiple pages, child pages can also have content actions
4699         * set, although the list of available actions comes from the main notification and not
4700         * from the child page's notification.
4701         *
4702         * @param actionIndex The index of the action to hoist onto the current notification page.
4703         *                    If wearable actions were added to the main notification, this index
4704         *                    will apply to that list, otherwise it will apply to the regular
4705         *                    actions list.
4706         */
4707        public WearableExtender setContentAction(int actionIndex) {
4708            mContentActionIndex = actionIndex;
4709            return this;
4710        }
4711
4712        /**
4713         * Get the index of the notification action, if any, that was specified as being clickable
4714         * with the content of this notification. This action will no longer display separately
4715         * from the notification's content.
4716         *
4717         * <p>For notifications with multiple pages, child pages can also have content actions
4718         * set, although the list of available actions comes from the main notification and not
4719         * from the child page's notification.
4720         *
4721         * <p>If wearable specific actions were added to the main notification, this index will
4722         * apply to that list, otherwise it will apply to the regular actions list.
4723         *
4724         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
4725         */
4726        public int getContentAction() {
4727            return mContentActionIndex;
4728        }
4729
4730        /**
4731         * Set the gravity that this notification should have within the available viewport space.
4732         * Supported values include {@link android.view.Gravity#TOP},
4733         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4734         * The default value is {@link android.view.Gravity#BOTTOM}.
4735         */
4736        public WearableExtender setGravity(int gravity) {
4737            mGravity = gravity;
4738            return this;
4739        }
4740
4741        /**
4742         * Get the gravity that this notification should have within the available viewport space.
4743         * Supported values include {@link android.view.Gravity#TOP},
4744         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4745         * The default value is {@link android.view.Gravity#BOTTOM}.
4746         */
4747        public int getGravity() {
4748            return mGravity;
4749        }
4750
4751        /**
4752         * Set the custom size preset for the display of this notification out of the available
4753         * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4754         * {@link #SIZE_LARGE}.
4755         * <p>Some custom size presets are only applicable for custom display notifications created
4756         * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4757         * documentation for the preset in question. See also
4758         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4759         */
4760        public WearableExtender setCustomSizePreset(int sizePreset) {
4761            mCustomSizePreset = sizePreset;
4762            return this;
4763        }
4764
4765        /**
4766         * Get the custom size preset for the display of this notification out of the available
4767         * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4768         * {@link #SIZE_LARGE}.
4769         * <p>Some custom size presets are only applicable for custom display notifications created
4770         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4771         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4772         */
4773        public int getCustomSizePreset() {
4774            return mCustomSizePreset;
4775        }
4776
4777        /**
4778         * Set the custom height in pixels for the display of this notification's content.
4779         * <p>This option is only available for custom display notifications created
4780         * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4781         * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4782         * {@link #getCustomContentHeight}.
4783         */
4784        public WearableExtender setCustomContentHeight(int height) {
4785            mCustomContentHeight = height;
4786            return this;
4787        }
4788
4789        /**
4790         * Get the custom height in pixels for the display of this notification's content.
4791         * <p>This option is only available for custom display notifications created
4792         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4793         * {@link #setCustomContentHeight}.
4794         */
4795        public int getCustomContentHeight() {
4796            return mCustomContentHeight;
4797        }
4798
4799        /**
4800         * Set whether the scrolling position for the contents of this notification should start
4801         * at the bottom of the contents instead of the top when the contents are too long to
4802         * display within the screen.  Default is false (start scroll at the top).
4803         */
4804        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4805            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4806            return this;
4807        }
4808
4809        /**
4810         * Get whether the scrolling position for the contents of this notification should start
4811         * at the bottom of the contents instead of the top when the contents are too long to
4812         * display within the screen. Default is false (start scroll at the top).
4813         */
4814        public boolean getStartScrollBottom() {
4815            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4816        }
4817
4818        /**
4819         * Set whether the content intent is available when the wearable device is not connected
4820         * to a companion device.  The user can still trigger this intent when the wearable device
4821         * is offline, but a visual hint will indicate that the content intent may not be available.
4822         * Defaults to true.
4823         */
4824        public WearableExtender setContentIntentAvailableOffline(
4825                boolean contentIntentAvailableOffline) {
4826            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4827            return this;
4828        }
4829
4830        /**
4831         * Get whether the content intent is available when the wearable device is not connected
4832         * to a companion device.  The user can still trigger this intent when the wearable device
4833         * is offline, but a visual hint will indicate that the content intent may not be available.
4834         * Defaults to true.
4835         */
4836        public boolean getContentIntentAvailableOffline() {
4837            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
4838        }
4839
4840        /**
4841         * Set a hint that this notification's icon should not be displayed.
4842         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
4843         * @return this object for method chaining
4844         */
4845        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
4846            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
4847            return this;
4848        }
4849
4850        /**
4851         * Get a hint that this notification's icon should not be displayed.
4852         * @return {@code true} if this icon should not be displayed, false otherwise.
4853         * The default value is {@code false} if this was never set.
4854         */
4855        public boolean getHintHideIcon() {
4856            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
4857        }
4858
4859        /**
4860         * Set a visual hint that only the background image of this notification should be
4861         * displayed, and other semantic content should be hidden. This hint is only applicable
4862         * to sub-pages added using {@link #addPage}.
4863         */
4864        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
4865            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
4866            return this;
4867        }
4868
4869        /**
4870         * Get a visual hint that only the background image of this notification should be
4871         * displayed, and other semantic content should be hidden. This hint is only applicable
4872         * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
4873         */
4874        public boolean getHintShowBackgroundOnly() {
4875            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
4876        }
4877
4878        private void setFlag(int mask, boolean value) {
4879            if (value) {
4880                mFlags |= mask;
4881            } else {
4882                mFlags &= ~mask;
4883            }
4884        }
4885    }
4886
4887    /**
4888     * Get an array of Notification objects from a parcelable array bundle field.
4889     * Update the bundle to have a typed array so fetches in the future don't need
4890     * to do an array copy.
4891     */
4892    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
4893        Parcelable[] array = bundle.getParcelableArray(key);
4894        if (array instanceof Notification[] || array == null) {
4895            return (Notification[]) array;
4896        }
4897        Notification[] typedArray = Arrays.copyOf(array, array.length,
4898                Notification[].class);
4899        bundle.putParcelableArray(key, typedArray);
4900        return typedArray;
4901    }
4902
4903    private static class BuilderRemoteViews extends RemoteViews {
4904        public BuilderRemoteViews(Parcel parcel) {
4905            super(parcel);
4906        }
4907
4908        public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
4909            super(appInfo, layoutId);
4910        }
4911
4912        @Override
4913        public BuilderRemoteViews clone() {
4914            Parcel p = Parcel.obtain();
4915            writeToParcel(p, 0);
4916            p.setDataPosition(0);
4917            BuilderRemoteViews brv = new BuilderRemoteViews(p);
4918            p.recycle();
4919            return brv;
4920        }
4921    }
4922}
4923