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 
17 package android.app;
18 
19 import android.annotation.IntDef;
20 import android.annotation.SdkConstant;
21 import android.annotation.SdkConstant.SdkConstantType;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.content.res.ColorStateList;
27 import android.graphics.Bitmap;
28 import android.graphics.Canvas;
29 import android.graphics.PorterDuff;
30 import android.graphics.drawable.Drawable;
31 import android.media.AudioAttributes;
32 import android.media.AudioManager;
33 import android.media.session.MediaSession;
34 import android.net.Uri;
35 import android.os.BadParcelableException;
36 import android.os.Build;
37 import android.os.Bundle;
38 import android.os.Parcel;
39 import android.os.Parcelable;
40 import android.os.SystemClock;
41 import android.os.UserHandle;
42 import android.text.TextUtils;
43 import android.util.Log;
44 import android.util.MathUtils;
45 import android.util.TypedValue;
46 import android.view.Gravity;
47 import android.view.View;
48 import android.widget.ProgressBar;
49 import android.widget.RemoteViews;
50 
51 import com.android.internal.R;
52 import com.android.internal.util.NotificationColorUtil;
53 
54 import java.lang.annotation.Retention;
55 import java.lang.annotation.RetentionPolicy;
56 import java.lang.reflect.Constructor;
57 import java.text.NumberFormat;
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.Collections;
61 import 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  */
77 public 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      */
getGroup()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      */
getSortKey()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 
Action(Parcel in)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          */
Action(int icon, CharSequence title, PendingIntent intent)920         public Action(int icon, CharSequence title, PendingIntent intent) {
921             this(icon, title, intent, new Bundle(), null);
922         }
923 
Action(int icon, CharSequence title, PendingIntent intent, Bundle extras, RemoteInput[] remoteInputs)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          */
getExtras()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          */
getRemoteInputs()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              */
Builder(int icon, CharSequence title, PendingIntent intent)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              */
Builder(Action action)973             public Builder(Action action) {
974                 this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
975                         action.getRemoteInputs());
976             }
977 
Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras, RemoteInput[] remoteInputs)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              */
addExtras(Bundle extras)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              */
getExtras()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              */
addRemoteInput(RemoteInput remoteInput)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              */
extend(Extender extender)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              */
build()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
clone()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
describeContents()1059         public int describeContents() {
1060             return 0;
1061         }
1062         @Override
writeToParcel(Parcel out, int flags)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              */
extend(Builder builder)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             // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
1117             private static final String KEY_FLAGS = "flags";
1118             private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1119             private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1120             private static final String KEY_CANCEL_LABEL = "cancelLabel";
1121 
1122             // Flags bitwise-ored to mFlags
1123             private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1124 
1125             // Default value for flags integer
1126             private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1127 
1128             private int mFlags = DEFAULT_FLAGS;
1129 
1130             private CharSequence mInProgressLabel;
1131             private CharSequence mConfirmLabel;
1132             private CharSequence mCancelLabel;
1133 
1134             /**
1135              * Create a {@link android.app.Notification.Action.WearableExtender} with default
1136              * options.
1137              */
WearableExtender()1138             public WearableExtender() {
1139             }
1140 
1141             /**
1142              * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1143              * wearable options present in an existing notification action.
1144              * @param action the notification action to inspect.
1145              */
WearableExtender(Action action)1146             public WearableExtender(Action action) {
1147                 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1148                 if (wearableBundle != null) {
1149                     mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1150                     mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1151                     mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1152                     mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
1153                 }
1154             }
1155 
1156             /**
1157              * Apply wearable extensions to a notification action that is being built. This is
1158              * typically called by the {@link android.app.Notification.Action.Builder#extend}
1159              * method of {@link android.app.Notification.Action.Builder}.
1160              */
1161             @Override
extend(Action.Builder builder)1162             public Action.Builder extend(Action.Builder builder) {
1163                 Bundle wearableBundle = new Bundle();
1164 
1165                 if (mFlags != DEFAULT_FLAGS) {
1166                     wearableBundle.putInt(KEY_FLAGS, mFlags);
1167                 }
1168                 if (mInProgressLabel != null) {
1169                     wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1170                 }
1171                 if (mConfirmLabel != null) {
1172                     wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1173                 }
1174                 if (mCancelLabel != null) {
1175                     wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1176                 }
1177 
1178                 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1179                 return builder;
1180             }
1181 
1182             @Override
clone()1183             public WearableExtender clone() {
1184                 WearableExtender that = new WearableExtender();
1185                 that.mFlags = this.mFlags;
1186                 that.mInProgressLabel = this.mInProgressLabel;
1187                 that.mConfirmLabel = this.mConfirmLabel;
1188                 that.mCancelLabel = this.mCancelLabel;
1189                 return that;
1190             }
1191 
1192             /**
1193              * Set whether this action is available when the wearable device is not connected to
1194              * a companion device. The user can still trigger this action when the wearable device is
1195              * offline, but a visual hint will indicate that the action may not be available.
1196              * Defaults to true.
1197              */
setAvailableOffline(boolean availableOffline)1198             public WearableExtender setAvailableOffline(boolean availableOffline) {
1199                 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1200                 return this;
1201             }
1202 
1203             /**
1204              * Get whether this action is available when the wearable device is not connected to
1205              * a companion device. The user can still trigger this action when the wearable device is
1206              * offline, but a visual hint will indicate that the action may not be available.
1207              * Defaults to true.
1208              */
isAvailableOffline()1209             public boolean isAvailableOffline() {
1210                 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1211             }
1212 
setFlag(int mask, boolean value)1213             private void setFlag(int mask, boolean value) {
1214                 if (value) {
1215                     mFlags |= mask;
1216                 } else {
1217                     mFlags &= ~mask;
1218                 }
1219             }
1220 
1221             /**
1222              * Set a label to display while the wearable is preparing to automatically execute the
1223              * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1224              *
1225              * @param label the label to display while the action is being prepared to execute
1226              * @return this object for method chaining
1227              */
setInProgressLabel(CharSequence label)1228             public WearableExtender setInProgressLabel(CharSequence label) {
1229                 mInProgressLabel = label;
1230                 return this;
1231             }
1232 
1233             /**
1234              * Get the label to display while the wearable is preparing to automatically execute
1235              * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1236              *
1237              * @return the label to display while the action is being prepared to execute
1238              */
getInProgressLabel()1239             public CharSequence getInProgressLabel() {
1240                 return mInProgressLabel;
1241             }
1242 
1243             /**
1244              * Set a label to display to confirm that the action should be executed.
1245              * This is usually an imperative verb like "Send".
1246              *
1247              * @param label the label to confirm the action should be executed
1248              * @return this object for method chaining
1249              */
setConfirmLabel(CharSequence label)1250             public WearableExtender setConfirmLabel(CharSequence label) {
1251                 mConfirmLabel = label;
1252                 return this;
1253             }
1254 
1255             /**
1256              * Get the label to display to confirm that the action should be executed.
1257              * This is usually an imperative verb like "Send".
1258              *
1259              * @return the label to confirm the action should be executed
1260              */
getConfirmLabel()1261             public CharSequence getConfirmLabel() {
1262                 return mConfirmLabel;
1263             }
1264 
1265             /**
1266              * Set a label to display to cancel the action.
1267              * This is usually an imperative verb, like "Cancel".
1268              *
1269              * @param label the label to display to cancel the action
1270              * @return this object for method chaining
1271              */
setCancelLabel(CharSequence label)1272             public WearableExtender setCancelLabel(CharSequence label) {
1273                 mCancelLabel = label;
1274                 return this;
1275             }
1276 
1277             /**
1278              * Get the label to display to cancel the action.
1279              * This is usually an imperative verb like "Cancel".
1280              *
1281              * @return the label to display to cancel the action
1282              */
getCancelLabel()1283             public CharSequence getCancelLabel() {
1284                 return mCancelLabel;
1285             }
1286         }
1287     }
1288 
1289     /**
1290      * Array of all {@link Action} structures attached to this notification by
1291      * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1292      * {@link android.service.notification.NotificationListenerService} that provide an alternative
1293      * interface for invoking actions.
1294      */
1295     public Action[] actions;
1296 
1297     /**
1298      * Replacement version of this notification whose content will be shown
1299      * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1300      * and {@link #VISIBILITY_PUBLIC}.
1301      */
1302     public Notification publicVersion;
1303 
1304     /**
1305      * Constructs a Notification object with default values.
1306      * You might want to consider using {@link Builder} instead.
1307      */
Notification()1308     public Notification()
1309     {
1310         this.when = System.currentTimeMillis();
1311         this.priority = PRIORITY_DEFAULT;
1312     }
1313 
1314     /**
1315      * @hide
1316      */
Notification(Context context, int icon, CharSequence tickerText, long when, CharSequence contentTitle, CharSequence contentText, Intent contentIntent)1317     public Notification(Context context, int icon, CharSequence tickerText, long when,
1318             CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1319     {
1320         this.when = when;
1321         this.icon = icon;
1322         this.tickerText = tickerText;
1323         setLatestEventInfo(context, contentTitle, contentText,
1324                 PendingIntent.getActivity(context, 0, contentIntent, 0));
1325     }
1326 
1327     /**
1328      * Constructs a Notification object with the information needed to
1329      * have a status bar icon without the standard expanded view.
1330      *
1331      * @param icon          The resource id of the icon to put in the status bar.
1332      * @param tickerText    The text that flows by in the status bar when the notification first
1333      *                      activates.
1334      * @param when          The time to show in the time field.  In the System.currentTimeMillis
1335      *                      timebase.
1336      *
1337      * @deprecated Use {@link Builder} instead.
1338      */
1339     @Deprecated
Notification(int icon, CharSequence tickerText, long when)1340     public Notification(int icon, CharSequence tickerText, long when)
1341     {
1342         this.icon = icon;
1343         this.tickerText = tickerText;
1344         this.when = when;
1345     }
1346 
1347     /**
1348      * Unflatten the notification from a parcel.
1349      */
Notification(Parcel parcel)1350     public Notification(Parcel parcel)
1351     {
1352         int version = parcel.readInt();
1353 
1354         when = parcel.readLong();
1355         icon = parcel.readInt();
1356         number = parcel.readInt();
1357         if (parcel.readInt() != 0) {
1358             contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1359         }
1360         if (parcel.readInt() != 0) {
1361             deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1362         }
1363         if (parcel.readInt() != 0) {
1364             tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1365         }
1366         if (parcel.readInt() != 0) {
1367             tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
1368         }
1369         if (parcel.readInt() != 0) {
1370             contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1371         }
1372         if (parcel.readInt() != 0) {
1373             largeIcon = Bitmap.CREATOR.createFromParcel(parcel);
1374         }
1375         defaults = parcel.readInt();
1376         flags = parcel.readInt();
1377         if (parcel.readInt() != 0) {
1378             sound = Uri.CREATOR.createFromParcel(parcel);
1379         }
1380 
1381         audioStreamType = parcel.readInt();
1382         if (parcel.readInt() != 0) {
1383             audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1384         }
1385         vibrate = parcel.createLongArray();
1386         ledARGB = parcel.readInt();
1387         ledOnMS = parcel.readInt();
1388         ledOffMS = parcel.readInt();
1389         iconLevel = parcel.readInt();
1390 
1391         if (parcel.readInt() != 0) {
1392             fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1393         }
1394 
1395         priority = parcel.readInt();
1396 
1397         category = parcel.readString();
1398 
1399         mGroupKey = parcel.readString();
1400 
1401         mSortKey = parcel.readString();
1402 
1403         extras = parcel.readBundle(); // may be null
1404 
1405         actions = parcel.createTypedArray(Action.CREATOR); // may be null
1406 
1407         if (parcel.readInt() != 0) {
1408             bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1409         }
1410 
1411         if (parcel.readInt() != 0) {
1412             headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1413         }
1414 
1415         visibility = parcel.readInt();
1416 
1417         if (parcel.readInt() != 0) {
1418             publicVersion = Notification.CREATOR.createFromParcel(parcel);
1419         }
1420 
1421         color = parcel.readInt();
1422     }
1423 
1424     @Override
clone()1425     public Notification clone() {
1426         Notification that = new Notification();
1427         cloneInto(that, true);
1428         return that;
1429     }
1430 
1431     /**
1432      * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1433      * of this into that.
1434      * @hide
1435      */
cloneInto(Notification that, boolean heavy)1436     public void cloneInto(Notification that, boolean heavy) {
1437         that.when = this.when;
1438         that.icon = this.icon;
1439         that.number = this.number;
1440 
1441         // PendingIntents are global, so there's no reason (or way) to clone them.
1442         that.contentIntent = this.contentIntent;
1443         that.deleteIntent = this.deleteIntent;
1444         that.fullScreenIntent = this.fullScreenIntent;
1445 
1446         if (this.tickerText != null) {
1447             that.tickerText = this.tickerText.toString();
1448         }
1449         if (heavy && this.tickerView != null) {
1450             that.tickerView = this.tickerView.clone();
1451         }
1452         if (heavy && this.contentView != null) {
1453             that.contentView = this.contentView.clone();
1454         }
1455         if (heavy && this.largeIcon != null) {
1456             that.largeIcon = Bitmap.createBitmap(this.largeIcon);
1457         }
1458         that.iconLevel = this.iconLevel;
1459         that.sound = this.sound; // android.net.Uri is immutable
1460         that.audioStreamType = this.audioStreamType;
1461         if (this.audioAttributes != null) {
1462             that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
1463         }
1464 
1465         final long[] vibrate = this.vibrate;
1466         if (vibrate != null) {
1467             final int N = vibrate.length;
1468             final long[] vib = that.vibrate = new long[N];
1469             System.arraycopy(vibrate, 0, vib, 0, N);
1470         }
1471 
1472         that.ledARGB = this.ledARGB;
1473         that.ledOnMS = this.ledOnMS;
1474         that.ledOffMS = this.ledOffMS;
1475         that.defaults = this.defaults;
1476 
1477         that.flags = this.flags;
1478 
1479         that.priority = this.priority;
1480 
1481         that.category = this.category;
1482 
1483         that.mGroupKey = this.mGroupKey;
1484 
1485         that.mSortKey = this.mSortKey;
1486 
1487         if (this.extras != null) {
1488             try {
1489                 that.extras = new Bundle(this.extras);
1490                 // will unparcel
1491                 that.extras.size();
1492             } catch (BadParcelableException e) {
1493                 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
1494                 that.extras = null;
1495             }
1496         }
1497 
1498         if (this.actions != null) {
1499             that.actions = new Action[this.actions.length];
1500             for(int i=0; i<this.actions.length; i++) {
1501                 that.actions[i] = this.actions[i].clone();
1502             }
1503         }
1504 
1505         if (heavy && this.bigContentView != null) {
1506             that.bigContentView = this.bigContentView.clone();
1507         }
1508 
1509         if (heavy && this.headsUpContentView != null) {
1510             that.headsUpContentView = this.headsUpContentView.clone();
1511         }
1512 
1513         that.visibility = this.visibility;
1514 
1515         if (this.publicVersion != null) {
1516             that.publicVersion = new Notification();
1517             this.publicVersion.cloneInto(that.publicVersion, heavy);
1518         }
1519 
1520         that.color = this.color;
1521 
1522         if (!heavy) {
1523             that.lightenPayload(); // will clean out extras
1524         }
1525     }
1526 
1527     /**
1528      * Removes heavyweight parts of the Notification object for archival or for sending to
1529      * listeners when the full contents are not necessary.
1530      * @hide
1531      */
lightenPayload()1532     public final void lightenPayload() {
1533         tickerView = null;
1534         contentView = null;
1535         bigContentView = null;
1536         headsUpContentView = null;
1537         largeIcon = null;
1538         if (extras != null) {
1539             extras.remove(Notification.EXTRA_LARGE_ICON);
1540             extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
1541             extras.remove(Notification.EXTRA_PICTURE);
1542             extras.remove(Notification.EXTRA_BIG_TEXT);
1543             // Prevent light notifications from being rebuilt.
1544             extras.remove(Builder.EXTRA_NEEDS_REBUILD);
1545         }
1546     }
1547 
1548     /**
1549      * Make sure this CharSequence is safe to put into a bundle, which basically
1550      * means it had better not be some custom Parcelable implementation.
1551      * @hide
1552      */
safeCharSequence(CharSequence cs)1553     public static CharSequence safeCharSequence(CharSequence cs) {
1554         if (cs == null) return cs;
1555         if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
1556             cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
1557         }
1558         if (cs instanceof Parcelable) {
1559             Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
1560                     + " instance is a custom Parcelable and not allowed in Notification");
1561             return cs.toString();
1562         }
1563 
1564         return cs;
1565     }
1566 
describeContents()1567     public int describeContents() {
1568         return 0;
1569     }
1570 
1571     /**
1572      * Flatten this notification from a parcel.
1573      */
writeToParcel(Parcel parcel, int flags)1574     public void writeToParcel(Parcel parcel, int flags)
1575     {
1576         parcel.writeInt(1);
1577 
1578         parcel.writeLong(when);
1579         parcel.writeInt(icon);
1580         parcel.writeInt(number);
1581         if (contentIntent != null) {
1582             parcel.writeInt(1);
1583             contentIntent.writeToParcel(parcel, 0);
1584         } else {
1585             parcel.writeInt(0);
1586         }
1587         if (deleteIntent != null) {
1588             parcel.writeInt(1);
1589             deleteIntent.writeToParcel(parcel, 0);
1590         } else {
1591             parcel.writeInt(0);
1592         }
1593         if (tickerText != null) {
1594             parcel.writeInt(1);
1595             TextUtils.writeToParcel(tickerText, parcel, flags);
1596         } else {
1597             parcel.writeInt(0);
1598         }
1599         if (tickerView != null) {
1600             parcel.writeInt(1);
1601             tickerView.writeToParcel(parcel, 0);
1602         } else {
1603             parcel.writeInt(0);
1604         }
1605         if (contentView != null) {
1606             parcel.writeInt(1);
1607             contentView.writeToParcel(parcel, 0);
1608         } else {
1609             parcel.writeInt(0);
1610         }
1611         if (largeIcon != null) {
1612             parcel.writeInt(1);
1613             largeIcon.writeToParcel(parcel, 0);
1614         } else {
1615             parcel.writeInt(0);
1616         }
1617 
1618         parcel.writeInt(defaults);
1619         parcel.writeInt(this.flags);
1620 
1621         if (sound != null) {
1622             parcel.writeInt(1);
1623             sound.writeToParcel(parcel, 0);
1624         } else {
1625             parcel.writeInt(0);
1626         }
1627         parcel.writeInt(audioStreamType);
1628 
1629         if (audioAttributes != null) {
1630             parcel.writeInt(1);
1631             audioAttributes.writeToParcel(parcel, 0);
1632         } else {
1633             parcel.writeInt(0);
1634         }
1635 
1636         parcel.writeLongArray(vibrate);
1637         parcel.writeInt(ledARGB);
1638         parcel.writeInt(ledOnMS);
1639         parcel.writeInt(ledOffMS);
1640         parcel.writeInt(iconLevel);
1641 
1642         if (fullScreenIntent != null) {
1643             parcel.writeInt(1);
1644             fullScreenIntent.writeToParcel(parcel, 0);
1645         } else {
1646             parcel.writeInt(0);
1647         }
1648 
1649         parcel.writeInt(priority);
1650 
1651         parcel.writeString(category);
1652 
1653         parcel.writeString(mGroupKey);
1654 
1655         parcel.writeString(mSortKey);
1656 
1657         parcel.writeBundle(extras); // null ok
1658 
1659         parcel.writeTypedArray(actions, 0); // null ok
1660 
1661         if (bigContentView != null) {
1662             parcel.writeInt(1);
1663             bigContentView.writeToParcel(parcel, 0);
1664         } else {
1665             parcel.writeInt(0);
1666         }
1667 
1668         if (headsUpContentView != null) {
1669             parcel.writeInt(1);
1670             headsUpContentView.writeToParcel(parcel, 0);
1671         } else {
1672             parcel.writeInt(0);
1673         }
1674 
1675         parcel.writeInt(visibility);
1676 
1677         if (publicVersion != null) {
1678             parcel.writeInt(1);
1679             publicVersion.writeToParcel(parcel, 0);
1680         } else {
1681             parcel.writeInt(0);
1682         }
1683 
1684         parcel.writeInt(color);
1685     }
1686 
1687     /**
1688      * Parcelable.Creator that instantiates Notification objects
1689      */
1690     public static final Parcelable.Creator<Notification> CREATOR
1691             = new Parcelable.Creator<Notification>()
1692     {
1693         public Notification createFromParcel(Parcel parcel)
1694         {
1695             return new Notification(parcel);
1696         }
1697 
1698         public Notification[] newArray(int size)
1699         {
1700             return new Notification[size];
1701         }
1702     };
1703 
1704     /**
1705      * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
1706      * layout.
1707      *
1708      * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
1709      * in the view.</p>
1710      * @param context       The context for your application / activity.
1711      * @param contentTitle The title that goes in the expanded entry.
1712      * @param contentText  The text that goes in the expanded entry.
1713      * @param contentIntent The intent to launch when the user clicks the expanded notification.
1714      * If this is an activity, it must include the
1715      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
1716      * that you take care of task management as described in the
1717      * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
1718      * Stack</a> document.
1719      *
1720      * @deprecated Use {@link Builder} instead.
1721      */
1722     @Deprecated
setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent)1723     public void setLatestEventInfo(Context context,
1724             CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
1725         Notification.Builder builder = new Notification.Builder(context);
1726 
1727         // First, ensure that key pieces of information that may have been set directly
1728         // are preserved
1729         builder.setWhen(this.when);
1730         builder.setSmallIcon(this.icon);
1731         builder.setPriority(this.priority);
1732         builder.setTicker(this.tickerText);
1733         builder.setNumber(this.number);
1734         builder.setColor(this.color);
1735         builder.mFlags = this.flags;
1736         builder.setSound(this.sound, this.audioStreamType);
1737         builder.setDefaults(this.defaults);
1738         builder.setVibrate(this.vibrate);
1739 
1740         // now apply the latestEventInfo fields
1741         if (contentTitle != null) {
1742             builder.setContentTitle(contentTitle);
1743         }
1744         if (contentText != null) {
1745             builder.setContentText(contentText);
1746         }
1747         builder.setContentIntent(contentIntent);
1748         builder.buildInto(this);
1749     }
1750 
1751     @Override
toString()1752     public String toString() {
1753         StringBuilder sb = new StringBuilder();
1754         sb.append("Notification(pri=");
1755         sb.append(priority);
1756         sb.append(" contentView=");
1757         if (contentView != null) {
1758             sb.append(contentView.getPackage());
1759             sb.append("/0x");
1760             sb.append(Integer.toHexString(contentView.getLayoutId()));
1761         } else {
1762             sb.append("null");
1763         }
1764         sb.append(" vibrate=");
1765         if ((this.defaults & DEFAULT_VIBRATE) != 0) {
1766             sb.append("default");
1767         } else if (this.vibrate != null) {
1768             int N = this.vibrate.length-1;
1769             sb.append("[");
1770             for (int i=0; i<N; i++) {
1771                 sb.append(this.vibrate[i]);
1772                 sb.append(',');
1773             }
1774             if (N != -1) {
1775                 sb.append(this.vibrate[N]);
1776             }
1777             sb.append("]");
1778         } else {
1779             sb.append("null");
1780         }
1781         sb.append(" sound=");
1782         if ((this.defaults & DEFAULT_SOUND) != 0) {
1783             sb.append("default");
1784         } else if (this.sound != null) {
1785             sb.append(this.sound.toString());
1786         } else {
1787             sb.append("null");
1788         }
1789         sb.append(" defaults=0x");
1790         sb.append(Integer.toHexString(this.defaults));
1791         sb.append(" flags=0x");
1792         sb.append(Integer.toHexString(this.flags));
1793         sb.append(String.format(" color=0x%08x", this.color));
1794         if (this.category != null) {
1795             sb.append(" category=");
1796             sb.append(this.category);
1797         }
1798         if (this.mGroupKey != null) {
1799             sb.append(" groupKey=");
1800             sb.append(this.mGroupKey);
1801         }
1802         if (this.mSortKey != null) {
1803             sb.append(" sortKey=");
1804             sb.append(this.mSortKey);
1805         }
1806         if (actions != null) {
1807             sb.append(" actions=");
1808             sb.append(actions.length);
1809         }
1810         sb.append(" vis=");
1811         sb.append(visibilityToString(this.visibility));
1812         if (this.publicVersion != null) {
1813             sb.append(" publicVersion=");
1814             sb.append(publicVersion.toString());
1815         }
1816         sb.append(")");
1817         return sb.toString();
1818     }
1819 
1820     /**
1821      * {@hide}
1822      */
visibilityToString(int vis)1823     public static String visibilityToString(int vis) {
1824         switch (vis) {
1825             case VISIBILITY_PRIVATE:
1826                 return "PRIVATE";
1827             case VISIBILITY_PUBLIC:
1828                 return "PUBLIC";
1829             case VISIBILITY_SECRET:
1830                 return "SECRET";
1831             default:
1832                 return "UNKNOWN(" + String.valueOf(vis) + ")";
1833         }
1834     }
1835 
1836     /**
1837      * @hide
1838      */
isValid()1839     public boolean isValid() {
1840         // Would like to check for icon!=0 here, too, but NotificationManagerService accepts that
1841         // for legacy reasons.
1842         return contentView != null || extras.getBoolean(Builder.EXTRA_REBUILD_CONTENT_VIEW);
1843     }
1844 
1845     /**
1846      * @hide
1847      */
isGroupSummary()1848     public boolean isGroupSummary() {
1849         return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
1850     }
1851 
1852     /**
1853      * @hide
1854      */
isGroupChild()1855     public boolean isGroupChild() {
1856         return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
1857     }
1858 
1859     /**
1860      * Builder class for {@link Notification} objects.
1861      *
1862      * Provides a convenient way to set the various fields of a {@link Notification} and generate
1863      * content views using the platform's notification layout template. If your app supports
1864      * versions of Android as old as API level 4, you can instead use
1865      * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
1866      * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
1867      * library</a>.
1868      *
1869      * <p>Example:
1870      *
1871      * <pre class="prettyprint">
1872      * Notification noti = new Notification.Builder(mContext)
1873      *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
1874      *         .setContentText(subject)
1875      *         .setSmallIcon(R.drawable.new_mail)
1876      *         .setLargeIcon(aBitmap)
1877      *         .build();
1878      * </pre>
1879      */
1880     public static class Builder {
1881         private static final int MAX_ACTION_BUTTONS = 3;
1882         private static final float LARGE_TEXT_SCALE = 1.3f;
1883 
1884         /**
1885          * @hide
1886          */
1887         public static final String EXTRA_NEEDS_REBUILD = "android.rebuild";
1888 
1889         /**
1890          * @hide
1891          */
1892         public static final String EXTRA_REBUILD_LARGE_ICON = "android.rebuild.largeIcon";
1893         /**
1894          * @hide
1895          */
1896         public static final String EXTRA_REBUILD_CONTENT_VIEW = "android.rebuild.contentView";
1897         /**
1898          * @hide
1899          */
1900         public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
1901                 "android.rebuild.contentViewActionCount";
1902         /**
1903          * @hide
1904          */
1905         public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW
1906                 = "android.rebuild.bigView";
1907         /**
1908          * @hide
1909          */
1910         public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
1911                 = "android.rebuild.bigViewActionCount";
1912         /**
1913          * @hide
1914          */
1915         public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW
1916                 = "android.rebuild.hudView";
1917         /**
1918          * @hide
1919          */
1920         public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
1921                 = "android.rebuild.hudViewActionCount";
1922 
1923         /**
1924          * The ApplicationInfo of the package that created the notification, used to create
1925          * a context to rebuild the notification via a Builder.
1926          * @hide
1927          */
1928         private static final String EXTRA_REBUILD_CONTEXT_APPLICATION_INFO =
1929                 "android.rebuild.applicationInfo";
1930 
1931         // Whether to enable stripping (at post time) & rebuilding (at listener receive time) of
1932         // memory intensive resources.
1933         private static final boolean STRIP_AND_REBUILD = true;
1934 
1935         private Context mContext;
1936 
1937         private long mWhen;
1938         private int mSmallIcon;
1939         private int mSmallIconLevel;
1940         private int mNumber;
1941         private CharSequence mContentTitle;
1942         private CharSequence mContentText;
1943         private CharSequence mContentInfo;
1944         private CharSequence mSubText;
1945         private PendingIntent mContentIntent;
1946         private RemoteViews mContentView;
1947         private PendingIntent mDeleteIntent;
1948         private PendingIntent mFullScreenIntent;
1949         private CharSequence mTickerText;
1950         private RemoteViews mTickerView;
1951         private Bitmap mLargeIcon;
1952         private Uri mSound;
1953         private int mAudioStreamType;
1954         private AudioAttributes mAudioAttributes;
1955         private long[] mVibrate;
1956         private int mLedArgb;
1957         private int mLedOnMs;
1958         private int mLedOffMs;
1959         private int mDefaults;
1960         private int mFlags;
1961         private int mProgressMax;
1962         private int mProgress;
1963         private boolean mProgressIndeterminate;
1964         private String mCategory;
1965         private String mGroupKey;
1966         private String mSortKey;
1967         private Bundle mExtras;
1968         private int mPriority;
1969         private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
1970         private boolean mUseChronometer;
1971         private Style mStyle;
1972         private boolean mShowWhen = true;
1973         private int mVisibility = VISIBILITY_PRIVATE;
1974         private Notification mPublicVersion = null;
1975         private final NotificationColorUtil mColorUtil;
1976         private ArrayList<String> mPeople;
1977         private int mColor = COLOR_DEFAULT;
1978 
1979         /**
1980          * The user that built the notification originally.
1981          */
1982         private int mOriginatingUserId;
1983 
1984         /**
1985          * Contains extras related to rebuilding during the build phase.
1986          */
1987         private Bundle mRebuildBundle = new Bundle();
1988         /**
1989          * Contains the notification to rebuild when this Builder is in "rebuild" mode.
1990          * Null otherwise.
1991          */
1992         private Notification mRebuildNotification = null;
1993 
1994         /**
1995          * Whether the build notification has three lines. This is used to make the top padding for
1996          * both the contracted and expanded layout consistent.
1997          *
1998          * <p>
1999          * This field is only valid during the build phase.
2000          */
2001         private boolean mHasThreeLines;
2002 
2003         /**
2004          * Constructs a new Builder with the defaults:
2005          *
2006 
2007          * <table>
2008          * <tr><th align=right>priority</th>
2009          *     <td>{@link #PRIORITY_DEFAULT}</td></tr>
2010          * <tr><th align=right>when</th>
2011          *     <td>now ({@link System#currentTimeMillis()})</td></tr>
2012          * <tr><th align=right>audio stream</th>
2013          *     <td>{@link #STREAM_DEFAULT}</td></tr>
2014          * </table>
2015          *
2016 
2017          * @param context
2018          *            A {@link Context} that will be used by the Builder to construct the
2019          *            RemoteViews. The Context will not be held past the lifetime of this Builder
2020          *            object.
2021          */
Builder(Context context)2022         public Builder(Context context) {
2023             /*
2024              * Important compatibility note!
2025              * Some apps out in the wild create a Notification.Builder in their Activity subclass
2026              * constructor for later use. At this point Activities - themselves subclasses of
2027              * ContextWrapper - do not have their inner Context populated yet. This means that
2028              * any calls to Context methods from within this constructor can cause NPEs in existing
2029              * apps. Any data populated from mContext should therefore be populated lazily to
2030              * preserve compatibility.
2031              */
2032             mContext = context;
2033 
2034             // Set defaults to match the defaults of a Notification
2035             mWhen = System.currentTimeMillis();
2036             mAudioStreamType = STREAM_DEFAULT;
2037             mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
2038             mPriority = PRIORITY_DEFAULT;
2039             mPeople = new ArrayList<String>();
2040 
2041             mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP ?
2042                     NotificationColorUtil.getInstance(mContext) : null;
2043         }
2044 
2045         /**
2046          * Creates a Builder for rebuilding the given Notification.
2047          * <p>
2048          * Call {@link #rebuild()} to retrieve the rebuilt version of 'n'.
2049          */
2050         private Builder(Context context, Notification n) {
2051             this(context);
2052             mRebuildNotification = n;
2053             restoreFromNotification(n);
2054 
2055             Style style = null;
2056             Bundle extras = n.extras;
2057             String templateClass = extras.getString(EXTRA_TEMPLATE);
2058             if (!TextUtils.isEmpty(templateClass)) {
2059                 Class<? extends Style> styleClass = getNotificationStyleClass(templateClass);
2060                 if (styleClass == null) {
2061                     Log.d(TAG, "Unknown style class: " + styleClass);
2062                     return;
2063                 }
2064 
2065                 try {
2066                     Constructor<? extends Style> constructor = styleClass.getConstructor();
2067                     style = constructor.newInstance();
2068                     style.restoreFromExtras(extras);
2069                 } catch (Throwable t) {
2070                     Log.e(TAG, "Could not create Style", t);
2071                     return;
2072                 }
2073             }
2074             if (style != null) {
2075                 setStyle(style);
2076             }
2077         }
2078 
2079         /**
2080          * Add a timestamp pertaining to the notification (usually the time the event occurred).
2081          * It will be shown in the notification content view by default; use
2082          * {@link #setShowWhen(boolean) setShowWhen} to control this.
2083          *
2084          * @see Notification#when
2085          */
2086         public Builder setWhen(long when) {
2087             mWhen = when;
2088             return this;
2089         }
2090 
2091         /**
2092          * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
2093          * in the content view.
2094          */
2095         public Builder setShowWhen(boolean show) {
2096             mShowWhen = show;
2097             return this;
2098         }
2099 
2100         /**
2101          * Show the {@link Notification#when} field as a stopwatch.
2102          *
2103          * Instead of presenting <code>when</code> as a timestamp, the notification will show an
2104          * automatically updating display of the minutes and seconds since <code>when</code>.
2105          *
2106          * Useful when showing an elapsed time (like an ongoing phone call).
2107          *
2108          * @see android.widget.Chronometer
2109          * @see Notification#when
2110          */
2111         public Builder setUsesChronometer(boolean b) {
2112             mUseChronometer = b;
2113             return this;
2114         }
2115 
2116         /**
2117          * Set the small icon resource, which will be used to represent the notification in the
2118          * status bar.
2119          *
2120 
2121          * The platform template for the expanded view will draw this icon in the left, unless a
2122          * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2123          * icon will be moved to the right-hand side.
2124          *
2125 
2126          * @param icon
2127          *            A resource ID in the application's package of the drawable to use.
2128          * @see Notification#icon
2129          */
2130         public Builder setSmallIcon(int icon) {
2131             mSmallIcon = icon;
2132             return this;
2133         }
2134 
2135         /**
2136          * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
2137          * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
2138          * LevelListDrawable}.
2139          *
2140          * @param icon A resource ID in the application's package of the drawable to use.
2141          * @param level The level to use for the icon.
2142          *
2143          * @see Notification#icon
2144          * @see Notification#iconLevel
2145          */
2146         public Builder setSmallIcon(int icon, int level) {
2147             mSmallIcon = icon;
2148             mSmallIconLevel = level;
2149             return this;
2150         }
2151 
2152         /**
2153          * Set the first line of text in the platform notification template.
2154          */
2155         public Builder setContentTitle(CharSequence title) {
2156             mContentTitle = safeCharSequence(title);
2157             return this;
2158         }
2159 
2160         /**
2161          * Set the second line of text in the platform notification template.
2162          */
2163         public Builder setContentText(CharSequence text) {
2164             mContentText = safeCharSequence(text);
2165             return this;
2166         }
2167 
2168         /**
2169          * Set the third line of text in the platform notification template.
2170          * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
2171          * same location in the standard template.
2172          */
2173         public Builder setSubText(CharSequence text) {
2174             mSubText = safeCharSequence(text);
2175             return this;
2176         }
2177 
2178         /**
2179          * Set the large number at the right-hand side of the notification.  This is
2180          * equivalent to setContentInfo, although it might show the number in a different
2181          * font size for readability.
2182          */
2183         public Builder setNumber(int number) {
2184             mNumber = number;
2185             return this;
2186         }
2187 
2188         /**
2189          * A small piece of additional information pertaining to this notification.
2190          *
2191          * The platform template will draw this on the last line of the notification, at the far
2192          * right (to the right of a smallIcon if it has been placed there).
2193          */
2194         public Builder setContentInfo(CharSequence info) {
2195             mContentInfo = safeCharSequence(info);
2196             return this;
2197         }
2198 
2199         /**
2200          * Set the progress this notification represents.
2201          *
2202          * The platform template will represent this using a {@link ProgressBar}.
2203          */
2204         public Builder setProgress(int max, int progress, boolean indeterminate) {
2205             mProgressMax = max;
2206             mProgress = progress;
2207             mProgressIndeterminate = indeterminate;
2208             return this;
2209         }
2210 
2211         /**
2212          * Supply a custom RemoteViews to use instead of the platform template.
2213          *
2214          * @see Notification#contentView
2215          */
2216         public Builder setContent(RemoteViews views) {
2217             mContentView = views;
2218             return this;
2219         }
2220 
2221         /**
2222          * Supply a {@link PendingIntent} to be sent when the notification is clicked.
2223          *
2224          * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
2225          * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
2226          * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
2227          * to assign PendingIntents to individual views in that custom layout (i.e., to create
2228          * clickable buttons inside the notification view).
2229          *
2230          * @see Notification#contentIntent Notification.contentIntent
2231          */
2232         public Builder setContentIntent(PendingIntent intent) {
2233             mContentIntent = intent;
2234             return this;
2235         }
2236 
2237         /**
2238          * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
2239          *
2240          * @see Notification#deleteIntent
2241          */
2242         public Builder setDeleteIntent(PendingIntent intent) {
2243             mDeleteIntent = intent;
2244             return this;
2245         }
2246 
2247         /**
2248          * An intent to launch instead of posting the notification to the status bar.
2249          * Only for use with extremely high-priority notifications demanding the user's
2250          * <strong>immediate</strong> attention, such as an incoming phone call or
2251          * alarm clock that the user has explicitly set to a particular time.
2252          * If this facility is used for something else, please give the user an option
2253          * to turn it off and use a normal notification, as this can be extremely
2254          * disruptive.
2255          *
2256          * <p>
2257          * The system UI may choose to display a heads-up notification, instead of
2258          * launching this intent, while the user is using the device.
2259          * </p>
2260          *
2261          * @param intent The pending intent to launch.
2262          * @param highPriority Passing true will cause this notification to be sent
2263          *          even if other notifications are suppressed.
2264          *
2265          * @see Notification#fullScreenIntent
2266          */
2267         public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
2268             mFullScreenIntent = intent;
2269             setFlag(FLAG_HIGH_PRIORITY, highPriority);
2270             return this;
2271         }
2272 
2273         /**
2274          * Set the "ticker" text which is sent to accessibility services.
2275          *
2276          * @see Notification#tickerText
2277          */
2278         public Builder setTicker(CharSequence tickerText) {
2279             mTickerText = safeCharSequence(tickerText);
2280             return this;
2281         }
2282 
2283         /**
2284          * Obsolete version of {@link #setTicker(CharSequence)}.
2285          *
2286          */
2287         @Deprecated
2288         public Builder setTicker(CharSequence tickerText, RemoteViews views) {
2289             mTickerText = safeCharSequence(tickerText);
2290             mTickerView = views; // we'll save it for you anyway
2291             return this;
2292         }
2293 
2294         /**
2295          * Add a large icon to the notification (and the ticker on some devices).
2296          *
2297          * In the platform template, this image will be shown on the left of the notification view
2298          * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side).
2299          *
2300          * @see Notification#largeIcon
2301          */
2302         public Builder setLargeIcon(Bitmap icon) {
2303             mLargeIcon = icon;
2304             return this;
2305         }
2306 
2307         /**
2308          * Set the sound to play.
2309          *
2310          * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
2311          * for notifications.
2312          *
2313          * <p>
2314          * A notification that is noisy is more likely to be presented as a heads-up notification.
2315          * </p>
2316          *
2317          * @see Notification#sound
2318          */
2319         public Builder setSound(Uri sound) {
2320             mSound = sound;
2321             mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
2322             return this;
2323         }
2324 
2325         /**
2326          * Set the sound to play, along with a specific stream on which to play it.
2327          *
2328          * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
2329          *
2330          * <p>
2331          * A notification that is noisy is more likely to be presented as a heads-up notification.
2332          * </p>
2333          * @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.
2334          * @see Notification#sound
2335          */
2336         @Deprecated
2337         public Builder setSound(Uri sound, int streamType) {
2338             mSound = sound;
2339             mAudioStreamType = streamType;
2340             return this;
2341         }
2342 
2343         /**
2344          * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
2345          * use during playback.
2346          *
2347          * <p>
2348          * A notification that is noisy is more likely to be presented as a heads-up notification.
2349          * </p>
2350          *
2351          * @see Notification#sound
2352          */
2353         public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
2354             mSound = sound;
2355             mAudioAttributes = audioAttributes;
2356             return this;
2357         }
2358 
2359         /**
2360          * Set the vibration pattern to use.
2361          *
2362          * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
2363          * <code>pattern</code> parameter.
2364          *
2365          * <p>
2366          * A notification that vibrates is more likely to be presented as a heads-up notification.
2367          * </p>
2368          *
2369          * @see Notification#vibrate
2370          */
2371         public Builder setVibrate(long[] pattern) {
2372             mVibrate = pattern;
2373             return this;
2374         }
2375 
2376         /**
2377          * Set the desired color for the indicator LED on the device, as well as the
2378          * blink duty cycle (specified in milliseconds).
2379          *
2380 
2381          * Not all devices will honor all (or even any) of these values.
2382          *
2383 
2384          * @see Notification#ledARGB
2385          * @see Notification#ledOnMS
2386          * @see Notification#ledOffMS
2387          */
2388         public Builder setLights(int argb, int onMs, int offMs) {
2389             mLedArgb = argb;
2390             mLedOnMs = onMs;
2391             mLedOffMs = offMs;
2392             return this;
2393         }
2394 
2395         /**
2396          * Set whether this is an "ongoing" notification.
2397          *
2398 
2399          * Ongoing notifications cannot be dismissed by the user, so your application or service
2400          * must take care of canceling them.
2401          *
2402 
2403          * They are typically used to indicate a background task that the user is actively engaged
2404          * with (e.g., playing music) or is pending in some way and therefore occupying the device
2405          * (e.g., a file download, sync operation, active network connection).
2406          *
2407 
2408          * @see Notification#FLAG_ONGOING_EVENT
2409          * @see Service#setForeground(boolean)
2410          */
2411         public Builder setOngoing(boolean ongoing) {
2412             setFlag(FLAG_ONGOING_EVENT, ongoing);
2413             return this;
2414         }
2415 
2416         /**
2417          * Set this flag if you would only like the sound, vibrate
2418          * and ticker to be played if the notification is not already showing.
2419          *
2420          * @see Notification#FLAG_ONLY_ALERT_ONCE
2421          */
2422         public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
2423             setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
2424             return this;
2425         }
2426 
2427         /**
2428          * Make this notification automatically dismissed when the user touches it. The
2429          * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
2430          *
2431          * @see Notification#FLAG_AUTO_CANCEL
2432          */
2433         public Builder setAutoCancel(boolean autoCancel) {
2434             setFlag(FLAG_AUTO_CANCEL, autoCancel);
2435             return this;
2436         }
2437 
2438         /**
2439          * Set whether or not this notification should not bridge to other devices.
2440          *
2441          * <p>Some notifications can be bridged to other devices for remote display.
2442          * This hint can be set to recommend this notification not be bridged.
2443          */
2444         public Builder setLocalOnly(boolean localOnly) {
2445             setFlag(FLAG_LOCAL_ONLY, localOnly);
2446             return this;
2447         }
2448 
2449         /**
2450          * Set which notification properties will be inherited from system defaults.
2451          * <p>
2452          * The value should be one or more of the following fields combined with
2453          * bitwise-or:
2454          * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
2455          * <p>
2456          * For all default values, use {@link #DEFAULT_ALL}.
2457          */
2458         public Builder setDefaults(int defaults) {
2459             mDefaults = defaults;
2460             return this;
2461         }
2462 
2463         /**
2464          * Set the priority of this notification.
2465          *
2466          * @see Notification#priority
2467          */
2468         public Builder setPriority(@Priority int pri) {
2469             mPriority = pri;
2470             return this;
2471         }
2472 
2473         /**
2474          * Set the notification category.
2475          *
2476          * @see Notification#category
2477          */
2478         public Builder setCategory(String category) {
2479             mCategory = category;
2480             return this;
2481         }
2482 
2483         /**
2484          * Add a person that is relevant to this notification.
2485          *
2486          * <P>
2487          * Depending on user preferences, this annotation may allow the notification to pass
2488          * through interruption filters, and to appear more prominently in the user interface.
2489          * </P>
2490          *
2491          * <P>
2492          * The person should be specified by the {@code String} representation of a
2493          * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
2494          * </P>
2495          *
2496          * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
2497          * URIs.  The path part of these URIs must exist in the contacts database, in the
2498          * appropriate column, or the reference will be discarded as invalid. Telephone schema
2499          * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
2500          * </P>
2501          *
2502          * @param uri A URI for the person.
2503          * @see Notification#EXTRA_PEOPLE
2504          */
2505         public Builder addPerson(String uri) {
2506             mPeople.add(uri);
2507             return this;
2508         }
2509 
2510         /**
2511          * Set this notification to be part of a group of notifications sharing the same key.
2512          * Grouped notifications may display in a cluster or stack on devices which
2513          * support such rendering.
2514          *
2515          * <p>To make this notification the summary for its group, also call
2516          * {@link #setGroupSummary}. A sort order can be specified for group members by using
2517          * {@link #setSortKey}.
2518          * @param groupKey The group key of the group.
2519          * @return this object for method chaining
2520          */
2521         public Builder setGroup(String groupKey) {
2522             mGroupKey = groupKey;
2523             return this;
2524         }
2525 
2526         /**
2527          * Set this notification to be the group summary for a group of notifications.
2528          * Grouped notifications may display in a cluster or stack on devices which
2529          * support such rendering. Requires a group key also be set using {@link #setGroup}.
2530          * @param isGroupSummary Whether this notification should be a group summary.
2531          * @return this object for method chaining
2532          */
2533         public Builder setGroupSummary(boolean isGroupSummary) {
2534             setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
2535             return this;
2536         }
2537 
2538         /**
2539          * Set a sort key that orders this notification among other notifications from the
2540          * same package. This can be useful if an external sort was already applied and an app
2541          * would like to preserve this. Notifications will be sorted lexicographically using this
2542          * value, although providing different priorities in addition to providing sort key may
2543          * cause this value to be ignored.
2544          *
2545          * <p>This sort key can also be used to order members of a notification group. See
2546          * {@link #setGroup}.
2547          *
2548          * @see String#compareTo(String)
2549          */
2550         public Builder setSortKey(String sortKey) {
2551             mSortKey = sortKey;
2552             return this;
2553         }
2554 
2555         /**
2556          * Merge additional metadata into this notification.
2557          *
2558          * <p>Values within the Bundle will replace existing extras values in this Builder.
2559          *
2560          * @see Notification#extras
2561          */
2562         public Builder addExtras(Bundle extras) {
2563             if (extras != null) {
2564                 if (mExtras == null) {
2565                     mExtras = new Bundle(extras);
2566                 } else {
2567                     mExtras.putAll(extras);
2568                 }
2569             }
2570             return this;
2571         }
2572 
2573         /**
2574          * Set metadata for this notification.
2575          *
2576          * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
2577          * current contents are copied into the Notification each time {@link #build()} is
2578          * called.
2579          *
2580          * <p>Replaces any existing extras values with those from the provided Bundle.
2581          * Use {@link #addExtras} to merge in metadata instead.
2582          *
2583          * @see Notification#extras
2584          */
2585         public Builder setExtras(Bundle extras) {
2586             mExtras = extras;
2587             return this;
2588         }
2589 
2590         /**
2591          * Get the current metadata Bundle used by this notification Builder.
2592          *
2593          * <p>The returned Bundle is shared with this Builder.
2594          *
2595          * <p>The current contents of this Bundle are copied into the Notification each time
2596          * {@link #build()} is called.
2597          *
2598          * @see Notification#extras
2599          */
2600         public Bundle getExtras() {
2601             if (mExtras == null) {
2602                 mExtras = new Bundle();
2603             }
2604             return mExtras;
2605         }
2606 
2607         /**
2608          * Add an action to this notification. Actions are typically displayed by
2609          * the system as a button adjacent to the notification content.
2610          * <p>
2611          * Every action must have an icon (32dp square and matching the
2612          * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2613          * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2614          * <p>
2615          * A notification in its expanded form can display up to 3 actions, from left to right in
2616          * the order they were added. Actions will not be displayed when the notification is
2617          * collapsed, however, so be sure that any essential functions may be accessed by the user
2618          * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2619          *
2620          * @param icon Resource ID of a drawable that represents the action.
2621          * @param title Text describing the action.
2622          * @param intent PendingIntent to be fired when the action is invoked.
2623          */
2624         public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
2625             mActions.add(new Action(icon, safeCharSequence(title), intent));
2626             return this;
2627         }
2628 
2629         /**
2630          * Add an action to this notification. Actions are typically displayed by
2631          * the system as a button adjacent to the notification content.
2632          * <p>
2633          * Every action must have an icon (32dp square and matching the
2634          * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
2635          * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
2636          * <p>
2637          * A notification in its expanded form can display up to 3 actions, from left to right in
2638          * the order they were added. Actions will not be displayed when the notification is
2639          * collapsed, however, so be sure that any essential functions may be accessed by the user
2640          * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
2641          *
2642          * @param action The action to add.
2643          */
2644         public Builder addAction(Action action) {
2645             mActions.add(action);
2646             return this;
2647         }
2648 
2649         /**
2650          * Add a rich notification style to be applied at build time.
2651          *
2652          * @param style Object responsible for modifying the notification style.
2653          */
2654         public Builder setStyle(Style style) {
2655             if (mStyle != style) {
2656                 mStyle = style;
2657                 if (mStyle != null) {
2658                     mStyle.setBuilder(this);
2659                 }
2660             }
2661             return this;
2662         }
2663 
2664         /**
2665          * Specify the value of {@link #visibility}.
2666          *
2667          * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
2668          * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
2669          *
2670          * @return The same Builder.
2671          */
2672         public Builder setVisibility(int visibility) {
2673             mVisibility = visibility;
2674             return this;
2675         }
2676 
2677         /**
2678          * Supply a replacement Notification whose contents should be shown in insecure contexts
2679          * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
2680          * @param n A replacement notification, presumably with some or all info redacted.
2681          * @return The same Builder.
2682          */
2683         public Builder setPublicVersion(Notification n) {
2684             mPublicVersion = n;
2685             return this;
2686         }
2687 
2688         /**
2689          * Apply an extender to this notification builder. Extenders may be used to add
2690          * metadata or change options on this builder.
2691          */
2692         public Builder extend(Extender extender) {
2693             extender.extend(this);
2694             return this;
2695         }
2696 
2697         private void setFlag(int mask, boolean value) {
2698             if (value) {
2699                 mFlags |= mask;
2700             } else {
2701                 mFlags &= ~mask;
2702             }
2703         }
2704 
2705         /**
2706          * Sets {@link Notification#color}.
2707          *
2708          * @param argb The accent color to use
2709          *
2710          * @return The same Builder.
2711          */
2712         public Builder setColor(int argb) {
2713             mColor = argb;
2714             return this;
2715         }
2716 
2717         private Drawable getProfileBadgeDrawable() {
2718             // Note: This assumes that the current user can read the profile badge of the
2719             // originating user.
2720             return mContext.getPackageManager().getUserBadgeForDensity(
2721                     new UserHandle(mOriginatingUserId), 0);
2722         }
2723 
2724         private Bitmap getProfileBadge() {
2725             Drawable badge = getProfileBadgeDrawable();
2726             if (badge == null) {
2727                 return null;
2728             }
2729             final int size = mContext.getResources().getDimensionPixelSize(
2730                     R.dimen.notification_badge_size);
2731             Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
2732             Canvas canvas = new Canvas(bitmap);
2733             badge.setBounds(0, 0, size, size);
2734             badge.draw(canvas);
2735             return bitmap;
2736         }
2737 
2738         private boolean addProfileBadge(RemoteViews contentView, int resId) {
2739             Bitmap profileBadge = getProfileBadge();
2740 
2741             contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
2742             contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
2743             contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
2744 
2745             if (profileBadge != null) {
2746                 contentView.setImageViewBitmap(resId, profileBadge);
2747                 contentView.setViewVisibility(resId, View.VISIBLE);
2748 
2749                 // Make sure Line 3 is visible. As badge will be here if there
2750                 // is no text to display.
2751                 if (resId == R.id.profile_badge_line3) {
2752                     contentView.setViewVisibility(R.id.line3, View.VISIBLE);
2753                 }
2754                 return true;
2755             }
2756             return false;
2757         }
2758 
2759         private void shrinkLine3Text(RemoteViews contentView) {
2760             float subTextSize = mContext.getResources().getDimensionPixelSize(
2761                     R.dimen.notification_subtext_size);
2762             contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
2763         }
2764 
2765         private void unshrinkLine3Text(RemoteViews contentView) {
2766             float regularTextSize = mContext.getResources().getDimensionPixelSize(
2767                     com.android.internal.R.dimen.notification_text_size);
2768             contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
2769         }
2770 
2771         private void resetStandardTemplate(RemoteViews contentView) {
2772             removeLargeIconBackground(contentView);
2773             contentView.setViewPadding(R.id.icon, 0, 0, 0, 0);
2774             contentView.setImageViewResource(R.id.icon, 0);
2775             contentView.setInt(R.id.icon, "setBackgroundResource", 0);
2776             contentView.setViewVisibility(R.id.right_icon, View.GONE);
2777             contentView.setInt(R.id.right_icon, "setBackgroundResource", 0);
2778             contentView.setImageViewResource(R.id.right_icon, 0);
2779             contentView.setImageViewResource(R.id.icon, 0);
2780             contentView.setTextViewText(R.id.title, null);
2781             contentView.setTextViewText(R.id.text, null);
2782             unshrinkLine3Text(contentView);
2783             contentView.setTextViewText(R.id.text2, null);
2784             contentView.setViewVisibility(R.id.text2, View.GONE);
2785             contentView.setViewVisibility(R.id.info, View.GONE);
2786             contentView.setViewVisibility(R.id.time, View.GONE);
2787             contentView.setViewVisibility(R.id.line3, View.GONE);
2788             contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
2789             contentView.setViewVisibility(R.id.progress, View.GONE);
2790             contentView.setViewVisibility(R.id.chronometer, View.GONE);
2791             contentView.setViewVisibility(R.id.time, View.GONE);
2792         }
2793 
2794         private RemoteViews applyStandardTemplate(int resId) {
2795             return applyStandardTemplate(resId, true /* hasProgress */);
2796         }
2797 
2798         /**
2799          * @param hasProgress whether the progress bar should be shown and set
2800          */
2801         private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
2802             RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
2803 
2804             resetStandardTemplate(contentView);
2805 
2806             boolean showLine3 = false;
2807             boolean showLine2 = false;
2808             boolean contentTextInLine2 = false;
2809 
2810             if (mLargeIcon != null) {
2811                 contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
2812                 processLargeLegacyIcon(mLargeIcon, contentView);
2813                 contentView.setImageViewResource(R.id.right_icon, mSmallIcon);
2814                 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
2815                 processSmallRightIcon(mSmallIcon, contentView);
2816             } else { // small icon at left
2817                 contentView.setImageViewResource(R.id.icon, mSmallIcon);
2818                 contentView.setViewVisibility(R.id.icon, View.VISIBLE);
2819                 processSmallIconAsLarge(mSmallIcon, contentView);
2820             }
2821             if (mContentTitle != null) {
2822                 contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle));
2823             }
2824             if (mContentText != null) {
2825                 contentView.setTextViewText(R.id.text, processLegacyText(mContentText));
2826                 showLine3 = true;
2827             }
2828             if (mContentInfo != null) {
2829                 contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo));
2830                 contentView.setViewVisibility(R.id.info, View.VISIBLE);
2831                 showLine3 = true;
2832             } else if (mNumber > 0) {
2833                 final int tooBig = mContext.getResources().getInteger(
2834                         R.integer.status_bar_notification_info_maxnum);
2835                 if (mNumber > tooBig) {
2836                     contentView.setTextViewText(R.id.info, processLegacyText(
2837                             mContext.getResources().getString(
2838                                     R.string.status_bar_notification_info_overflow)));
2839                 } else {
2840                     NumberFormat f = NumberFormat.getIntegerInstance();
2841                     contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber)));
2842                 }
2843                 contentView.setViewVisibility(R.id.info, View.VISIBLE);
2844                 showLine3 = true;
2845             } else {
2846                 contentView.setViewVisibility(R.id.info, View.GONE);
2847             }
2848 
2849             // Need to show three lines?
2850             if (mSubText != null) {
2851                 contentView.setTextViewText(R.id.text, processLegacyText(mSubText));
2852                 if (mContentText != null) {
2853                     contentView.setTextViewText(R.id.text2, processLegacyText(mContentText));
2854                     contentView.setViewVisibility(R.id.text2, View.VISIBLE);
2855                     showLine2 = true;
2856                     contentTextInLine2 = true;
2857                 } else {
2858                     contentView.setViewVisibility(R.id.text2, View.GONE);
2859                 }
2860             } else {
2861                 contentView.setViewVisibility(R.id.text2, View.GONE);
2862                 if (hasProgress && (mProgressMax != 0 || mProgressIndeterminate)) {
2863                     contentView.setViewVisibility(R.id.progress, View.VISIBLE);
2864                     contentView.setProgressBar(
2865                             R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
2866                     contentView.setProgressBackgroundTintList(
2867                             R.id.progress, ColorStateList.valueOf(mContext.getResources().getColor(
2868                                     R.color.notification_progress_background_color)));
2869                     if (mColor != COLOR_DEFAULT) {
2870                         ColorStateList colorStateList = ColorStateList.valueOf(mColor);
2871                         contentView.setProgressTintList(R.id.progress, colorStateList);
2872                         contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
2873                     }
2874                     showLine2 = true;
2875                 } else {
2876                     contentView.setViewVisibility(R.id.progress, View.GONE);
2877                 }
2878             }
2879             if (showLine2) {
2880 
2881                 // need to shrink all the type to make sure everything fits
2882                 shrinkLine3Text(contentView);
2883             }
2884 
2885             if (showsTimeOrChronometer()) {
2886                 if (mUseChronometer) {
2887                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
2888                     contentView.setLong(R.id.chronometer, "setBase",
2889                             mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
2890                     contentView.setBoolean(R.id.chronometer, "setStarted", true);
2891                 } else {
2892                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
2893                     contentView.setLong(R.id.time, "setTime", mWhen);
2894                 }
2895             }
2896 
2897             // Adjust padding depending on line count and font size.
2898             contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext,
2899                     mHasThreeLines, mContext.getResources().getConfiguration().fontScale),
2900                     0, 0);
2901 
2902             // We want to add badge to first line of text.
2903             boolean addedBadge = addProfileBadge(contentView,
2904                     contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
2905             // If we added the badge to line 3 then we should show line 3.
2906             if (addedBadge && !contentTextInLine2) {
2907                 showLine3 = true;
2908             }
2909 
2910             // Note getStandardView may hide line 3 again.
2911             contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
2912             contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE);
2913             return contentView;
2914         }
2915 
2916         /**
2917          * @return true if the built notification will show the time or the chronometer; false
2918          *         otherwise
2919          */
showsTimeOrChronometer()2920         private boolean showsTimeOrChronometer() {
2921             return mWhen != 0 && mShowWhen;
2922         }
2923 
2924         /**
2925          * Logic to find out whether the notification is going to have three lines in the contracted
2926          * layout. This is used to adjust the top padding.
2927          *
2928          * @return true if the notification is going to have three lines; false if the notification
2929          *         is going to have one or two lines
2930          */
hasThreeLines()2931         private boolean hasThreeLines() {
2932             boolean contentTextInLine2 = mSubText != null && mContentText != null;
2933             boolean hasProgress = mStyle == null || mStyle.hasProgress();
2934             // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
2935             boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
2936             boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0
2937                     || badgeInLine3;
2938             boolean hasLine2 = (mSubText != null && mContentText != null) ||
2939                     (hasProgress && mSubText == null
2940                             && (mProgressMax != 0 || mProgressIndeterminate));
2941             return hasLine2 && hasLine3;
2942         }
2943 
2944         /**
2945          * @hide
2946          */
calculateTopPadding(Context ctx, boolean hasThreeLines, float fontScale)2947         public static int calculateTopPadding(Context ctx, boolean hasThreeLines,
2948                 float fontScale) {
2949             int padding = ctx.getResources().getDimensionPixelSize(hasThreeLines
2950                     ? R.dimen.notification_top_pad_narrow
2951                     : R.dimen.notification_top_pad);
2952             int largePadding = ctx.getResources().getDimensionPixelSize(hasThreeLines
2953                     ? R.dimen.notification_top_pad_large_text_narrow
2954                     : R.dimen.notification_top_pad_large_text);
2955             float largeFactor = (MathUtils.constrain(fontScale, 1.0f, LARGE_TEXT_SCALE) - 1f)
2956                     / (LARGE_TEXT_SCALE - 1f);
2957 
2958             // Linearly interpolate the padding between large and normal with the font scale ranging
2959             // from 1f to LARGE_TEXT_SCALE
2960             return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
2961         }
2962 
resetStandardTemplateWithActions(RemoteViews big)2963         private void resetStandardTemplateWithActions(RemoteViews big) {
2964             big.setViewVisibility(R.id.actions, View.GONE);
2965             big.setViewVisibility(R.id.action_divider, View.GONE);
2966             big.removeAllViews(R.id.actions);
2967         }
2968 
applyStandardTemplateWithActions(int layoutId)2969         private RemoteViews applyStandardTemplateWithActions(int layoutId) {
2970             RemoteViews big = applyStandardTemplate(layoutId);
2971 
2972             resetStandardTemplateWithActions(big);
2973 
2974             int N = mActions.size();
2975             if (N > 0) {
2976                 big.setViewVisibility(R.id.actions, View.VISIBLE);
2977                 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
2978                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
2979                 for (int i=0; i<N; i++) {
2980                     final RemoteViews button = generateActionButton(mActions.get(i));
2981                     big.addView(R.id.actions, button);
2982                 }
2983             }
2984             return big;
2985         }
2986 
makeContentView()2987         private RemoteViews makeContentView() {
2988             if (mContentView != null) {
2989                 return mContentView;
2990             } else {
2991                 return applyStandardTemplate(getBaseLayoutResource());
2992             }
2993         }
2994 
makeTickerView()2995         private RemoteViews makeTickerView() {
2996             if (mTickerView != null) {
2997                 return mTickerView;
2998             }
2999             return null; // tickers are not created by default anymore
3000         }
3001 
makeBigContentView()3002         private RemoteViews makeBigContentView() {
3003             if (mActions.size() == 0) return null;
3004 
3005             return applyStandardTemplateWithActions(getBigBaseLayoutResource());
3006         }
3007 
makeHeadsUpContentView()3008         private RemoteViews makeHeadsUpContentView() {
3009             if (mActions.size() == 0) return null;
3010 
3011             return applyStandardTemplateWithActions(getBigBaseLayoutResource());
3012         }
3013 
3014 
generateActionButton(Action action)3015         private RemoteViews generateActionButton(Action action) {
3016             final boolean tombstone = (action.actionIntent == null);
3017             RemoteViews button = new RemoteViews(mContext.getPackageName(),
3018                     tombstone ? getActionTombstoneLayoutResource()
3019                               : getActionLayoutResource());
3020             button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0);
3021             button.setTextViewText(R.id.action0, processLegacyText(action.title));
3022             if (!tombstone) {
3023                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
3024             }
3025             button.setContentDescription(R.id.action0, action.title);
3026             processLegacyAction(action, button);
3027             return button;
3028         }
3029 
3030         /**
3031          * @return Whether we are currently building a notification from a legacy (an app that
3032          *         doesn't create material notifications by itself) app.
3033          */
isLegacy()3034         private boolean isLegacy() {
3035             return mColorUtil != null;
3036         }
3037 
processLegacyAction(Action action, RemoteViews button)3038         private void processLegacyAction(Action action, RemoteViews button) {
3039             if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.icon)) {
3040                 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
3041                         mContext.getResources().getColor(R.color.notification_action_color_filter),
3042                         PorterDuff.Mode.MULTIPLY);
3043             }
3044         }
3045 
processLegacyText(CharSequence charSequence)3046         private CharSequence processLegacyText(CharSequence charSequence) {
3047             if (isLegacy()) {
3048                 return mColorUtil.invertCharSequenceColors(charSequence);
3049             } else {
3050                 return charSequence;
3051             }
3052         }
3053 
3054         /**
3055          * Apply any necessary background to smallIcons being used in the largeIcon spot.
3056          */
processSmallIconAsLarge(int largeIconId, RemoteViews contentView)3057         private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) {
3058             if (!isLegacy()) {
3059                 contentView.setDrawableParameters(R.id.icon, false, -1,
3060                         0xFFFFFFFF,
3061                         PorterDuff.Mode.SRC_ATOP, -1);
3062             }
3063             if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, largeIconId)) {
3064                 applyLargeIconBackground(contentView);
3065             }
3066         }
3067 
3068         /**
3069          * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
3070          * if it's grayscale).
3071          */
3072         // TODO: also check bounds, transparency, that sort of thing.
processLargeLegacyIcon(Bitmap largeIcon, RemoteViews contentView)3073         private void processLargeLegacyIcon(Bitmap largeIcon, RemoteViews contentView) {
3074             if (isLegacy() && mColorUtil.isGrayscaleIcon(largeIcon)) {
3075                 applyLargeIconBackground(contentView);
3076             } else {
3077                 removeLargeIconBackground(contentView);
3078             }
3079         }
3080 
3081         /**
3082          * Add a colored circle behind the largeIcon slot.
3083          */
applyLargeIconBackground(RemoteViews contentView)3084         private void applyLargeIconBackground(RemoteViews contentView) {
3085             contentView.setInt(R.id.icon, "setBackgroundResource",
3086                     R.drawable.notification_icon_legacy_bg);
3087 
3088             contentView.setDrawableParameters(
3089                     R.id.icon,
3090                     true,
3091                     -1,
3092                     resolveColor(),
3093                     PorterDuff.Mode.SRC_ATOP,
3094                     -1);
3095 
3096             int padding = mContext.getResources().getDimensionPixelSize(
3097                     R.dimen.notification_large_icon_circle_padding);
3098             contentView.setViewPadding(R.id.icon, padding, padding, padding, padding);
3099         }
3100 
removeLargeIconBackground(RemoteViews contentView)3101         private void removeLargeIconBackground(RemoteViews contentView) {
3102             contentView.setInt(R.id.icon, "setBackgroundResource", 0);
3103         }
3104 
3105         /**
3106          * Recolor small icons when used in the R.id.right_icon slot.
3107          */
processSmallRightIcon(int smallIconDrawableId, RemoteViews contentView)3108         private void processSmallRightIcon(int smallIconDrawableId,
3109                 RemoteViews contentView) {
3110             if (!isLegacy()) {
3111                 contentView.setDrawableParameters(R.id.right_icon, false, -1,
3112                         0xFFFFFFFF,
3113                         PorterDuff.Mode.SRC_ATOP, -1);
3114             }
3115             if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, smallIconDrawableId)) {
3116                 contentView.setInt(R.id.right_icon,
3117                         "setBackgroundResource",
3118                         R.drawable.notification_icon_legacy_bg);
3119 
3120                 contentView.setDrawableParameters(
3121                         R.id.right_icon,
3122                         true,
3123                         -1,
3124                         resolveColor(),
3125                         PorterDuff.Mode.SRC_ATOP,
3126                         -1);
3127             }
3128         }
3129 
sanitizeColor()3130         private int sanitizeColor() {
3131             if (mColor != COLOR_DEFAULT) {
3132                 mColor |= 0xFF000000; // no alpha for custom colors
3133             }
3134             return mColor;
3135         }
3136 
resolveColor()3137         private int resolveColor() {
3138             if (mColor == COLOR_DEFAULT) {
3139                 return mContext.getResources().getColor(R.color.notification_icon_bg_color);
3140             }
3141             return mColor;
3142         }
3143 
3144         /**
3145          * Apply the unstyled operations and return a new {@link Notification} object.
3146          * @hide
3147          */
buildUnstyled()3148         public Notification buildUnstyled() {
3149             Notification n = new Notification();
3150             n.when = mWhen;
3151             n.icon = mSmallIcon;
3152             n.iconLevel = mSmallIconLevel;
3153             n.number = mNumber;
3154 
3155             n.color = sanitizeColor();
3156 
3157             setBuilderContentView(n, makeContentView());
3158             n.contentIntent = mContentIntent;
3159             n.deleteIntent = mDeleteIntent;
3160             n.fullScreenIntent = mFullScreenIntent;
3161             n.tickerText = mTickerText;
3162             n.tickerView = makeTickerView();
3163             n.largeIcon = mLargeIcon;
3164             n.sound = mSound;
3165             n.audioStreamType = mAudioStreamType;
3166             n.audioAttributes = mAudioAttributes;
3167             n.vibrate = mVibrate;
3168             n.ledARGB = mLedArgb;
3169             n.ledOnMS = mLedOnMs;
3170             n.ledOffMS = mLedOffMs;
3171             n.defaults = mDefaults;
3172             n.flags = mFlags;
3173             setBuilderBigContentView(n, makeBigContentView());
3174             setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
3175             if (mLedOnMs != 0 || mLedOffMs != 0) {
3176                 n.flags |= FLAG_SHOW_LIGHTS;
3177             }
3178             if ((mDefaults & DEFAULT_LIGHTS) != 0) {
3179                 n.flags |= FLAG_SHOW_LIGHTS;
3180             }
3181             n.category = mCategory;
3182             n.mGroupKey = mGroupKey;
3183             n.mSortKey = mSortKey;
3184             n.priority = mPriority;
3185             if (mActions.size() > 0) {
3186                 n.actions = new Action[mActions.size()];
3187                 mActions.toArray(n.actions);
3188             }
3189             n.visibility = mVisibility;
3190 
3191             if (mPublicVersion != null) {
3192                 n.publicVersion = new Notification();
3193                 mPublicVersion.cloneInto(n.publicVersion, true);
3194             }
3195             // Note: If you're adding new fields, also update restoreFromNotitification().
3196             return n;
3197         }
3198 
3199         /**
3200          * Capture, in the provided bundle, semantic information used in the construction of
3201          * this Notification object.
3202          * @hide
3203          */
populateExtras(Bundle extras)3204         public void populateExtras(Bundle extras) {
3205             // Store original information used in the construction of this object
3206             extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
3207             extras.putParcelable(EXTRA_REBUILD_CONTEXT_APPLICATION_INFO,
3208                     mContext.getApplicationInfo());
3209             extras.putCharSequence(EXTRA_TITLE, mContentTitle);
3210             extras.putCharSequence(EXTRA_TEXT, mContentText);
3211             extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
3212             extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo);
3213             extras.putInt(EXTRA_SMALL_ICON, mSmallIcon);
3214             extras.putInt(EXTRA_PROGRESS, mProgress);
3215             extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax);
3216             extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate);
3217             extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer);
3218             extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen);
3219             if (mLargeIcon != null) {
3220                 extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
3221             }
3222             if (!mPeople.isEmpty()) {
3223                 extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
3224             }
3225             // NOTE: If you're adding new extras also update restoreFromNotification().
3226         }
3227 
3228 
3229         /**
3230          * @hide
3231          */
stripForDelivery(Notification n)3232         public static void stripForDelivery(Notification n) {
3233             if (!STRIP_AND_REBUILD) {
3234                 return;
3235             }
3236 
3237             String templateClass = n.extras.getString(EXTRA_TEMPLATE);
3238             // Only strip views for known Styles because we won't know how to
3239             // re-create them otherwise.
3240             boolean stripViews = TextUtils.isEmpty(templateClass) ||
3241                     getNotificationStyleClass(templateClass) != null;
3242 
3243             boolean isStripped = false;
3244 
3245             if (n.largeIcon != null && n.extras.containsKey(EXTRA_LARGE_ICON)) {
3246                 // TODO: Would like to check for equality here, but if the notification
3247                 // has been cloned, we can't.
3248                 n.largeIcon = null;
3249                 n.extras.putBoolean(EXTRA_REBUILD_LARGE_ICON, true);
3250                 isStripped = true;
3251             }
3252             // Get rid of unmodified BuilderRemoteViews.
3253 
3254             if (stripViews &&
3255                     n.contentView instanceof BuilderRemoteViews &&
3256                     n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
3257                             n.contentView.getSequenceNumber()) {
3258                 n.contentView = null;
3259                 n.extras.putBoolean(EXTRA_REBUILD_CONTENT_VIEW, true);
3260                 n.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
3261                 isStripped = true;
3262             }
3263             if (stripViews &&
3264                     n.bigContentView instanceof BuilderRemoteViews &&
3265                     n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
3266                             n.bigContentView.getSequenceNumber()) {
3267                 n.bigContentView = null;
3268                 n.extras.putBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW, true);
3269                 n.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
3270                 isStripped = true;
3271             }
3272             if (stripViews &&
3273                     n.headsUpContentView instanceof BuilderRemoteViews &&
3274                     n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
3275                             n.headsUpContentView.getSequenceNumber()) {
3276                 n.headsUpContentView = null;
3277                 n.extras.putBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW, true);
3278                 n.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
3279                 isStripped = true;
3280             }
3281 
3282             if (isStripped) {
3283                 n.extras.putBoolean(EXTRA_NEEDS_REBUILD, true);
3284             }
3285         }
3286 
3287         /**
3288          * @hide
3289          */
rebuild(Context context, Notification n)3290         public static Notification rebuild(Context context, Notification n) {
3291             Bundle extras = n.extras;
3292             if (!extras.getBoolean(EXTRA_NEEDS_REBUILD)) return n;
3293             extras.remove(EXTRA_NEEDS_REBUILD);
3294 
3295             // Re-create notification context so we can access app resources.
3296             ApplicationInfo applicationInfo = extras.getParcelable(
3297                     EXTRA_REBUILD_CONTEXT_APPLICATION_INFO);
3298             Context builderContext;
3299             try {
3300                 builderContext = context.createApplicationContext(applicationInfo,
3301                         Context.CONTEXT_RESTRICTED);
3302             } catch (NameNotFoundException e) {
3303                 Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
3304                 builderContext = context;  // try with our context
3305             }
3306 
3307             Builder b = new Builder(builderContext, n);
3308             return b.rebuild();
3309         }
3310 
3311         /**
3312          * Rebuilds the notification passed in to the rebuild-constructor
3313          * {@link #Builder(Context, Notification)}.
3314          *
3315          * <p>
3316          * Throws IllegalStateException when invoked on a Builder that isn't in rebuild mode.
3317          *
3318          * @hide
3319          */
rebuild()3320         private Notification rebuild() {
3321             if (mRebuildNotification == null) {
3322                 throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
3323             }
3324             mHasThreeLines = hasThreeLines();
3325 
3326             Bundle extras = mRebuildNotification.extras;
3327 
3328             if (extras.getBoolean(EXTRA_REBUILD_LARGE_ICON)) {
3329                 mRebuildNotification.largeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
3330             }
3331             extras.remove(EXTRA_REBUILD_LARGE_ICON);
3332 
3333             if (extras.getBoolean(EXTRA_REBUILD_CONTENT_VIEW)) {
3334                 setBuilderContentView(mRebuildNotification, makeContentView());
3335                 if (mStyle != null) {
3336                     mStyle.populateContentView(mRebuildNotification);
3337                 }
3338             }
3339             extras.remove(EXTRA_REBUILD_CONTENT_VIEW);
3340 
3341             if (extras.getBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW)) {
3342                 setBuilderBigContentView(mRebuildNotification, makeBigContentView());
3343                 if (mStyle != null) {
3344                     mStyle.populateBigContentView(mRebuildNotification);
3345                 }
3346             }
3347             extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW);
3348 
3349             if (extras.getBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW)) {
3350                 setBuilderHeadsUpContentView(mRebuildNotification, makeHeadsUpContentView());
3351                 if (mStyle != null) {
3352                     mStyle.populateHeadsUpContentView(mRebuildNotification);
3353                 }
3354             }
3355             extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
3356 
3357             mHasThreeLines = false;
3358             return mRebuildNotification;
3359         }
3360 
getNotificationStyleClass(String templateClass)3361         private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
3362             Class<? extends Style>[] classes = new Class[]{
3363                     BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
3364             for (Class<? extends Style> innerClass : classes) {
3365                 if (templateClass.equals(innerClass.getName())) {
3366                     return innerClass;
3367                 }
3368             }
3369             return null;
3370         }
3371 
setBuilderContentView(Notification n, RemoteViews contentView)3372         private void setBuilderContentView(Notification n, RemoteViews contentView) {
3373             n.contentView = contentView;
3374             if (contentView instanceof BuilderRemoteViews) {
3375                 mRebuildBundle.putInt(Builder.EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
3376                         contentView.getSequenceNumber());
3377             }
3378         }
3379 
setBuilderBigContentView(Notification n, RemoteViews bigContentView)3380         private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
3381             n.bigContentView = bigContentView;
3382             if (bigContentView instanceof BuilderRemoteViews) {
3383                 mRebuildBundle.putInt(Builder.EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
3384                         bigContentView.getSequenceNumber());
3385             }
3386         }
3387 
setBuilderHeadsUpContentView(Notification n, RemoteViews headsUpContentView)3388         private void setBuilderHeadsUpContentView(Notification n,
3389                 RemoteViews headsUpContentView) {
3390             n.headsUpContentView = headsUpContentView;
3391             if (headsUpContentView instanceof BuilderRemoteViews) {
3392                 mRebuildBundle.putInt(Builder.EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
3393                         headsUpContentView.getSequenceNumber());
3394             }
3395         }
3396 
restoreFromNotification(Notification n)3397         private void restoreFromNotification(Notification n) {
3398 
3399             // Notification fields.
3400             mWhen = n.when;
3401             mSmallIcon = n.icon;
3402             mSmallIconLevel = n.iconLevel;
3403             mNumber = n.number;
3404 
3405             mColor = n.color;
3406 
3407             mContentView = n.contentView;
3408             mDeleteIntent = n.deleteIntent;
3409             mFullScreenIntent = n.fullScreenIntent;
3410             mTickerText = n.tickerText;
3411             mTickerView = n.tickerView;
3412             mLargeIcon = n.largeIcon;
3413             mSound = n.sound;
3414             mAudioStreamType = n.audioStreamType;
3415             mAudioAttributes = n.audioAttributes;
3416 
3417             mVibrate = n.vibrate;
3418             mLedArgb = n.ledARGB;
3419             mLedOnMs = n.ledOnMS;
3420             mLedOffMs = n.ledOffMS;
3421             mDefaults = n.defaults;
3422             mFlags = n.flags;
3423 
3424             mCategory = n.category;
3425             mGroupKey = n.mGroupKey;
3426             mSortKey = n.mSortKey;
3427             mPriority = n.priority;
3428             mActions.clear();
3429             if (n.actions != null) {
3430                 Collections.addAll(mActions, n.actions);
3431             }
3432             mVisibility = n.visibility;
3433 
3434             mPublicVersion = n.publicVersion;
3435 
3436             // Extras.
3437             Bundle extras = n.extras;
3438             mOriginatingUserId = extras.getInt(EXTRA_ORIGINATING_USERID);
3439             mContentTitle = extras.getCharSequence(EXTRA_TITLE);
3440             mContentText = extras.getCharSequence(EXTRA_TEXT);
3441             mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
3442             mContentInfo = extras.getCharSequence(EXTRA_INFO_TEXT);
3443             mSmallIcon = extras.getInt(EXTRA_SMALL_ICON);
3444             mProgress = extras.getInt(EXTRA_PROGRESS);
3445             mProgressMax = extras.getInt(EXTRA_PROGRESS_MAX);
3446             mProgressIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
3447             mUseChronometer = extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
3448             mShowWhen = extras.getBoolean(EXTRA_SHOW_WHEN);
3449             if (extras.containsKey(EXTRA_LARGE_ICON)) {
3450                 mLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
3451             }
3452             if (extras.containsKey(EXTRA_PEOPLE)) {
3453                 mPeople.clear();
3454                 Collections.addAll(mPeople, extras.getStringArray(EXTRA_PEOPLE));
3455             }
3456         }
3457 
3458         /**
3459          * @deprecated Use {@link #build()} instead.
3460          */
3461         @Deprecated
getNotification()3462         public Notification getNotification() {
3463             return build();
3464         }
3465 
3466         /**
3467          * Combine all of the options that have been set and return a new {@link Notification}
3468          * object.
3469          */
build()3470         public Notification build() {
3471             mOriginatingUserId = mContext.getUserId();
3472             mHasThreeLines = hasThreeLines();
3473 
3474             Notification n = buildUnstyled();
3475 
3476             if (mStyle != null) {
3477                 n = mStyle.buildStyled(n);
3478             }
3479 
3480             if (mExtras != null) {
3481                 n.extras.putAll(mExtras);
3482             }
3483 
3484             if (mRebuildBundle.size() > 0) {
3485                 n.extras.putAll(mRebuildBundle);
3486                 mRebuildBundle.clear();
3487             }
3488 
3489             populateExtras(n.extras);
3490             if (mStyle != null) {
3491                 mStyle.addExtras(n.extras);
3492             }
3493 
3494             mHasThreeLines = false;
3495             return n;
3496         }
3497 
3498         /**
3499          * Apply this Builder to an existing {@link Notification} object.
3500          *
3501          * @hide
3502          */
buildInto(Notification n)3503         public Notification buildInto(Notification n) {
3504             build().cloneInto(n, true);
3505             return n;
3506         }
3507 
getBaseLayoutResource()3508         private int getBaseLayoutResource() {
3509             return R.layout.notification_template_material_base;
3510         }
3511 
getBigBaseLayoutResource()3512         private int getBigBaseLayoutResource() {
3513             return R.layout.notification_template_material_big_base;
3514         }
3515 
getBigPictureLayoutResource()3516         private int getBigPictureLayoutResource() {
3517             return R.layout.notification_template_material_big_picture;
3518         }
3519 
getBigTextLayoutResource()3520         private int getBigTextLayoutResource() {
3521             return R.layout.notification_template_material_big_text;
3522         }
3523 
getInboxLayoutResource()3524         private int getInboxLayoutResource() {
3525             return R.layout.notification_template_material_inbox;
3526         }
3527 
getActionLayoutResource()3528         private int getActionLayoutResource() {
3529             return R.layout.notification_material_action;
3530         }
3531 
getActionTombstoneLayoutResource()3532         private int getActionTombstoneLayoutResource() {
3533             return R.layout.notification_material_action_tombstone;
3534         }
3535     }
3536 
3537     /**
3538      * An object that can apply a rich notification style to a {@link Notification.Builder}
3539      * object.
3540      */
3541     public static abstract class Style {
3542         private CharSequence mBigContentTitle;
3543 
3544         /**
3545          * @hide
3546          */
3547         protected CharSequence mSummaryText = null;
3548 
3549         /**
3550          * @hide
3551          */
3552         protected boolean mSummaryTextSet = false;
3553 
3554         protected Builder mBuilder;
3555 
3556         /**
3557          * Overrides ContentTitle in the big form of the template.
3558          * This defaults to the value passed to setContentTitle().
3559          */
internalSetBigContentTitle(CharSequence title)3560         protected void internalSetBigContentTitle(CharSequence title) {
3561             mBigContentTitle = title;
3562         }
3563 
3564         /**
3565          * Set the first line of text after the detail section in the big form of the template.
3566          */
internalSetSummaryText(CharSequence cs)3567         protected void internalSetSummaryText(CharSequence cs) {
3568             mSummaryText = cs;
3569             mSummaryTextSet = true;
3570         }
3571 
setBuilder(Builder builder)3572         public void setBuilder(Builder builder) {
3573             if (mBuilder != builder) {
3574                 mBuilder = builder;
3575                 if (mBuilder != null) {
3576                     mBuilder.setStyle(this);
3577                 }
3578             }
3579         }
3580 
checkBuilder()3581         protected void checkBuilder() {
3582             if (mBuilder == null) {
3583                 throw new IllegalArgumentException("Style requires a valid Builder object");
3584             }
3585         }
3586 
getStandardView(int layoutId)3587         protected RemoteViews getStandardView(int layoutId) {
3588             checkBuilder();
3589 
3590             // Nasty.
3591             CharSequence oldBuilderContentTitle = mBuilder.mContentTitle;
3592             if (mBigContentTitle != null) {
3593                 mBuilder.setContentTitle(mBigContentTitle);
3594             }
3595 
3596             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
3597 
3598             mBuilder.mContentTitle = oldBuilderContentTitle;
3599 
3600             if (mBigContentTitle != null && mBigContentTitle.equals("")) {
3601                 contentView.setViewVisibility(R.id.line1, View.GONE);
3602             } else {
3603                 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
3604             }
3605 
3606             // The last line defaults to the subtext, but can be replaced by mSummaryText
3607             final CharSequence overflowText =
3608                     mSummaryTextSet ? mSummaryText
3609                                     : mBuilder.mSubText;
3610             if (overflowText != null) {
3611                 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
3612                 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
3613                 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
3614             } else {
3615                 // Clear text in case we use the line to show the profile badge.
3616                 contentView.setTextViewText(R.id.text, "");
3617                 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
3618                 contentView.setViewVisibility(R.id.line3, View.GONE);
3619             }
3620 
3621             return contentView;
3622         }
3623 
3624         /**
3625          * Changes the padding of the first line such that the big and small content view have the
3626          * same top padding.
3627          *
3628          * @hide
3629          */
applyTopPadding(RemoteViews contentView)3630         protected void applyTopPadding(RemoteViews contentView) {
3631             int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
3632                     mBuilder.mHasThreeLines,
3633                     mBuilder.mContext.getResources().getConfiguration().fontScale);
3634             contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
3635         }
3636 
3637         /**
3638          * @hide
3639          */
addExtras(Bundle extras)3640         public void addExtras(Bundle extras) {
3641             if (mSummaryTextSet) {
3642                 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
3643             }
3644             if (mBigContentTitle != null) {
3645                 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
3646             }
3647             extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
3648         }
3649 
3650         /**
3651          * @hide
3652          */
restoreFromExtras(Bundle extras)3653         protected void restoreFromExtras(Bundle extras) {
3654             if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
3655                 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
3656                 mSummaryTextSet = true;
3657             }
3658             if (extras.containsKey(EXTRA_TITLE_BIG)) {
3659                 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
3660             }
3661         }
3662 
3663 
3664         /**
3665          * @hide
3666          */
buildStyled(Notification wip)3667         public Notification buildStyled(Notification wip) {
3668             populateTickerView(wip);
3669             populateContentView(wip);
3670             populateBigContentView(wip);
3671             populateHeadsUpContentView(wip);
3672             return wip;
3673         }
3674 
3675         // The following methods are split out so we can re-create notification partially.
3676         /**
3677          * @hide
3678          */
populateTickerView(Notification wip)3679         protected void populateTickerView(Notification wip) {}
3680         /**
3681          * @hide
3682          */
populateContentView(Notification wip)3683         protected void populateContentView(Notification wip) {}
3684 
3685         /**
3686          * @hide
3687          */
populateBigContentView(Notification wip)3688         protected void populateBigContentView(Notification wip) {}
3689 
3690         /**
3691          * @hide
3692          */
populateHeadsUpContentView(Notification wip)3693         protected void populateHeadsUpContentView(Notification wip) {}
3694 
3695         /**
3696          * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
3697          * attached to.
3698          *
3699          * @return the fully constructed Notification.
3700          */
build()3701         public Notification build() {
3702             checkBuilder();
3703             return mBuilder.build();
3704         }
3705 
3706         /**
3707          * @hide
3708          * @return true if the style positions the progress bar on the second line; false if the
3709          *         style hides the progress bar
3710          */
hasProgress()3711         protected boolean hasProgress() {
3712             return true;
3713         }
3714     }
3715 
3716     /**
3717      * Helper class for generating large-format notifications that include a large image attachment.
3718      *
3719      * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
3720      * <pre class="prettyprint">
3721      * Notification notif = new Notification.Builder(mContext)
3722      *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
3723      *     .setContentText(subject)
3724      *     .setSmallIcon(R.drawable.new_post)
3725      *     .setLargeIcon(aBitmap)
3726      *     .setStyle(new Notification.BigPictureStyle()
3727      *         .bigPicture(aBigBitmap))
3728      *     .build();
3729      * </pre>
3730      *
3731      * @see Notification#bigContentView
3732      */
3733     public static class BigPictureStyle extends Style {
3734         private Bitmap mPicture;
3735         private Bitmap mBigLargeIcon;
3736         private boolean mBigLargeIconSet = false;
3737 
BigPictureStyle()3738         public BigPictureStyle() {
3739         }
3740 
BigPictureStyle(Builder builder)3741         public BigPictureStyle(Builder builder) {
3742             setBuilder(builder);
3743         }
3744 
3745         /**
3746          * Overrides ContentTitle in the big form of the template.
3747          * This defaults to the value passed to setContentTitle().
3748          */
setBigContentTitle(CharSequence title)3749         public BigPictureStyle setBigContentTitle(CharSequence title) {
3750             internalSetBigContentTitle(safeCharSequence(title));
3751             return this;
3752         }
3753 
3754         /**
3755          * Set the first line of text after the detail section in the big form of the template.
3756          */
setSummaryText(CharSequence cs)3757         public BigPictureStyle setSummaryText(CharSequence cs) {
3758             internalSetSummaryText(safeCharSequence(cs));
3759             return this;
3760         }
3761 
3762         /**
3763          * Provide the bitmap to be used as the payload for the BigPicture notification.
3764          */
bigPicture(Bitmap b)3765         public BigPictureStyle bigPicture(Bitmap b) {
3766             mPicture = b;
3767             return this;
3768         }
3769 
3770         /**
3771          * Override the large icon when the big notification is shown.
3772          */
bigLargeIcon(Bitmap b)3773         public BigPictureStyle bigLargeIcon(Bitmap b) {
3774             mBigLargeIconSet = true;
3775             mBigLargeIcon = b;
3776             return this;
3777         }
3778 
makeBigContentView()3779         private RemoteViews makeBigContentView() {
3780 
3781             // Replace mLargeIcon with mBigLargeIcon if mBigLargeIconSet
3782             // This covers the following cases:
3783             //   1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
3784             //          mLargeIcon
3785             //   2. !mBigLargeIconSet -> mLargeIcon applies
3786             Bitmap oldLargeIcon = null;
3787             if (mBigLargeIconSet) {
3788                 oldLargeIcon = mBuilder.mLargeIcon;
3789                 mBuilder.mLargeIcon = mBigLargeIcon;
3790             }
3791 
3792             RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
3793 
3794             if (mBigLargeIconSet) {
3795                 mBuilder.mLargeIcon = oldLargeIcon;
3796             }
3797 
3798             contentView.setImageViewBitmap(R.id.big_picture, mPicture);
3799 
3800             applyTopPadding(contentView);
3801 
3802             boolean twoTextLines = mBuilder.mSubText != null && mBuilder.mContentText != null;
3803             mBuilder.addProfileBadge(contentView,
3804                     twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
3805             return contentView;
3806         }
3807 
3808         /**
3809          * @hide
3810          */
addExtras(Bundle extras)3811         public void addExtras(Bundle extras) {
3812             super.addExtras(extras);
3813 
3814             if (mBigLargeIconSet) {
3815                 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
3816             }
3817             extras.putParcelable(EXTRA_PICTURE, mPicture);
3818         }
3819 
3820         /**
3821          * @hide
3822          */
3823         @Override
restoreFromExtras(Bundle extras)3824         protected void restoreFromExtras(Bundle extras) {
3825             super.restoreFromExtras(extras);
3826 
3827             if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
3828                 mBigLargeIconSet = true;
3829                 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
3830             }
3831             mPicture = extras.getParcelable(EXTRA_PICTURE);
3832         }
3833 
3834         /**
3835          * @hide
3836          */
3837         @Override
populateBigContentView(Notification wip)3838         public void populateBigContentView(Notification wip) {
3839             mBuilder.setBuilderBigContentView(wip, makeBigContentView());
3840         }
3841     }
3842 
3843     /**
3844      * Helper class for generating large-format notifications that include a lot of text.
3845      *
3846      * Here's how you'd set the <code>BigTextStyle</code> on a notification:
3847      * <pre class="prettyprint">
3848      * Notification notif = new Notification.Builder(mContext)
3849      *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
3850      *     .setContentText(subject)
3851      *     .setSmallIcon(R.drawable.new_mail)
3852      *     .setLargeIcon(aBitmap)
3853      *     .setStyle(new Notification.BigTextStyle()
3854      *         .bigText(aVeryLongString))
3855      *     .build();
3856      * </pre>
3857      *
3858      * @see Notification#bigContentView
3859      */
3860     public static class BigTextStyle extends Style {
3861 
3862         private static final int MAX_LINES = 13;
3863         private static final int LINES_CONSUMED_BY_ACTIONS = 3;
3864         private static final int LINES_CONSUMED_BY_SUMMARY = 2;
3865 
3866         private CharSequence mBigText;
3867 
BigTextStyle()3868         public BigTextStyle() {
3869         }
3870 
BigTextStyle(Builder builder)3871         public BigTextStyle(Builder builder) {
3872             setBuilder(builder);
3873         }
3874 
3875         /**
3876          * Overrides ContentTitle in the big form of the template.
3877          * This defaults to the value passed to setContentTitle().
3878          */
setBigContentTitle(CharSequence title)3879         public BigTextStyle setBigContentTitle(CharSequence title) {
3880             internalSetBigContentTitle(safeCharSequence(title));
3881             return this;
3882         }
3883 
3884         /**
3885          * Set the first line of text after the detail section in the big form of the template.
3886          */
setSummaryText(CharSequence cs)3887         public BigTextStyle setSummaryText(CharSequence cs) {
3888             internalSetSummaryText(safeCharSequence(cs));
3889             return this;
3890         }
3891 
3892         /**
3893          * Provide the longer text to be displayed in the big form of the
3894          * template in place of the content text.
3895          */
bigText(CharSequence cs)3896         public BigTextStyle bigText(CharSequence cs) {
3897             mBigText = safeCharSequence(cs);
3898             return this;
3899         }
3900 
3901         /**
3902          * @hide
3903          */
addExtras(Bundle extras)3904         public void addExtras(Bundle extras) {
3905             super.addExtras(extras);
3906 
3907             extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
3908         }
3909 
3910         /**
3911          * @hide
3912          */
3913         @Override
restoreFromExtras(Bundle extras)3914         protected void restoreFromExtras(Bundle extras) {
3915             super.restoreFromExtras(extras);
3916 
3917             mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
3918         }
3919 
makeBigContentView()3920         private RemoteViews makeBigContentView() {
3921 
3922             // Nasty
3923             CharSequence oldBuilderContentText = mBuilder.mContentText;
3924             mBuilder.mContentText = null;
3925 
3926             RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
3927 
3928             mBuilder.mContentText = oldBuilderContentText;
3929 
3930             contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
3931             contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
3932             contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
3933             contentView.setViewVisibility(R.id.text2, View.GONE);
3934 
3935             applyTopPadding(contentView);
3936 
3937             mBuilder.shrinkLine3Text(contentView);
3938 
3939             mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
3940 
3941             return contentView;
3942         }
3943 
calculateMaxLines()3944         private int calculateMaxLines() {
3945             int lineCount = MAX_LINES;
3946             boolean hasActions = mBuilder.mActions.size() > 0;
3947             boolean hasSummary = (mSummaryTextSet ? mSummaryText : mBuilder.mSubText) != null;
3948             if (hasActions) {
3949                 lineCount -= LINES_CONSUMED_BY_ACTIONS;
3950             }
3951             if (hasSummary) {
3952                 lineCount -= LINES_CONSUMED_BY_SUMMARY;
3953             }
3954 
3955             // If we have less top padding at the top, we can fit less lines.
3956             if (!mBuilder.mHasThreeLines) {
3957                 lineCount--;
3958             }
3959             return lineCount;
3960         }
3961 
3962         /**
3963          * @hide
3964          */
3965         @Override
populateBigContentView(Notification wip)3966         public void populateBigContentView(Notification wip) {
3967             mBuilder.setBuilderBigContentView(wip, makeBigContentView());
3968         }
3969     }
3970 
3971     /**
3972      * Helper class for generating large-format notifications that include a list of (up to 5) strings.
3973      *
3974      * Here's how you'd set the <code>InboxStyle</code> on a notification:
3975      * <pre class="prettyprint">
3976      * Notification notif = new Notification.Builder(mContext)
3977      *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
3978      *     .setContentText(subject)
3979      *     .setSmallIcon(R.drawable.new_mail)
3980      *     .setLargeIcon(aBitmap)
3981      *     .setStyle(new Notification.InboxStyle()
3982      *         .addLine(str1)
3983      *         .addLine(str2)
3984      *         .setContentTitle(&quot;&quot;)
3985      *         .setSummaryText(&quot;+3 more&quot;))
3986      *     .build();
3987      * </pre>
3988      *
3989      * @see Notification#bigContentView
3990      */
3991     public static class InboxStyle extends Style {
3992         private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
3993 
InboxStyle()3994         public InboxStyle() {
3995         }
3996 
InboxStyle(Builder builder)3997         public InboxStyle(Builder builder) {
3998             setBuilder(builder);
3999         }
4000 
4001         /**
4002          * Overrides ContentTitle in the big form of the template.
4003          * This defaults to the value passed to setContentTitle().
4004          */
setBigContentTitle(CharSequence title)4005         public InboxStyle setBigContentTitle(CharSequence title) {
4006             internalSetBigContentTitle(safeCharSequence(title));
4007             return this;
4008         }
4009 
4010         /**
4011          * Set the first line of text after the detail section in the big form of the template.
4012          */
setSummaryText(CharSequence cs)4013         public InboxStyle setSummaryText(CharSequence cs) {
4014             internalSetSummaryText(safeCharSequence(cs));
4015             return this;
4016         }
4017 
4018         /**
4019          * Append a line to the digest section of the Inbox notification.
4020          */
addLine(CharSequence cs)4021         public InboxStyle addLine(CharSequence cs) {
4022             mTexts.add(safeCharSequence(cs));
4023             return this;
4024         }
4025 
4026         /**
4027          * @hide
4028          */
addExtras(Bundle extras)4029         public void addExtras(Bundle extras) {
4030             super.addExtras(extras);
4031 
4032             CharSequence[] a = new CharSequence[mTexts.size()];
4033             extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
4034         }
4035 
4036         /**
4037          * @hide
4038          */
4039         @Override
restoreFromExtras(Bundle extras)4040         protected void restoreFromExtras(Bundle extras) {
4041             super.restoreFromExtras(extras);
4042 
4043             mTexts.clear();
4044             if (extras.containsKey(EXTRA_TEXT_LINES)) {
4045                 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
4046             }
4047         }
4048 
makeBigContentView()4049         private RemoteViews makeBigContentView() {
4050             // Remove the content text so line3 disappears unless you have a summary
4051 
4052             // Nasty
4053             CharSequence oldBuilderContentText = mBuilder.mContentText;
4054             mBuilder.mContentText = null;
4055 
4056             RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
4057 
4058             mBuilder.mContentText = oldBuilderContentText;
4059 
4060             contentView.setViewVisibility(R.id.text2, View.GONE);
4061 
4062             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
4063                     R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
4064 
4065             // Make sure all rows are gone in case we reuse a view.
4066             for (int rowId : rowIds) {
4067                 contentView.setViewVisibility(rowId, View.GONE);
4068             }
4069 
4070             final boolean largeText =
4071                     mBuilder.mContext.getResources().getConfiguration().fontScale > 1f;
4072             final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
4073                     R.dimen.notification_subtext_size);
4074             int i=0;
4075             while (i < mTexts.size() && i < rowIds.length) {
4076                 CharSequence str = mTexts.get(i);
4077                 if (str != null && !str.equals("")) {
4078                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
4079                     contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
4080                     if (largeText) {
4081                         contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
4082                                 subTextSize);
4083                     }
4084                 }
4085                 i++;
4086             }
4087 
4088             contentView.setViewVisibility(R.id.inbox_end_pad,
4089                     mTexts.size() > 0 ? View.VISIBLE : View.GONE);
4090 
4091             contentView.setViewVisibility(R.id.inbox_more,
4092                     mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
4093 
4094             applyTopPadding(contentView);
4095 
4096             mBuilder.shrinkLine3Text(contentView);
4097 
4098             mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
4099 
4100             return contentView;
4101         }
4102 
4103         /**
4104          * @hide
4105          */
4106         @Override
populateBigContentView(Notification wip)4107         public void populateBigContentView(Notification wip) {
4108             mBuilder.setBuilderBigContentView(wip, makeBigContentView());
4109         }
4110     }
4111 
4112     /**
4113      * Notification style for media playback notifications.
4114      *
4115      * In the expanded form, {@link Notification#bigContentView}, up to 5
4116      * {@link Notification.Action}s specified with
4117      * {@link Notification.Builder#addAction(int, CharSequence, PendingIntent) addAction} will be
4118      * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
4119      * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
4120      * treated as album artwork.
4121      *
4122      * Unlike the other styles provided here, MediaStyle can also modify the standard-size
4123      * {@link Notification#contentView}; by providing action indices to
4124      * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
4125      * in the standard view alongside the usual content.
4126      *
4127      * Notifications created with MediaStyle will have their category set to
4128      * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
4129      * category using {@link Notification.Builder#setCategory(String) setCategory()}.
4130      *
4131      * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
4132      * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
4133      * the System UI can identify this as a notification representing an active media session
4134      * and respond accordingly (by showing album artwork in the lockscreen, for example).
4135      *
4136      * To use this style with your Notification, feed it to
4137      * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
4138      * <pre class="prettyprint">
4139      * Notification noti = new Notification.Builder()
4140      *     .setSmallIcon(R.drawable.ic_stat_player)
4141      *     .setContentTitle(&quot;Track title&quot;)
4142      *     .setContentText(&quot;Artist - Album&quot;)
4143      *     .setLargeIcon(albumArtBitmap))
4144      *     .setStyle(<b>new Notification.MediaStyle()</b>
4145      *         .setMediaSession(mySession))
4146      *     .build();
4147      * </pre>
4148      *
4149      * @see Notification#bigContentView
4150      */
4151     public static class MediaStyle extends Style {
4152         static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
4153         static final int MAX_MEDIA_BUTTONS = 5;
4154 
4155         private int[] mActionsToShowInCompact = null;
4156         private MediaSession.Token mToken;
4157 
MediaStyle()4158         public MediaStyle() {
4159         }
4160 
MediaStyle(Builder builder)4161         public MediaStyle(Builder builder) {
4162             setBuilder(builder);
4163         }
4164 
4165         /**
4166          * Request up to 3 actions (by index in the order of addition) to be shown in the compact
4167          * notification view.
4168          *
4169          * @param actions the indices of the actions to show in the compact notification view
4170          */
setShowActionsInCompactView(int...actions)4171         public MediaStyle setShowActionsInCompactView(int...actions) {
4172             mActionsToShowInCompact = actions;
4173             return this;
4174         }
4175 
4176         /**
4177          * Attach a {@link android.media.session.MediaSession.Token} to this Notification
4178          * to provide additional playback information and control to the SystemUI.
4179          */
setMediaSession(MediaSession.Token token)4180         public MediaStyle setMediaSession(MediaSession.Token token) {
4181             mToken = token;
4182             return this;
4183         }
4184 
4185         /**
4186          * @hide
4187          */
4188         @Override
buildStyled(Notification wip)4189         public Notification buildStyled(Notification wip) {
4190             super.buildStyled(wip);
4191             if (wip.category == null) {
4192                 wip.category = Notification.CATEGORY_TRANSPORT;
4193             }
4194             return wip;
4195         }
4196 
4197         /**
4198          * @hide
4199          */
4200         @Override
populateContentView(Notification wip)4201         public void populateContentView(Notification wip) {
4202             mBuilder.setBuilderContentView(wip, makeMediaContentView());
4203         }
4204 
4205         /**
4206          * @hide
4207          */
4208         @Override
populateBigContentView(Notification wip)4209         public void populateBigContentView(Notification wip) {
4210             mBuilder.setBuilderBigContentView(wip, makeMediaBigContentView());
4211         }
4212 
4213         /** @hide */
4214         @Override
addExtras(Bundle extras)4215         public void addExtras(Bundle extras) {
4216             super.addExtras(extras);
4217 
4218             if (mToken != null) {
4219                 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
4220             }
4221             if (mActionsToShowInCompact != null) {
4222                 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
4223             }
4224         }
4225 
4226         /**
4227          * @hide
4228          */
4229         @Override
restoreFromExtras(Bundle extras)4230         protected void restoreFromExtras(Bundle extras) {
4231             super.restoreFromExtras(extras);
4232 
4233             if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
4234                 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
4235             }
4236             if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
4237                 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
4238             }
4239         }
4240 
generateMediaActionButton(Action action)4241         private RemoteViews generateMediaActionButton(Action action) {
4242             final boolean tombstone = (action.actionIntent == null);
4243             RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
4244                     R.layout.notification_material_media_action);
4245             button.setImageViewResource(R.id.action0, action.icon);
4246             button.setDrawableParameters(R.id.action0, false, -1,
4247                     0xFFFFFFFF,
4248                     PorterDuff.Mode.SRC_ATOP, -1);
4249             if (!tombstone) {
4250                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4251             }
4252             button.setContentDescription(R.id.action0, action.title);
4253             return button;
4254         }
4255 
makeMediaContentView()4256         private RemoteViews makeMediaContentView() {
4257             RemoteViews view = mBuilder.applyStandardTemplate(
4258                     R.layout.notification_template_material_media, false /* hasProgress */);
4259 
4260             final int numActions = mBuilder.mActions.size();
4261             final int N = mActionsToShowInCompact == null
4262                     ? 0
4263                     : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
4264             if (N > 0) {
4265                 view.removeAllViews(com.android.internal.R.id.media_actions);
4266                 for (int i = 0; i < N; i++) {
4267                     if (i >= numActions) {
4268                         throw new IllegalArgumentException(String.format(
4269                                 "setShowActionsInCompactView: action %d out of bounds (max %d)",
4270                                 i, numActions - 1));
4271                     }
4272 
4273                     final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
4274                     final RemoteViews button = generateMediaActionButton(action);
4275                     view.addView(com.android.internal.R.id.media_actions, button);
4276                 }
4277             }
4278             styleText(view);
4279             hideRightIcon(view);
4280             return view;
4281         }
4282 
makeMediaBigContentView()4283         private RemoteViews makeMediaBigContentView() {
4284             final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
4285             RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount),
4286                     false /* hasProgress */);
4287 
4288             if (actionCount > 0) {
4289                 big.removeAllViews(com.android.internal.R.id.media_actions);
4290                 for (int i = 0; i < actionCount; i++) {
4291                     final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
4292                     big.addView(com.android.internal.R.id.media_actions, button);
4293                 }
4294             }
4295             styleText(big);
4296             hideRightIcon(big);
4297             applyTopPadding(big);
4298             big.setViewVisibility(android.R.id.progress, View.GONE);
4299             return big;
4300         }
4301 
getBigLayoutResource(int actionCount)4302         private int getBigLayoutResource(int actionCount) {
4303             if (actionCount <= 3) {
4304                 return R.layout.notification_template_material_big_media_narrow;
4305             } else {
4306                 return R.layout.notification_template_material_big_media;
4307             }
4308         }
4309 
hideRightIcon(RemoteViews contentView)4310         private void hideRightIcon(RemoteViews contentView) {
4311             contentView.setViewVisibility(R.id.right_icon, View.GONE);
4312         }
4313 
4314         /**
4315          * Applies the special text colors for media notifications to all text views.
4316          */
styleText(RemoteViews contentView)4317         private void styleText(RemoteViews contentView) {
4318             int primaryColor = mBuilder.mContext.getResources().getColor(
4319                     R.color.notification_media_primary_color);
4320             int secondaryColor = mBuilder.mContext.getResources().getColor(
4321                     R.color.notification_media_secondary_color);
4322             contentView.setTextColor(R.id.title, primaryColor);
4323             if (mBuilder.showsTimeOrChronometer()) {
4324                 if (mBuilder.mUseChronometer) {
4325                     contentView.setTextColor(R.id.chronometer, secondaryColor);
4326                 } else {
4327                     contentView.setTextColor(R.id.time, secondaryColor);
4328                 }
4329             }
4330             contentView.setTextColor(R.id.text2, secondaryColor);
4331             contentView.setTextColor(R.id.text, secondaryColor);
4332             contentView.setTextColor(R.id.info, secondaryColor);
4333         }
4334 
4335         /**
4336          * @hide
4337          */
4338         @Override
hasProgress()4339         protected boolean hasProgress() {
4340             return false;
4341         }
4342     }
4343 
4344     // When adding a new Style subclass here, don't forget to update
4345     // Builder.getNotificationStyleClass.
4346 
4347     /**
4348      * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
4349      * metadata or change options on a notification builder.
4350      */
4351     public interface Extender {
4352         /**
4353          * Apply this extender to a notification builder.
4354          * @param builder the builder to be modified.
4355          * @return the build object for chaining.
4356          */
extend(Builder builder)4357         public Builder extend(Builder builder);
4358     }
4359 
4360     /**
4361      * Helper class to add wearable extensions to notifications.
4362      * <p class="note"> See
4363      * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
4364      * for Android Wear</a> for more information on how to use this class.
4365      * <p>
4366      * To create a notification with wearable extensions:
4367      * <ol>
4368      *   <li>Create a {@link android.app.Notification.Builder}, setting any desired
4369      *   properties.
4370      *   <li>Create a {@link android.app.Notification.WearableExtender}.
4371      *   <li>Set wearable-specific properties using the
4372      *   {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
4373      *   <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
4374      *   notification.
4375      *   <li>Post the notification to the notification system with the
4376      *   {@code NotificationManager.notify(...)} methods.
4377      * </ol>
4378      *
4379      * <pre class="prettyprint">
4380      * Notification notif = new Notification.Builder(mContext)
4381      *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
4382      *         .setContentText(subject)
4383      *         .setSmallIcon(R.drawable.new_mail)
4384      *         .extend(new Notification.WearableExtender()
4385      *                 .setContentIcon(R.drawable.new_mail))
4386      *         .build();
4387      * NotificationManager notificationManger =
4388      *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
4389      * notificationManger.notify(0, notif);</pre>
4390      *
4391      * <p>Wearable extensions can be accessed on an existing notification by using the
4392      * {@code WearableExtender(Notification)} constructor,
4393      * and then using the {@code get} methods to access values.
4394      *
4395      * <pre class="prettyprint">
4396      * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
4397      *         notification);
4398      * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
4399      */
4400     public static final class WearableExtender implements Extender {
4401         /**
4402          * Sentinel value for an action index that is unset.
4403          */
4404         public static final int UNSET_ACTION_INDEX = -1;
4405 
4406         /**
4407          * Size value for use with {@link #setCustomSizePreset} to show this notification with
4408          * default sizing.
4409          * <p>For custom display notifications created using {@link #setDisplayIntent},
4410          * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
4411          * on their content.
4412          */
4413         public static final int SIZE_DEFAULT = 0;
4414 
4415         /**
4416          * Size value for use with {@link #setCustomSizePreset} to show this notification
4417          * with an extra small size.
4418          * <p>This value is only applicable for custom display notifications created using
4419          * {@link #setDisplayIntent}.
4420          */
4421         public static final int SIZE_XSMALL = 1;
4422 
4423         /**
4424          * Size value for use with {@link #setCustomSizePreset} to show this notification
4425          * with a small size.
4426          * <p>This value is only applicable for custom display notifications created using
4427          * {@link #setDisplayIntent}.
4428          */
4429         public static final int SIZE_SMALL = 2;
4430 
4431         /**
4432          * Size value for use with {@link #setCustomSizePreset} to show this notification
4433          * with a medium size.
4434          * <p>This value is only applicable for custom display notifications created using
4435          * {@link #setDisplayIntent}.
4436          */
4437         public static final int SIZE_MEDIUM = 3;
4438 
4439         /**
4440          * Size value for use with {@link #setCustomSizePreset} to show this notification
4441          * with a large size.
4442          * <p>This value is only applicable for custom display notifications created using
4443          * {@link #setDisplayIntent}.
4444          */
4445         public static final int SIZE_LARGE = 4;
4446 
4447         /**
4448          * Size value for use with {@link #setCustomSizePreset} to show this notification
4449          * full screen.
4450          * <p>This value is only applicable for custom display notifications created using
4451          * {@link #setDisplayIntent}.
4452          */
4453         public static final int SIZE_FULL_SCREEN = 5;
4454 
4455         /**
4456          * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
4457          * short amount of time when this notification is displayed on the screen. This
4458          * is the default value.
4459          */
4460         public static final int SCREEN_TIMEOUT_SHORT = 0;
4461 
4462         /**
4463          * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
4464          * for a longer amount of time when this notification is displayed on the screen.
4465          */
4466         public static final int SCREEN_TIMEOUT_LONG = -1;
4467 
4468         /** Notification extra which contains wearable extensions */
4469         private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
4470 
4471         // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
4472         private static final String KEY_ACTIONS = "actions";
4473         private static final String KEY_FLAGS = "flags";
4474         private static final String KEY_DISPLAY_INTENT = "displayIntent";
4475         private static final String KEY_PAGES = "pages";
4476         private static final String KEY_BACKGROUND = "background";
4477         private static final String KEY_CONTENT_ICON = "contentIcon";
4478         private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
4479         private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
4480         private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
4481         private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
4482         private static final String KEY_GRAVITY = "gravity";
4483         private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
4484 
4485         // Flags bitwise-ored to mFlags
4486         private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
4487         private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
4488         private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
4489         private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
4490         private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
4491 
4492         // Default value for flags integer
4493         private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
4494 
4495         private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
4496         private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
4497 
4498         private ArrayList<Action> mActions = new ArrayList<Action>();
4499         private int mFlags = DEFAULT_FLAGS;
4500         private PendingIntent mDisplayIntent;
4501         private ArrayList<Notification> mPages = new ArrayList<Notification>();
4502         private Bitmap mBackground;
4503         private int mContentIcon;
4504         private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
4505         private int mContentActionIndex = UNSET_ACTION_INDEX;
4506         private int mCustomSizePreset = SIZE_DEFAULT;
4507         private int mCustomContentHeight;
4508         private int mGravity = DEFAULT_GRAVITY;
4509         private int mHintScreenTimeout;
4510 
4511         /**
4512          * Create a {@link android.app.Notification.WearableExtender} with default
4513          * options.
4514          */
WearableExtender()4515         public WearableExtender() {
4516         }
4517 
WearableExtender(Notification notif)4518         public WearableExtender(Notification notif) {
4519             Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
4520             if (wearableBundle != null) {
4521                 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
4522                 if (actions != null) {
4523                     mActions.addAll(actions);
4524                 }
4525 
4526                 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
4527                 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
4528 
4529                 Notification[] pages = getNotificationArrayFromBundle(
4530                         wearableBundle, KEY_PAGES);
4531                 if (pages != null) {
4532                     Collections.addAll(mPages, pages);
4533                 }
4534 
4535                 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
4536                 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
4537                 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
4538                         DEFAULT_CONTENT_ICON_GRAVITY);
4539                 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
4540                         UNSET_ACTION_INDEX);
4541                 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
4542                         SIZE_DEFAULT);
4543                 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
4544                 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
4545                 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
4546             }
4547         }
4548 
4549         /**
4550          * Apply wearable extensions to a notification that is being built. This is typically
4551          * called by the {@link android.app.Notification.Builder#extend} method of
4552          * {@link android.app.Notification.Builder}.
4553          */
4554         @Override
extend(Notification.Builder builder)4555         public Notification.Builder extend(Notification.Builder builder) {
4556             Bundle wearableBundle = new Bundle();
4557 
4558             if (!mActions.isEmpty()) {
4559                 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
4560             }
4561             if (mFlags != DEFAULT_FLAGS) {
4562                 wearableBundle.putInt(KEY_FLAGS, mFlags);
4563             }
4564             if (mDisplayIntent != null) {
4565                 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
4566             }
4567             if (!mPages.isEmpty()) {
4568                 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
4569                         new Notification[mPages.size()]));
4570             }
4571             if (mBackground != null) {
4572                 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
4573             }
4574             if (mContentIcon != 0) {
4575                 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
4576             }
4577             if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
4578                 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
4579             }
4580             if (mContentActionIndex != UNSET_ACTION_INDEX) {
4581                 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
4582                         mContentActionIndex);
4583             }
4584             if (mCustomSizePreset != SIZE_DEFAULT) {
4585                 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
4586             }
4587             if (mCustomContentHeight != 0) {
4588                 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
4589             }
4590             if (mGravity != DEFAULT_GRAVITY) {
4591                 wearableBundle.putInt(KEY_GRAVITY, mGravity);
4592             }
4593             if (mHintScreenTimeout != 0) {
4594                 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
4595             }
4596 
4597             builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
4598             return builder;
4599         }
4600 
4601         @Override
clone()4602         public WearableExtender clone() {
4603             WearableExtender that = new WearableExtender();
4604             that.mActions = new ArrayList<Action>(this.mActions);
4605             that.mFlags = this.mFlags;
4606             that.mDisplayIntent = this.mDisplayIntent;
4607             that.mPages = new ArrayList<Notification>(this.mPages);
4608             that.mBackground = this.mBackground;
4609             that.mContentIcon = this.mContentIcon;
4610             that.mContentIconGravity = this.mContentIconGravity;
4611             that.mContentActionIndex = this.mContentActionIndex;
4612             that.mCustomSizePreset = this.mCustomSizePreset;
4613             that.mCustomContentHeight = this.mCustomContentHeight;
4614             that.mGravity = this.mGravity;
4615             that.mHintScreenTimeout = this.mHintScreenTimeout;
4616             return that;
4617         }
4618 
4619         /**
4620          * Add a wearable action to this notification.
4621          *
4622          * <p>When wearable actions are added using this method, the set of actions that
4623          * show on a wearable device splits from devices that only show actions added
4624          * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4625          * of which actions display on different devices.
4626          *
4627          * @param action the action to add to this notification
4628          * @return this object for method chaining
4629          * @see android.app.Notification.Action
4630          */
addAction(Action action)4631         public WearableExtender addAction(Action action) {
4632             mActions.add(action);
4633             return this;
4634         }
4635 
4636         /**
4637          * Adds wearable actions to this notification.
4638          *
4639          * <p>When wearable actions are added using this method, the set of actions that
4640          * show on a wearable device splits from devices that only show actions added
4641          * using {@link android.app.Notification.Builder#addAction}. This allows for customization
4642          * of which actions display on different devices.
4643          *
4644          * @param actions the actions to add to this notification
4645          * @return this object for method chaining
4646          * @see android.app.Notification.Action
4647          */
addActions(List<Action> actions)4648         public WearableExtender addActions(List<Action> actions) {
4649             mActions.addAll(actions);
4650             return this;
4651         }
4652 
4653         /**
4654          * Clear all wearable actions present on this builder.
4655          * @return this object for method chaining.
4656          * @see #addAction
4657          */
clearActions()4658         public WearableExtender clearActions() {
4659             mActions.clear();
4660             return this;
4661         }
4662 
4663         /**
4664          * Get the wearable actions present on this notification.
4665          */
getActions()4666         public List<Action> getActions() {
4667             return mActions;
4668         }
4669 
4670         /**
4671          * Set an intent to launch inside of an activity view when displaying
4672          * this notification. The {@link PendingIntent} provided should be for an activity.
4673          *
4674          * <pre class="prettyprint">
4675          * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
4676          * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
4677          *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
4678          * Notification notif = new Notification.Builder(context)
4679          *         .extend(new Notification.WearableExtender()
4680          *                 .setDisplayIntent(displayPendingIntent)
4681          *                 .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
4682          *         .build();</pre>
4683          *
4684          * <p>The activity to launch needs to allow embedding, must be exported, and
4685          * should have an empty task affinity. It is also recommended to use the device
4686          * default light theme.
4687          *
4688          * <p>Example AndroidManifest.xml entry:
4689          * <pre class="prettyprint">
4690          * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
4691          *     android:exported=&quot;true&quot;
4692          *     android:allowEmbedded=&quot;true&quot;
4693          *     android:taskAffinity=&quot;&quot;
4694          *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
4695          *
4696          * @param intent the {@link PendingIntent} for an activity
4697          * @return this object for method chaining
4698          * @see android.app.Notification.WearableExtender#getDisplayIntent
4699          */
setDisplayIntent(PendingIntent intent)4700         public WearableExtender setDisplayIntent(PendingIntent intent) {
4701             mDisplayIntent = intent;
4702             return this;
4703         }
4704 
4705         /**
4706          * Get the intent to launch inside of an activity view when displaying this
4707          * notification. This {@code PendingIntent} should be for an activity.
4708          */
getDisplayIntent()4709         public PendingIntent getDisplayIntent() {
4710             return mDisplayIntent;
4711         }
4712 
4713         /**
4714          * Add an additional page of content to display with this notification. The current
4715          * notification forms the first page, and pages added using this function form
4716          * subsequent pages. This field can be used to separate a notification into multiple
4717          * sections.
4718          *
4719          * @param page the notification to add as another page
4720          * @return this object for method chaining
4721          * @see android.app.Notification.WearableExtender#getPages
4722          */
addPage(Notification page)4723         public WearableExtender addPage(Notification page) {
4724             mPages.add(page);
4725             return this;
4726         }
4727 
4728         /**
4729          * Add additional pages of content to display with this notification. The current
4730          * notification forms the first page, and pages added using this function form
4731          * subsequent pages. This field can be used to separate a notification into multiple
4732          * sections.
4733          *
4734          * @param pages a list of notifications
4735          * @return this object for method chaining
4736          * @see android.app.Notification.WearableExtender#getPages
4737          */
addPages(List<Notification> pages)4738         public WearableExtender addPages(List<Notification> pages) {
4739             mPages.addAll(pages);
4740             return this;
4741         }
4742 
4743         /**
4744          * Clear all additional pages present on this builder.
4745          * @return this object for method chaining.
4746          * @see #addPage
4747          */
clearPages()4748         public WearableExtender clearPages() {
4749             mPages.clear();
4750             return this;
4751         }
4752 
4753         /**
4754          * Get the array of additional pages of content for displaying this notification. The
4755          * current notification forms the first page, and elements within this array form
4756          * subsequent pages. This field can be used to separate a notification into multiple
4757          * sections.
4758          * @return the pages for this notification
4759          */
getPages()4760         public List<Notification> getPages() {
4761             return mPages;
4762         }
4763 
4764         /**
4765          * Set a background image to be displayed behind the notification content.
4766          * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4767          * will work with any notification style.
4768          *
4769          * @param background the background bitmap
4770          * @return this object for method chaining
4771          * @see android.app.Notification.WearableExtender#getBackground
4772          */
setBackground(Bitmap background)4773         public WearableExtender setBackground(Bitmap background) {
4774             mBackground = background;
4775             return this;
4776         }
4777 
4778         /**
4779          * Get a background image to be displayed behind the notification content.
4780          * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
4781          * will work with any notification style.
4782          *
4783          * @return the background image
4784          * @see android.app.Notification.WearableExtender#setBackground
4785          */
getBackground()4786         public Bitmap getBackground() {
4787             return mBackground;
4788         }
4789 
4790         /**
4791          * Set an icon that goes with the content of this notification.
4792          */
setContentIcon(int icon)4793         public WearableExtender setContentIcon(int icon) {
4794             mContentIcon = icon;
4795             return this;
4796         }
4797 
4798         /**
4799          * Get an icon that goes with the content of this notification.
4800          */
getContentIcon()4801         public int getContentIcon() {
4802             return mContentIcon;
4803         }
4804 
4805         /**
4806          * Set the gravity that the content icon should have within the notification display.
4807          * Supported values include {@link android.view.Gravity#START} and
4808          * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4809          * @see #setContentIcon
4810          */
setContentIconGravity(int contentIconGravity)4811         public WearableExtender setContentIconGravity(int contentIconGravity) {
4812             mContentIconGravity = contentIconGravity;
4813             return this;
4814         }
4815 
4816         /**
4817          * Get the gravity that the content icon should have within the notification display.
4818          * Supported values include {@link android.view.Gravity#START} and
4819          * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
4820          * @see #getContentIcon
4821          */
getContentIconGravity()4822         public int getContentIconGravity() {
4823             return mContentIconGravity;
4824         }
4825 
4826         /**
4827          * Set an action from this notification's actions to be clickable with the content of
4828          * this notification. This action will no longer display separately from the
4829          * notification's content.
4830          *
4831          * <p>For notifications with multiple pages, child pages can also have content actions
4832          * set, although the list of available actions comes from the main notification and not
4833          * from the child page's notification.
4834          *
4835          * @param actionIndex The index of the action to hoist onto the current notification page.
4836          *                    If wearable actions were added to the main notification, this index
4837          *                    will apply to that list, otherwise it will apply to the regular
4838          *                    actions list.
4839          */
setContentAction(int actionIndex)4840         public WearableExtender setContentAction(int actionIndex) {
4841             mContentActionIndex = actionIndex;
4842             return this;
4843         }
4844 
4845         /**
4846          * Get the index of the notification action, if any, that was specified as being clickable
4847          * with the content of this notification. This action will no longer display separately
4848          * from the notification's content.
4849          *
4850          * <p>For notifications with multiple pages, child pages can also have content actions
4851          * set, although the list of available actions comes from the main notification and not
4852          * from the child page's notification.
4853          *
4854          * <p>If wearable specific actions were added to the main notification, this index will
4855          * apply to that list, otherwise it will apply to the regular actions list.
4856          *
4857          * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
4858          */
getContentAction()4859         public int getContentAction() {
4860             return mContentActionIndex;
4861         }
4862 
4863         /**
4864          * Set the gravity that this notification should have within the available viewport space.
4865          * Supported values include {@link android.view.Gravity#TOP},
4866          * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4867          * The default value is {@link android.view.Gravity#BOTTOM}.
4868          */
setGravity(int gravity)4869         public WearableExtender setGravity(int gravity) {
4870             mGravity = gravity;
4871             return this;
4872         }
4873 
4874         /**
4875          * Get the gravity that this notification should have within the available viewport space.
4876          * Supported values include {@link android.view.Gravity#TOP},
4877          * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
4878          * The default value is {@link android.view.Gravity#BOTTOM}.
4879          */
getGravity()4880         public int getGravity() {
4881             return mGravity;
4882         }
4883 
4884         /**
4885          * Set the custom size preset for the display of this notification out of the available
4886          * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4887          * {@link #SIZE_LARGE}.
4888          * <p>Some custom size presets are only applicable for custom display notifications created
4889          * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
4890          * documentation for the preset in question. See also
4891          * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
4892          */
setCustomSizePreset(int sizePreset)4893         public WearableExtender setCustomSizePreset(int sizePreset) {
4894             mCustomSizePreset = sizePreset;
4895             return this;
4896         }
4897 
4898         /**
4899          * Get the custom size preset for the display of this notification out of the available
4900          * presets found in {@link android.app.Notification.WearableExtender}, e.g.
4901          * {@link #SIZE_LARGE}.
4902          * <p>Some custom size presets are only applicable for custom display notifications created
4903          * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
4904          * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
4905          */
getCustomSizePreset()4906         public int getCustomSizePreset() {
4907             return mCustomSizePreset;
4908         }
4909 
4910         /**
4911          * Set the custom height in pixels for the display of this notification's content.
4912          * <p>This option is only available for custom display notifications created
4913          * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
4914          * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
4915          * {@link #getCustomContentHeight}.
4916          */
setCustomContentHeight(int height)4917         public WearableExtender setCustomContentHeight(int height) {
4918             mCustomContentHeight = height;
4919             return this;
4920         }
4921 
4922         /**
4923          * Get the custom height in pixels for the display of this notification's content.
4924          * <p>This option is only available for custom display notifications created
4925          * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
4926          * {@link #setCustomContentHeight}.
4927          */
getCustomContentHeight()4928         public int getCustomContentHeight() {
4929             return mCustomContentHeight;
4930         }
4931 
4932         /**
4933          * Set whether the scrolling position for the contents of this notification should start
4934          * at the bottom of the contents instead of the top when the contents are too long to
4935          * display within the screen.  Default is false (start scroll at the top).
4936          */
setStartScrollBottom(boolean startScrollBottom)4937         public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
4938             setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
4939             return this;
4940         }
4941 
4942         /**
4943          * Get whether the scrolling position for the contents of this notification should start
4944          * at the bottom of the contents instead of the top when the contents are too long to
4945          * display within the screen. Default is false (start scroll at the top).
4946          */
getStartScrollBottom()4947         public boolean getStartScrollBottom() {
4948             return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
4949         }
4950 
4951         /**
4952          * Set whether the content intent is available when the wearable device is not connected
4953          * to a companion device.  The user can still trigger this intent when the wearable device
4954          * is offline, but a visual hint will indicate that the content intent may not be available.
4955          * Defaults to true.
4956          */
setContentIntentAvailableOffline( boolean contentIntentAvailableOffline)4957         public WearableExtender setContentIntentAvailableOffline(
4958                 boolean contentIntentAvailableOffline) {
4959             setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
4960             return this;
4961         }
4962 
4963         /**
4964          * Get whether the content intent is available when the wearable device is not connected
4965          * to a companion device.  The user can still trigger this intent when the wearable device
4966          * is offline, but a visual hint will indicate that the content intent may not be available.
4967          * Defaults to true.
4968          */
getContentIntentAvailableOffline()4969         public boolean getContentIntentAvailableOffline() {
4970             return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
4971         }
4972 
4973         /**
4974          * Set a hint that this notification's icon should not be displayed.
4975          * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
4976          * @return this object for method chaining
4977          */
setHintHideIcon(boolean hintHideIcon)4978         public WearableExtender setHintHideIcon(boolean hintHideIcon) {
4979             setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
4980             return this;
4981         }
4982 
4983         /**
4984          * Get a hint that this notification's icon should not be displayed.
4985          * @return {@code true} if this icon should not be displayed, false otherwise.
4986          * The default value is {@code false} if this was never set.
4987          */
getHintHideIcon()4988         public boolean getHintHideIcon() {
4989             return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
4990         }
4991 
4992         /**
4993          * Set a visual hint that only the background image of this notification should be
4994          * displayed, and other semantic content should be hidden. This hint is only applicable
4995          * to sub-pages added using {@link #addPage}.
4996          */
setHintShowBackgroundOnly(boolean hintShowBackgroundOnly)4997         public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
4998             setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
4999             return this;
5000         }
5001 
5002         /**
5003          * Get a visual hint that only the background image of this notification should be
5004          * displayed, and other semantic content should be hidden. This hint is only applicable
5005          * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
5006          */
getHintShowBackgroundOnly()5007         public boolean getHintShowBackgroundOnly() {
5008             return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
5009         }
5010 
5011         /**
5012          * Set a hint that this notification's background should not be clipped if possible,
5013          * and should instead be resized to fully display on the screen, retaining the aspect
5014          * ratio of the image. This can be useful for images like barcodes or qr codes.
5015          * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
5016          * @return this object for method chaining
5017          */
setHintAvoidBackgroundClipping( boolean hintAvoidBackgroundClipping)5018         public WearableExtender setHintAvoidBackgroundClipping(
5019                 boolean hintAvoidBackgroundClipping) {
5020             setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
5021             return this;
5022         }
5023 
5024         /**
5025          * Get a hint that this notification's background should not be clipped if possible,
5026          * and should instead be resized to fully display on the screen, retaining the aspect
5027          * ratio of the image. This can be useful for images like barcodes or qr codes.
5028          * @return {@code true} if it's ok if the background is clipped on the screen, false
5029          * otherwise. The default value is {@code false} if this was never set.
5030          */
getHintAvoidBackgroundClipping()5031         public boolean getHintAvoidBackgroundClipping() {
5032             return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
5033         }
5034 
5035         /**
5036          * Set a hint that the screen should remain on for at least this duration when
5037          * this notification is displayed on the screen.
5038          * @param timeout The requested screen timeout in milliseconds. Can also be either
5039          *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5040          * @return this object for method chaining
5041          */
setHintScreenTimeout(int timeout)5042         public WearableExtender setHintScreenTimeout(int timeout) {
5043             mHintScreenTimeout = timeout;
5044             return this;
5045         }
5046 
5047         /**
5048          * Get the duration, in milliseconds, that the screen should remain on for
5049          * when this notification is displayed.
5050          * @return the duration in milliseconds if > 0, or either one of the sentinel values
5051          *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
5052          */
getHintScreenTimeout()5053         public int getHintScreenTimeout() {
5054             return mHintScreenTimeout;
5055         }
5056 
setFlag(int mask, boolean value)5057         private void setFlag(int mask, boolean value) {
5058             if (value) {
5059                 mFlags |= mask;
5060             } else {
5061                 mFlags &= ~mask;
5062             }
5063         }
5064     }
5065 
5066     /**
5067      * Get an array of Notification objects from a parcelable array bundle field.
5068      * Update the bundle to have a typed array so fetches in the future don't need
5069      * to do an array copy.
5070      */
getNotificationArrayFromBundle(Bundle bundle, String key)5071     private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
5072         Parcelable[] array = bundle.getParcelableArray(key);
5073         if (array instanceof Notification[] || array == null) {
5074             return (Notification[]) array;
5075         }
5076         Notification[] typedArray = Arrays.copyOf(array, array.length,
5077                 Notification[].class);
5078         bundle.putParcelableArray(key, typedArray);
5079         return typedArray;
5080     }
5081 
5082     private static class BuilderRemoteViews extends RemoteViews {
BuilderRemoteViews(Parcel parcel)5083         public BuilderRemoteViews(Parcel parcel) {
5084             super(parcel);
5085         }
5086 
BuilderRemoteViews(ApplicationInfo appInfo, int layoutId)5087         public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
5088             super(appInfo, layoutId);
5089         }
5090 
5091         @Override
clone()5092         public BuilderRemoteViews clone() {
5093             Parcel p = Parcel.obtain();
5094             writeToParcel(p, 0);
5095             p.setDataPosition(0);
5096             BuilderRemoteViews brv = new BuilderRemoteViews(p);
5097             p.recycle();
5098             return brv;
5099         }
5100     }
5101 }
5102