OSDN Git Service

Merge "Allow setup apps to colorize notifications." into oc-dr1-dev
[android-x86/frameworks-base.git] / core / java / android / app / Notification.java
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 static com.android.internal.util.NotificationColorUtil.satisfiesTextContrast;
20
21 import android.annotation.ColorInt;
22 import android.annotation.DrawableRes;
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SdkConstant;
27 import android.annotation.SdkConstant.SdkConstantType;
28 import android.annotation.SystemApi;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.PackageManager;
33 import android.content.pm.PackageManager.NameNotFoundException;
34 import android.content.pm.ShortcutInfo;
35 import android.content.res.ColorStateList;
36 import android.graphics.Bitmap;
37 import android.graphics.Canvas;
38 import android.graphics.Color;
39 import android.graphics.PorterDuff;
40 import android.graphics.drawable.Drawable;
41 import android.graphics.drawable.Icon;
42 import android.media.AudioAttributes;
43 import android.media.AudioManager;
44 import android.media.PlayerBase;
45 import android.media.session.MediaSession;
46 import android.net.Uri;
47 import android.os.BadParcelableException;
48 import android.os.Build;
49 import android.os.Bundle;
50 import android.os.IBinder;
51 import android.os.Parcel;
52 import android.os.Parcelable;
53 import android.os.SystemClock;
54 import android.os.SystemProperties;
55 import android.os.UserHandle;
56 import android.text.BidiFormatter;
57 import android.text.SpannableStringBuilder;
58 import android.text.Spanned;
59 import android.text.TextUtils;
60 import android.text.style.AbsoluteSizeSpan;
61 import android.text.style.BackgroundColorSpan;
62 import android.text.style.CharacterStyle;
63 import android.text.style.ForegroundColorSpan;
64 import android.text.style.RelativeSizeSpan;
65 import android.text.style.TextAppearanceSpan;
66 import android.util.ArraySet;
67 import android.util.Log;
68 import android.util.SparseArray;
69 import android.view.Gravity;
70 import android.view.NotificationHeaderView;
71 import android.view.View;
72 import android.view.ViewGroup;
73 import android.widget.ProgressBar;
74 import android.widget.RemoteViews;
75
76 import com.android.internal.R;
77 import com.android.internal.annotations.VisibleForTesting;
78 import com.android.internal.util.ArrayUtils;
79 import com.android.internal.util.NotificationColorUtil;
80 import com.android.internal.util.Preconditions;
81
82 import java.lang.annotation.Retention;
83 import java.lang.annotation.RetentionPolicy;
84 import java.lang.reflect.Constructor;
85 import java.util.ArrayList;
86 import java.util.Arrays;
87 import java.util.Collections;
88 import java.util.List;
89 import java.util.Set;
90
91 /**
92  * A class that represents how a persistent notification is to be presented to
93  * the user using the {@link android.app.NotificationManager}.
94  *
95  * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
96  * easier to construct Notifications.</p>
97  *
98  * <div class="special reference">
99  * <h3>Developer Guides</h3>
100  * <p>For a guide to creating notifications, read the
101  * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
102  * developer guide.</p>
103  * </div>
104  */
105 public class Notification implements Parcelable
106 {
107     private static final String TAG = "Notification";
108
109     /**
110      * An activity that provides a user interface for adjusting notification preferences for its
111      * containing application.
112      */
113     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
114     public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
115             = "android.intent.category.NOTIFICATION_PREFERENCES";
116
117     /**
118      * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
119      * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
120      * what settings should be shown in the target app.
121      */
122     public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
123
124     /**
125      * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
126      * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
127      * that can be used to narrow down what settings should be shown in the target app.
128      */
129     public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
130
131     /**
132      * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
133      * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
134      * that can be used to narrow down what settings should be shown in the target app.
135      */
136     public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
137
138     /**
139      * Use all default values (where applicable).
140      */
141     public static final int DEFAULT_ALL = ~0;
142
143     /**
144      * Use the default notification sound. This will ignore any given
145      * {@link #sound}.
146      *
147      * <p>
148      * A notification that is noisy is more likely to be presented as a heads-up notification.
149      * </p>
150      *
151      * @see #defaults
152      */
153
154     public static final int DEFAULT_SOUND = 1;
155
156     /**
157      * Use the default notification vibrate. This will ignore any given
158      * {@link #vibrate}. Using phone vibration requires the
159      * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
160      *
161      * <p>
162      * A notification that vibrates is more likely to be presented as a heads-up notification.
163      * </p>
164      *
165      * @see #defaults
166      */
167
168     public static final int DEFAULT_VIBRATE = 2;
169
170     /**
171      * Use the default notification lights. This will ignore the
172      * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
173      * {@link #ledOnMS}.
174      *
175      * @see #defaults
176      */
177
178     public static final int DEFAULT_LIGHTS = 4;
179
180     /**
181      * Maximum length of CharSequences accepted by Builder and friends.
182      *
183      * <p>
184      * Avoids spamming the system with overly large strings such as full e-mails.
185      */
186     private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
187
188     /**
189      * Maximum entries of reply text that are accepted by Builder and friends.
190      */
191     private static final int MAX_REPLY_HISTORY = 5;
192
193     /**
194      * A timestamp related to this notification, in milliseconds since the epoch.
195      *
196      * Default value: {@link System#currentTimeMillis() Now}.
197      *
198      * Choose a timestamp that will be most relevant to the user. For most finite events, this
199      * corresponds to the time the event happened (or will happen, in the case of events that have
200      * yet to occur but about which the user is being informed). Indefinite events should be
201      * timestamped according to when the activity began.
202      *
203      * Some examples:
204      *
205      * <ul>
206      *   <li>Notification of a new chat message should be stamped when the message was received.</li>
207      *   <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
208      *   <li>Notification of a completed file download should be stamped when the download finished.</li>
209      *   <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
210      *   <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
211      *   <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
212      * </ul>
213      *
214      * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
215      * anymore by default and must be opted into by using
216      * {@link android.app.Notification.Builder#setShowWhen(boolean)}
217      */
218     public long when;
219
220     /**
221      * The creation time of the notification
222      */
223     private long creationTime;
224
225     /**
226      * The resource id of a drawable to use as the icon in the status bar.
227      *
228      * @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.
229      */
230     @Deprecated
231     @DrawableRes
232     public int icon;
233
234     /**
235      * If the icon in the status bar is to have more than one level, you can set this.  Otherwise,
236      * leave it at its default value of 0.
237      *
238      * @see android.widget.ImageView#setImageLevel
239      * @see android.graphics.drawable.Drawable#setLevel
240      */
241     public int iconLevel;
242
243     /**
244      * The number of events that this notification represents. For example, in a new mail
245      * notification, this could be the number of unread messages.
246      *
247      * The system may or may not use this field to modify the appearance of the notification.
248      * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
249      * badge icon in Launchers that support badging.
250      */
251     public int number = 0;
252
253     /**
254      * The intent to execute when the expanded status entry is clicked.  If
255      * this is an activity, it must include the
256      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
257      * that you take care of task management as described in the
258      * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
259      * Stack</a> document.  In particular, make sure to read the notification section
260      * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
261      * Notifications</a> for the correct ways to launch an application from a
262      * notification.
263      */
264     public PendingIntent contentIntent;
265
266     /**
267      * The intent to execute when the notification is explicitly dismissed by the user, either with
268      * the "Clear All" button or by swiping it away individually.
269      *
270      * This probably shouldn't be launching an activity since several of those will be sent
271      * at the same time.
272      */
273     public PendingIntent deleteIntent;
274
275     /**
276      * An intent to launch instead of posting the notification to the status bar.
277      *
278      * <p>
279      * The system UI may choose to display a heads-up notification, instead of
280      * launching this intent, while the user is using the device.
281      * </p>
282      *
283      * @see Notification.Builder#setFullScreenIntent
284      */
285     public PendingIntent fullScreenIntent;
286
287     /**
288      * Text that summarizes this notification for accessibility services.
289      *
290      * As of the L release, this text is no longer shown on screen, but it is still useful to
291      * accessibility services (where it serves as an audible announcement of the notification's
292      * appearance).
293      *
294      * @see #tickerView
295      */
296     public CharSequence tickerText;
297
298     /**
299      * Formerly, a view showing the {@link #tickerText}.
300      *
301      * No longer displayed in the status bar as of API 21.
302      */
303     @Deprecated
304     public RemoteViews tickerView;
305
306     /**
307      * The view that will represent this notification in the notification list (which is pulled
308      * down from the status bar).
309      *
310      * As of N, this field may be null. The notification view is determined by the inputs
311      * to {@link Notification.Builder}; a custom RemoteViews can optionally be
312      * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
313      */
314     @Deprecated
315     public RemoteViews contentView;
316
317     /**
318      * A large-format version of {@link #contentView}, giving the Notification an
319      * opportunity to show more detail. The system UI may choose to show this
320      * instead of the normal content view at its discretion.
321      *
322      * As of N, this field may be null. The expanded notification view is determined by the
323      * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
324      * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
325      */
326     @Deprecated
327     public RemoteViews bigContentView;
328
329
330     /**
331      * A medium-format version of {@link #contentView}, providing the Notification an
332      * opportunity to add action buttons to contentView. At its discretion, the system UI may
333      * choose to show this as a heads-up notification, which will pop up so the user can see
334      * it without leaving their current activity.
335      *
336      * As of N, this field may be null. The heads-up notification view is determined by the
337      * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
338      * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
339      */
340     @Deprecated
341     public RemoteViews headsUpContentView;
342
343     /**
344      * A large bitmap to be shown in the notification content area.
345      *
346      * @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.
347      */
348     @Deprecated
349     public Bitmap largeIcon;
350
351     /**
352      * The sound to play.
353      *
354      * <p>
355      * A notification that is noisy is more likely to be presented as a heads-up notification.
356      * </p>
357      *
358      * <p>
359      * To play the default notification sound, see {@link #defaults}.
360      * </p>
361      * @deprecated use {@link NotificationChannel#getSound()}.
362      */
363     @Deprecated
364     public Uri sound;
365
366     /**
367      * Use this constant as the value for audioStreamType to request that
368      * the default stream type for notifications be used.  Currently the
369      * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
370      *
371      * @deprecated Use {@link NotificationChannel#getAudioAttributes()} instead.
372      */
373     @Deprecated
374     public static final int STREAM_DEFAULT = -1;
375
376     /**
377      * The audio stream type to use when playing the sound.
378      * Should be one of the STREAM_ constants from
379      * {@link android.media.AudioManager}.
380      *
381      * @deprecated Use {@link #audioAttributes} instead.
382      */
383     @Deprecated
384     public int audioStreamType = STREAM_DEFAULT;
385
386     /**
387      * The default value of {@link #audioAttributes}.
388      */
389     public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
390             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
391             .setUsage(AudioAttributes.USAGE_NOTIFICATION)
392             .build();
393
394     /**
395      * The {@link AudioAttributes audio attributes} to use when playing the sound.
396      *
397      * @deprecated use {@link NotificationChannel#getAudioAttributes()} instead.
398      */
399     @Deprecated
400     public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
401
402     /**
403      * The pattern with which to vibrate.
404      *
405      * <p>
406      * To vibrate the default pattern, see {@link #defaults}.
407      * </p>
408      *
409      * @see android.os.Vibrator#vibrate(long[],int)
410      * @deprecated use {@link NotificationChannel#getVibrationPattern()}.
411      */
412     @Deprecated
413     public long[] vibrate;
414
415     /**
416      * The color of the led.  The hardware will do its best approximation.
417      *
418      * @see #FLAG_SHOW_LIGHTS
419      * @see #flags
420      * @deprecated use {@link NotificationChannel#shouldShowLights()}.
421      */
422     @ColorInt
423     @Deprecated
424     public int ledARGB;
425
426     /**
427      * The number of milliseconds for the LED to be on while it's flashing.
428      * The hardware will do its best approximation.
429      *
430      * @see #FLAG_SHOW_LIGHTS
431      * @see #flags
432      * @deprecated use {@link NotificationChannel#shouldShowLights()}.
433      */
434     @Deprecated
435     public int ledOnMS;
436
437     /**
438      * The number of milliseconds for the LED to be off while it's flashing.
439      * The hardware will do its best approximation.
440      *
441      * @see #FLAG_SHOW_LIGHTS
442      * @see #flags
443      *
444      * @deprecated use {@link NotificationChannel#shouldShowLights()}.
445      */
446     @Deprecated
447     public int ledOffMS;
448
449     /**
450      * Specifies which values should be taken from the defaults.
451      * <p>
452      * To set, OR the desired from {@link #DEFAULT_SOUND},
453      * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
454      * values, use {@link #DEFAULT_ALL}.
455      * </p>
456      *
457      * @deprecated use {@link NotificationChannel#getSound()} and
458      * {@link NotificationChannel#shouldShowLights()} and
459      * {@link NotificationChannel#shouldVibrate()}.
460      */
461     @Deprecated
462     public int defaults;
463
464     /**
465      * Bit to be bitwise-ored into the {@link #flags} field that should be
466      * set if you want the LED on for this notification.
467      * <ul>
468      * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
469      *      or 0 for both ledOnMS and ledOffMS.</li>
470      * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
471      * <li>To flash the LED, pass the number of milliseconds that it should
472      *      be on and off to ledOnMS and ledOffMS.</li>
473      * </ul>
474      * <p>
475      * Since hardware varies, you are not guaranteed that any of the values
476      * you pass are honored exactly.  Use the system defaults (TODO) if possible
477      * because they will be set to values that work on any given hardware.
478      * <p>
479      * The alpha channel must be set for forward compatibility.
480      *
481      * @deprecated use {@link NotificationChannel#shouldShowLights()}.
482      */
483     @Deprecated
484     public static final int FLAG_SHOW_LIGHTS        = 0x00000001;
485
486     /**
487      * Bit to be bitwise-ored into the {@link #flags} field that should be
488      * set if this notification is in reference to something that is ongoing,
489      * like a phone call.  It should not be set if this notification is in
490      * reference to something that happened at a particular point in time,
491      * like a missed phone call.
492      */
493     public static final int FLAG_ONGOING_EVENT      = 0x00000002;
494
495     /**
496      * Bit to be bitwise-ored into the {@link #flags} field that if set,
497      * the audio will be repeated until the notification is
498      * cancelled or the notification window is opened.
499      */
500     public static final int FLAG_INSISTENT          = 0x00000004;
501
502     /**
503      * Bit to be bitwise-ored into the {@link #flags} field that should be
504      * set if you would only like the sound, vibrate and ticker to be played
505      * if the notification was not already showing.
506      */
507     public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;
508
509     /**
510      * Bit to be bitwise-ored into the {@link #flags} field that should be
511      * set if the notification should be canceled when it is clicked by the
512      * user.
513      */
514     public static final int FLAG_AUTO_CANCEL        = 0x00000010;
515
516     /**
517      * Bit to be bitwise-ored into the {@link #flags} field that should be
518      * set if the notification should not be canceled when the user clicks
519      * the Clear all button.
520      */
521     public static final int FLAG_NO_CLEAR           = 0x00000020;
522
523     /**
524      * Bit to be bitwise-ored into the {@link #flags} field that should be
525      * set if this notification represents a currently running service.  This
526      * will normally be set for you by {@link Service#startForeground}.
527      */
528     public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
529
530     /**
531      * Obsolete flag indicating high-priority notifications; use the priority field instead.
532      *
533      * @deprecated Use {@link #priority} with a positive value.
534      */
535     @Deprecated
536     public static final int FLAG_HIGH_PRIORITY      = 0x00000080;
537
538     /**
539      * Bit to be bitswise-ored into the {@link #flags} field that should be
540      * set if this notification is relevant to the current device only
541      * and it is not recommended that it bridge to other devices.
542      */
543     public static final int FLAG_LOCAL_ONLY         = 0x00000100;
544
545     /**
546      * Bit to be bitswise-ored into the {@link #flags} field that should be
547      * set if this notification is the group summary for a group of notifications.
548      * Grouped notifications may display in a cluster or stack on devices which
549      * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
550      */
551     public static final int FLAG_GROUP_SUMMARY      = 0x00000200;
552
553     /**
554      * Bit to be bitswise-ored into the {@link #flags} field that should be
555      * set if this notification is the group summary for an auto-group of notifications.
556      *
557      * @hide
558      */
559     @SystemApi
560     public static final int FLAG_AUTOGROUP_SUMMARY  = 0x00000400;
561
562     /**
563      * @hide
564      */
565     public static final int FLAG_CAN_COLORIZE = 0x00000800;
566
567     public int flags;
568
569     /** @hide */
570     @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
571     @Retention(RetentionPolicy.SOURCE)
572     public @interface Priority {}
573
574     /**
575      * Default notification {@link #priority}. If your application does not prioritize its own
576      * notifications, use this value for all notifications.
577      *
578      * @deprecated use {@link NotificationManager#IMPORTANCE_DEFAULT} instead.
579      */
580     @Deprecated
581     public static final int PRIORITY_DEFAULT = 0;
582
583     /**
584      * Lower {@link #priority}, for items that are less important. The UI may choose to show these
585      * items smaller, or at a different position in the list, compared with your app's
586      * {@link #PRIORITY_DEFAULT} items.
587      *
588      * @deprecated use {@link NotificationManager#IMPORTANCE_LOW} instead.
589      */
590     @Deprecated
591     public static final int PRIORITY_LOW = -1;
592
593     /**
594      * Lowest {@link #priority}; these items might not be shown to the user except under special
595      * circumstances, such as detailed notification logs.
596      *
597      * @deprecated use {@link NotificationManager#IMPORTANCE_MIN} instead.
598      */
599     @Deprecated
600     public static final int PRIORITY_MIN = -2;
601
602     /**
603      * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
604      * show these items larger, or at a different position in notification lists, compared with
605      * your app's {@link #PRIORITY_DEFAULT} items.
606      *
607      * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
608      */
609     @Deprecated
610     public static final int PRIORITY_HIGH = 1;
611
612     /**
613      * Highest {@link #priority}, for your application's most important items that require the
614      * user's prompt attention or input.
615      *
616      * @deprecated use {@link NotificationManager#IMPORTANCE_HIGH} instead.
617      */
618     @Deprecated
619     public static final int PRIORITY_MAX = 2;
620
621     /**
622      * Relative priority for this notification.
623      *
624      * Priority is an indication of how much of the user's valuable attention should be consumed by
625      * this notification. Low-priority notifications may be hidden from the user in certain
626      * situations, while the user might be interrupted for a higher-priority notification. The
627      * system will make a determination about how to interpret this priority when presenting
628      * the notification.
629      *
630      * <p>
631      * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
632      * as a heads-up notification.
633      * </p>
634      *
635      * @deprecated use {@link NotificationChannel#getImportance()} instead.
636      */
637     @Priority
638     @Deprecated
639     public int priority;
640
641     /**
642      * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
643      * to be applied by the standard Style templates when presenting this notification.
644      *
645      * The current template design constructs a colorful header image by overlaying the
646      * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
647      * ignored.
648      */
649     @ColorInt
650     public int color = COLOR_DEFAULT;
651
652     /**
653      * Special value of {@link #color} telling the system not to decorate this notification with
654      * any special color but instead use default colors when presenting this notification.
655      */
656     @ColorInt
657     public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
658
659     /**
660      * Special value of {@link #color} used as a place holder for an invalid color.
661      * @hide
662      */
663     @ColorInt
664     public static final int COLOR_INVALID = 1;
665
666     /**
667      * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
668      * the notification's presence and contents in untrusted situations (namely, on the secure
669      * lockscreen).
670      *
671      * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
672      * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are
673      * shown in all situations, but the contents are only available if the device is unlocked for
674      * the appropriate user.
675      *
676      * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification
677      * can be read even in an "insecure" context (that is, above a secure lockscreen).
678      * To modify the public version of this notification—for example, to redact some portions—see
679      * {@link Builder#setPublicVersion(Notification)}.
680      *
681      * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon
682      * and ticker until the user has bypassed the lockscreen.
683      */
684     public @Visibility int visibility;
685
686     /** @hide */
687     @IntDef(prefix = { "VISIBILITY_" }, value = {
688             VISIBILITY_PUBLIC,
689             VISIBILITY_PRIVATE,
690             VISIBILITY_SECRET,
691     })
692     @Retention(RetentionPolicy.SOURCE)
693     public @interface Visibility {}
694
695     /**
696      * Notification visibility: Show this notification in its entirety on all lockscreens.
697      *
698      * {@see #visibility}
699      */
700     public static final int VISIBILITY_PUBLIC = 1;
701
702     /**
703      * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
704      * private information on secure lockscreens.
705      *
706      * {@see #visibility}
707      */
708     public static final int VISIBILITY_PRIVATE = 0;
709
710     /**
711      * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
712      *
713      * {@see #visibility}
714      */
715     public static final int VISIBILITY_SECRET = -1;
716
717     /**
718      * Notification category: incoming call (voice or video) or similar synchronous communication request.
719      */
720     public static final String CATEGORY_CALL = "call";
721
722     /**
723      * Notification category: incoming direct message (SMS, instant message, etc.).
724      */
725     public static final String CATEGORY_MESSAGE = "msg";
726
727     /**
728      * Notification category: asynchronous bulk message (email).
729      */
730     public static final String CATEGORY_EMAIL = "email";
731
732     /**
733      * Notification category: calendar event.
734      */
735     public static final String CATEGORY_EVENT = "event";
736
737     /**
738      * Notification category: promotion or advertisement.
739      */
740     public static final String CATEGORY_PROMO = "promo";
741
742     /**
743      * Notification category: alarm or timer.
744      */
745     public static final String CATEGORY_ALARM = "alarm";
746
747     /**
748      * Notification category: progress of a long-running background operation.
749      */
750     public static final String CATEGORY_PROGRESS = "progress";
751
752     /**
753      * Notification category: social network or sharing update.
754      */
755     public static final String CATEGORY_SOCIAL = "social";
756
757     /**
758      * Notification category: error in background operation or authentication status.
759      */
760     public static final String CATEGORY_ERROR = "err";
761
762     /**
763      * Notification category: media transport control for playback.
764      */
765     public static final String CATEGORY_TRANSPORT = "transport";
766
767     /**
768      * Notification category: system or device status update.  Reserved for system use.
769      */
770     public static final String CATEGORY_SYSTEM = "sys";
771
772     /**
773      * Notification category: indication of running background service.
774      */
775     public static final String CATEGORY_SERVICE = "service";
776
777     /**
778      * Notification category: a specific, timely recommendation for a single thing.
779      * For example, a news app might want to recommend a news story it believes the user will
780      * want to read next.
781      */
782     public static final String CATEGORY_RECOMMENDATION = "recommendation";
783
784     /**
785      * Notification category: ongoing information about device or contextual status.
786      */
787     public static final String CATEGORY_STATUS = "status";
788
789     /**
790      * Notification category: user-scheduled reminder.
791      */
792     public static final String CATEGORY_REMINDER = "reminder";
793
794     /**
795      * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
796      * that best describes this Notification.  May be used by the system for ranking and filtering.
797      */
798     public String category;
799
800     private String mGroupKey;
801
802     /**
803      * Get the key used to group this notification into a cluster or stack
804      * with other notifications on devices which support such rendering.
805      */
806     public String getGroup() {
807         return mGroupKey;
808     }
809
810     private String mSortKey;
811
812     /**
813      * Get a sort key that orders this notification among other notifications from the
814      * same package. This can be useful if an external sort was already applied and an app
815      * would like to preserve this. Notifications will be sorted lexicographically using this
816      * value, although providing different priorities in addition to providing sort key may
817      * cause this value to be ignored.
818      *
819      * <p>This sort key can also be used to order members of a notification group. See
820      * {@link Builder#setGroup}.
821      *
822      * @see String#compareTo(String)
823      */
824     public String getSortKey() {
825         return mSortKey;
826     }
827
828     /**
829      * Additional semantic data to be carried around with this Notification.
830      * <p>
831      * The extras keys defined here are intended to capture the original inputs to {@link Builder}
832      * APIs, and are intended to be used by
833      * {@link android.service.notification.NotificationListenerService} implementations to extract
834      * detailed information from notification objects.
835      */
836     public Bundle extras = new Bundle();
837
838     /**
839      * All pending intents in the notification as the system needs to be able to access them but
840      * touching the extras bundle in the system process is not safe because the bundle may contain
841      * custom parcelable objects.
842      *
843      * @hide
844      */
845     public ArraySet<PendingIntent> allPendingIntents;
846
847     /**
848      * Token identifying the notification that is applying doze/bgcheck whitelisting to the
849      * pending intents inside of it, so only those will get the behavior.
850      *
851      * @hide
852      */
853     static public IBinder whitelistToken;
854
855     /**
856      * Must be set by a process to start associating tokens with Notification objects
857      * coming in to it.  This is set by NotificationManagerService.
858      *
859      * @hide
860      */
861     static public IBinder processWhitelistToken;
862
863     /**
864      * {@link #extras} key: this is the title of the notification,
865      * as supplied to {@link Builder#setContentTitle(CharSequence)}.
866      */
867     public static final String EXTRA_TITLE = "android.title";
868
869     /**
870      * {@link #extras} key: this is the title of the notification when shown in expanded form,
871      * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
872      */
873     public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
874
875     /**
876      * {@link #extras} key: this is the main text payload, as supplied to
877      * {@link Builder#setContentText(CharSequence)}.
878      */
879     public static final String EXTRA_TEXT = "android.text";
880
881     /**
882      * {@link #extras} key: this is a third line of text, as supplied to
883      * {@link Builder#setSubText(CharSequence)}.
884      */
885     public static final String EXTRA_SUB_TEXT = "android.subText";
886
887     /**
888      * {@link #extras} key: this is the remote input history, as supplied to
889      * {@link Builder#setRemoteInputHistory(CharSequence[])}.
890      *
891      * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
892      * with the most recent inputs that have been sent through a {@link RemoteInput} of this
893      * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
894      * notifications once the other party has responded).
895      *
896      * The extra with this key is of type CharSequence[] and contains the most recent entry at
897      * the 0 index, the second most recent at the 1 index, etc.
898      *
899      * @see Builder#setRemoteInputHistory(CharSequence[])
900      */
901     public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
902
903     /**
904      * {@link #extras} key: this is a small piece of additional text as supplied to
905      * {@link Builder#setContentInfo(CharSequence)}.
906      */
907     public static final String EXTRA_INFO_TEXT = "android.infoText";
908
909     /**
910      * {@link #extras} key: this is a line of summary information intended to be shown
911      * alongside expanded notifications, as supplied to (e.g.)
912      * {@link BigTextStyle#setSummaryText(CharSequence)}.
913      */
914     public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
915
916     /**
917      * {@link #extras} key: this is the longer text shown in the big form of a
918      * {@link BigTextStyle} notification, as supplied to
919      * {@link BigTextStyle#bigText(CharSequence)}.
920      */
921     public static final String EXTRA_BIG_TEXT = "android.bigText";
922
923     /**
924      * {@link #extras} key: this is the resource ID of the notification's main small icon, as
925      * supplied to {@link Builder#setSmallIcon(int)}.
926      *
927      * @deprecated Use {@link #getSmallIcon()}, which supports a wider variety of icon sources.
928      */
929     @Deprecated
930     public static final String EXTRA_SMALL_ICON = "android.icon";
931
932     /**
933      * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the
934      * notification payload, as
935      * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
936      *
937      * @deprecated Use {@link #getLargeIcon()}, which supports a wider variety of icon sources.
938      */
939     @Deprecated
940     public static final String EXTRA_LARGE_ICON = "android.largeIcon";
941
942     /**
943      * {@link #extras} key: this is a bitmap to be used instead of the one from
944      * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
945      * shown in its expanded form, as supplied to
946      * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
947      */
948     public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
949
950     /**
951      * {@link #extras} key: this is the progress value supplied to
952      * {@link Builder#setProgress(int, int, boolean)}.
953      */
954     public static final String EXTRA_PROGRESS = "android.progress";
955
956     /**
957      * {@link #extras} key: this is the maximum value supplied to
958      * {@link Builder#setProgress(int, int, boolean)}.
959      */
960     public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
961
962     /**
963      * {@link #extras} key: whether the progress bar is indeterminate, supplied to
964      * {@link Builder#setProgress(int, int, boolean)}.
965      */
966     public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
967
968     /**
969      * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically
970      * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to
971      * {@link Builder#setUsesChronometer(boolean)}.
972      */
973     public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
974
975     /**
976      * {@link #extras} key: whether the chronometer set on the notification should count down
977      * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
978      * This extra is a boolean. The default is false.
979      */
980     public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
981
982     /**
983      * {@link #extras} key: whether {@link #when} should be shown,
984      * as supplied to {@link Builder#setShowWhen(boolean)}.
985      */
986     public static final String EXTRA_SHOW_WHEN = "android.showWhen";
987
988     /**
989      * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
990      * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
991      */
992     public static final String EXTRA_PICTURE = "android.picture";
993
994     /**
995      * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
996      * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
997      */
998     public static final String EXTRA_TEXT_LINES = "android.textLines";
999
1000     /**
1001      * {@link #extras} key: A string representing the name of the specific
1002      * {@link android.app.Notification.Style} used to create this notification.
1003      */
1004     public static final String EXTRA_TEMPLATE = "android.template";
1005
1006     /**
1007      * {@link #extras} key: A String array containing the people that this notification relates to,
1008      * each of which was supplied to {@link Builder#addPerson(String)}.
1009      */
1010     public static final String EXTRA_PEOPLE = "android.people";
1011
1012     /**
1013      * Allow certain system-generated notifications to appear before the device is provisioned.
1014      * Only available to notifications coming from the android package.
1015      * @hide
1016      */
1017     @SystemApi
1018     @RequiresPermission(android.Manifest.permission.NOTIFICATION_DURING_SETUP)
1019     public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
1020
1021     /**
1022      * {@link #extras} key: A
1023      * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
1024      * in the background when the notification is selected. The URI must point to an image stream
1025      * suitable for passing into
1026      * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
1027      * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
1028      * URI used for this purpose must require no permissions to read the image data.
1029      */
1030     public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
1031
1032     /**
1033      * {@link #extras} key: A
1034      * {@link android.media.session.MediaSession.Token} associated with a
1035      * {@link android.app.Notification.MediaStyle} notification.
1036      */
1037     public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
1038
1039     /**
1040      * {@link #extras} key: the indices of actions to be shown in the compact view,
1041      * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
1042      */
1043     public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
1044
1045     /**
1046      * {@link #extras} key: the username to be displayed for all messages sent by the user including
1047      * direct replies
1048      * {@link android.app.Notification.MessagingStyle} notification. This extra is a
1049      * {@link CharSequence}
1050      */
1051     public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
1052
1053     /**
1054      * {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
1055      * represented by a {@link android.app.Notification.MessagingStyle}
1056      */
1057     public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
1058
1059     /**
1060      * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
1061      * bundles provided by a
1062      * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1063      * array of bundles.
1064      */
1065     public static final String EXTRA_MESSAGES = "android.messages";
1066
1067     /**
1068      * {@link #extras} key: an array of
1069      * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
1070      * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
1071      * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
1072      * array of bundles.
1073      */
1074     public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
1075
1076     /**
1077      * {@link #extras} key: whether the notification should be colorized as
1078      * supplied to {@link Builder#setColorized(boolean)}}.
1079      */
1080     public static final String EXTRA_COLORIZED = "android.colorized";
1081
1082     /**
1083      * @hide
1084      */
1085     public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
1086
1087     /**
1088      * @hide
1089      */
1090     public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
1091
1092     /**
1093      * {@link #extras} key: the audio contents of this notification.
1094      *
1095      * This is for use when rendering the notification on an audio-focused interface;
1096      * the audio contents are a complete sound sample that contains the contents/body of the
1097      * notification. This may be used in substitute of a Text-to-Speech reading of the
1098      * notification. For example if the notification represents a voice message this should point
1099      * to the audio of that message.
1100      *
1101      * The data stored under this key should be a String representation of a Uri that contains the
1102      * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
1103      *
1104      * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
1105      * has a field for holding data URI. That field can be used for audio.
1106      * See {@code Message#setData}.
1107      *
1108      * Example usage:
1109      * <pre>
1110      * {@code
1111      * Notification.Builder myBuilder = (build your Notification as normal);
1112      * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
1113      * }
1114      * </pre>
1115      */
1116     public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
1117
1118     /** @hide */
1119     @SystemApi
1120     @RequiresPermission(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME)
1121     public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
1122
1123     /**
1124      * This is set on the notification shown by the activity manager about all apps
1125      * running in the background.  It indicates that the notification should be shown
1126      * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
1127      * notification currently visible to the user.  This is a string array of all
1128      * package names of the apps.
1129      * @hide
1130      */
1131     public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
1132
1133     private Icon mSmallIcon;
1134     private Icon mLargeIcon;
1135
1136     private String mChannelId;
1137     private long mTimeout;
1138
1139     private String mShortcutId;
1140     private CharSequence mSettingsText;
1141
1142     /** @hide */
1143     @IntDef(prefix = { "GROUP_ALERT_" }, value = {
1144             GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
1145     })
1146     @Retention(RetentionPolicy.SOURCE)
1147     public @interface GroupAlertBehavior {}
1148
1149     /**
1150      * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all notifications in a
1151      * group with sound or vibration ought to make sound or vibrate (respectively), so this
1152      * notification will not be muted when it is in a group.
1153      */
1154     public static final int GROUP_ALERT_ALL = 0;
1155
1156     /**
1157      * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that all children
1158      * notification in a group should be silenced (no sound or vibration) even if they are posted
1159      * to a {@link NotificationChannel} that has sound and/or vibration. Use this constant to
1160      * mute this notification if this notification is a group child.
1161      *
1162      * <p> For example, you might want to use this constant if you post a number of children
1163      * notifications at once (say, after a periodic sync), and only need to notify the user
1164      * audibly once.
1165      */
1166     public static final int GROUP_ALERT_SUMMARY = 1;
1167
1168     /**
1169      * Constant for {@link Builder#setGroupAlertBehavior(int)}, meaning that the summary
1170      * notification in a group should be silenced (no sound or vibration) even if they are
1171      * posted to a {@link NotificationChannel} that has sound and/or vibration. Use this constant
1172      * to mute this notification if this notification is a group summary.
1173      *
1174      * <p>For example, you might want to use this constant if only the children notifications
1175      * in your group have content and the summary is only used to visually group notifications.
1176      */
1177     public static final int GROUP_ALERT_CHILDREN = 2;
1178
1179     private int mGroupAlertBehavior = GROUP_ALERT_ALL;
1180
1181     /**
1182      * If this notification is being shown as a badge, always show as a number.
1183      */
1184     public static final int BADGE_ICON_NONE = 0;
1185
1186     /**
1187      * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
1188      * represent this notification.
1189      */
1190     public static final int BADGE_ICON_SMALL = 1;
1191
1192     /**
1193      * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
1194      * represent this notification.
1195      */
1196     public static final int BADGE_ICON_LARGE = 2;
1197     private int mBadgeIcon = BADGE_ICON_NONE;
1198
1199     /**
1200      * Structure to encapsulate a named action that can be shown as part of this notification.
1201      * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
1202      * selected by the user.
1203      * <p>
1204      * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}
1205      * or {@link Notification.Builder#addAction(Notification.Action)}
1206      * to attach actions.
1207      */
1208     public static class Action implements Parcelable {
1209         /**
1210          * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
1211          * {@link RemoteInput}s.
1212          *
1213          * This is intended for {@link RemoteInput}s that only accept data, meaning
1214          * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
1215          * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
1216          * empty. These {@link RemoteInput}s will be ignored by devices that do not
1217          * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
1218          *
1219          * You can test if a RemoteInput matches these constraints using
1220          * {@link RemoteInput#isDataOnly}.
1221          */
1222         private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
1223
1224         private final Bundle mExtras;
1225         private Icon mIcon;
1226         private final RemoteInput[] mRemoteInputs;
1227         private boolean mAllowGeneratedReplies = true;
1228
1229         /**
1230          * Small icon representing the action.
1231          *
1232          * @deprecated Use {@link Action#getIcon()} instead.
1233          */
1234         @Deprecated
1235         public int icon;
1236
1237         /**
1238          * Title of the action.
1239          */
1240         public CharSequence title;
1241
1242         /**
1243          * Intent to send when the user invokes this action. May be null, in which case the action
1244          * may be rendered in a disabled presentation by the system UI.
1245          */
1246         public PendingIntent actionIntent;
1247
1248         private Action(Parcel in) {
1249             if (in.readInt() != 0) {
1250                 mIcon = Icon.CREATOR.createFromParcel(in);
1251                 if (mIcon.getType() == Icon.TYPE_RESOURCE) {
1252                     icon = mIcon.getResId();
1253                 }
1254             }
1255             title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1256             if (in.readInt() == 1) {
1257                 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
1258             }
1259             mExtras = Bundle.setDefusable(in.readBundle(), true);
1260             mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
1261             mAllowGeneratedReplies = in.readInt() == 1;
1262         }
1263
1264         /**
1265          * @deprecated Use {@link android.app.Notification.Action.Builder}.
1266          */
1267         @Deprecated
1268         public Action(int icon, CharSequence title, PendingIntent intent) {
1269             this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
1270         }
1271
1272         /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
1273         private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
1274                 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
1275             this.mIcon = icon;
1276             if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
1277                 this.icon = icon.getResId();
1278             }
1279             this.title = title;
1280             this.actionIntent = intent;
1281             this.mExtras = extras != null ? extras : new Bundle();
1282             this.mRemoteInputs = remoteInputs;
1283             this.mAllowGeneratedReplies = allowGeneratedReplies;
1284         }
1285
1286         /**
1287          * Return an icon representing the action.
1288          */
1289         public Icon getIcon() {
1290             if (mIcon == null && icon != 0) {
1291                 // you snuck an icon in here without using the builder; let's try to keep it
1292                 mIcon = Icon.createWithResource("", icon);
1293             }
1294             return mIcon;
1295         }
1296
1297         /**
1298          * Get additional metadata carried around with this Action.
1299          */
1300         public Bundle getExtras() {
1301             return mExtras;
1302         }
1303
1304         /**
1305          * Return whether the platform should automatically generate possible replies for this
1306          * {@link Action}
1307          */
1308         public boolean getAllowGeneratedReplies() {
1309             return mAllowGeneratedReplies;
1310         }
1311
1312         /**
1313          * Get the list of inputs to be collected from the user when this action is sent.
1314          * May return null if no remote inputs were added. Only returns inputs which accept
1315          * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
1316          */
1317         public RemoteInput[] getRemoteInputs() {
1318             return mRemoteInputs;
1319         }
1320
1321         /**
1322          * Get the list of inputs to be collected from the user that ONLY accept data when this
1323          * action is sent. These remote inputs are guaranteed to return true on a call to
1324          * {@link RemoteInput#isDataOnly}.
1325          *
1326          * Returns null if there are no data-only remote inputs.
1327          *
1328          * This method exists so that legacy RemoteInput collectors that pre-date the addition
1329          * of non-textual RemoteInputs do not access these remote inputs.
1330          */
1331         public RemoteInput[] getDataOnlyRemoteInputs() {
1332             return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1333         }
1334
1335         /**
1336          * Builder class for {@link Action} objects.
1337          */
1338         public static final class Builder {
1339             private final Icon mIcon;
1340             private final CharSequence mTitle;
1341             private final PendingIntent mIntent;
1342             private boolean mAllowGeneratedReplies = true;
1343             private final Bundle mExtras;
1344             private ArrayList<RemoteInput> mRemoteInputs;
1345
1346             /**
1347              * Construct a new builder for {@link Action} object.
1348              * @param icon icon to show for this action
1349              * @param title the title of the action
1350              * @param intent the {@link PendingIntent} to fire when users trigger this action
1351              */
1352             @Deprecated
1353             public Builder(int icon, CharSequence title, PendingIntent intent) {
1354                 this(Icon.createWithResource("", icon), title, intent);
1355             }
1356
1357             /**
1358              * Construct a new builder for {@link Action} object.
1359              * @param icon icon to show for this action
1360              * @param title the title of the action
1361              * @param intent the {@link PendingIntent} to fire when users trigger this action
1362              */
1363             public Builder(Icon icon, CharSequence title, PendingIntent intent) {
1364                 this(icon, title, intent, new Bundle(), null, true);
1365             }
1366
1367             /**
1368              * Construct a new builder for {@link Action} object using the fields from an
1369              * {@link Action}.
1370              * @param action the action to read fields from.
1371              */
1372             public Builder(Action action) {
1373                 this(action.getIcon(), action.title, action.actionIntent,
1374                         new Bundle(action.mExtras), action.getRemoteInputs(),
1375                         action.getAllowGeneratedReplies());
1376             }
1377
1378             private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
1379                     RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
1380                 mIcon = icon;
1381                 mTitle = title;
1382                 mIntent = intent;
1383                 mExtras = extras;
1384                 if (remoteInputs != null) {
1385                     mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
1386                     Collections.addAll(mRemoteInputs, remoteInputs);
1387                 }
1388                 mAllowGeneratedReplies = allowGeneratedReplies;
1389             }
1390
1391             /**
1392              * Merge additional metadata into this builder.
1393              *
1394              * <p>Values within the Bundle will replace existing extras values in this Builder.
1395              *
1396              * @see Notification.Action#extras
1397              */
1398             public Builder addExtras(Bundle extras) {
1399                 if (extras != null) {
1400                     mExtras.putAll(extras);
1401                 }
1402                 return this;
1403             }
1404
1405             /**
1406              * Get the metadata Bundle used by this Builder.
1407              *
1408              * <p>The returned Bundle is shared with this Builder.
1409              */
1410             public Bundle getExtras() {
1411                 return mExtras;
1412             }
1413
1414             /**
1415              * Add an input to be collected from the user when this action is sent.
1416              * Response values can be retrieved from the fired intent by using the
1417              * {@link RemoteInput#getResultsFromIntent} function.
1418              * @param remoteInput a {@link RemoteInput} to add to the action
1419              * @return this object for method chaining
1420              */
1421             public Builder addRemoteInput(RemoteInput remoteInput) {
1422                 if (mRemoteInputs == null) {
1423                     mRemoteInputs = new ArrayList<RemoteInput>();
1424                 }
1425                 mRemoteInputs.add(remoteInput);
1426                 return this;
1427             }
1428
1429             /**
1430              * Set whether the platform should automatically generate possible replies to add to
1431              * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
1432              * {@link RemoteInput}, this has no effect.
1433              * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
1434              * otherwise
1435              * @return this object for method chaining
1436              * The default value is {@code true}
1437              */
1438             public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
1439                 mAllowGeneratedReplies = allowGeneratedReplies;
1440                 return this;
1441             }
1442
1443             /**
1444              * Apply an extender to this action builder. Extenders may be used to add
1445              * metadata or change options on this builder.
1446              */
1447             public Builder extend(Extender extender) {
1448                 extender.extend(this);
1449                 return this;
1450             }
1451
1452             /**
1453              * Combine all of the options that have been set and return a new {@link Action}
1454              * object.
1455              * @return the built action
1456              */
1457             public Action build() {
1458                 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
1459                 RemoteInput[] previousDataInputs =
1460                     (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
1461                 if (previousDataInputs != null) {
1462                     for (RemoteInput input : previousDataInputs) {
1463                         dataOnlyInputs.add(input);
1464                     }
1465                 }
1466                 List<RemoteInput> textInputs = new ArrayList<>();
1467                 if (mRemoteInputs != null) {
1468                     for (RemoteInput input : mRemoteInputs) {
1469                         if (input.isDataOnly()) {
1470                             dataOnlyInputs.add(input);
1471                         } else {
1472                             textInputs.add(input);
1473                         }
1474                     }
1475                 }
1476                 if (!dataOnlyInputs.isEmpty()) {
1477                     RemoteInput[] dataInputsArr =
1478                             dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
1479                     mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
1480                 }
1481                 RemoteInput[] textInputsArr = textInputs.isEmpty()
1482                         ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
1483                 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
1484                         mAllowGeneratedReplies);
1485             }
1486         }
1487
1488         @Override
1489         public Action clone() {
1490             return new Action(
1491                     getIcon(),
1492                     title,
1493                     actionIntent, // safe to alias
1494                     mExtras == null ? new Bundle() : new Bundle(mExtras),
1495                     getRemoteInputs(),
1496                     getAllowGeneratedReplies());
1497         }
1498         @Override
1499         public int describeContents() {
1500             return 0;
1501         }
1502         @Override
1503         public void writeToParcel(Parcel out, int flags) {
1504             final Icon ic = getIcon();
1505             if (ic != null) {
1506                 out.writeInt(1);
1507                 ic.writeToParcel(out, 0);
1508             } else {
1509                 out.writeInt(0);
1510             }
1511             TextUtils.writeToParcel(title, out, flags);
1512             if (actionIntent != null) {
1513                 out.writeInt(1);
1514                 actionIntent.writeToParcel(out, flags);
1515             } else {
1516                 out.writeInt(0);
1517             }
1518             out.writeBundle(mExtras);
1519             out.writeTypedArray(mRemoteInputs, flags);
1520             out.writeInt(mAllowGeneratedReplies ? 1 : 0);
1521         }
1522         public static final Parcelable.Creator<Action> CREATOR =
1523                 new Parcelable.Creator<Action>() {
1524             public Action createFromParcel(Parcel in) {
1525                 return new Action(in);
1526             }
1527             public Action[] newArray(int size) {
1528                 return new Action[size];
1529             }
1530         };
1531
1532         /**
1533          * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
1534          * metadata or change options on an action builder.
1535          */
1536         public interface Extender {
1537             /**
1538              * Apply this extender to a notification action builder.
1539              * @param builder the builder to be modified.
1540              * @return the build object for chaining.
1541              */
1542             public Builder extend(Builder builder);
1543         }
1544
1545         /**
1546          * Wearable extender for notification actions. To add extensions to an action,
1547          * create a new {@link android.app.Notification.Action.WearableExtender} object using
1548          * the {@code WearableExtender()} constructor and apply it to a
1549          * {@link android.app.Notification.Action.Builder} using
1550          * {@link android.app.Notification.Action.Builder#extend}.
1551          *
1552          * <pre class="prettyprint">
1553          * Notification.Action action = new Notification.Action.Builder(
1554          *         R.drawable.archive_all, "Archive all", actionIntent)
1555          *         .extend(new Notification.Action.WearableExtender()
1556          *                 .setAvailableOffline(false))
1557          *         .build();</pre>
1558          */
1559         public static final class WearableExtender implements Extender {
1560             /** Notification action extra which contains wearable extensions */
1561             private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
1562
1563             // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
1564             private static final String KEY_FLAGS = "flags";
1565             private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
1566             private static final String KEY_CONFIRM_LABEL = "confirmLabel";
1567             private static final String KEY_CANCEL_LABEL = "cancelLabel";
1568
1569             // Flags bitwise-ored to mFlags
1570             private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
1571             private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
1572             private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
1573
1574             // Default value for flags integer
1575             private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
1576
1577             private int mFlags = DEFAULT_FLAGS;
1578
1579             private CharSequence mInProgressLabel;
1580             private CharSequence mConfirmLabel;
1581             private CharSequence mCancelLabel;
1582
1583             /**
1584              * Create a {@link android.app.Notification.Action.WearableExtender} with default
1585              * options.
1586              */
1587             public WearableExtender() {
1588             }
1589
1590             /**
1591              * Create a {@link android.app.Notification.Action.WearableExtender} by reading
1592              * wearable options present in an existing notification action.
1593              * @param action the notification action to inspect.
1594              */
1595             public WearableExtender(Action action) {
1596                 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
1597                 if (wearableBundle != null) {
1598                     mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
1599                     mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
1600                     mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
1601                     mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
1602                 }
1603             }
1604
1605             /**
1606              * Apply wearable extensions to a notification action that is being built. This is
1607              * typically called by the {@link android.app.Notification.Action.Builder#extend}
1608              * method of {@link android.app.Notification.Action.Builder}.
1609              */
1610             @Override
1611             public Action.Builder extend(Action.Builder builder) {
1612                 Bundle wearableBundle = new Bundle();
1613
1614                 if (mFlags != DEFAULT_FLAGS) {
1615                     wearableBundle.putInt(KEY_FLAGS, mFlags);
1616                 }
1617                 if (mInProgressLabel != null) {
1618                     wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
1619                 }
1620                 if (mConfirmLabel != null) {
1621                     wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
1622                 }
1623                 if (mCancelLabel != null) {
1624                     wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
1625                 }
1626
1627                 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
1628                 return builder;
1629             }
1630
1631             @Override
1632             public WearableExtender clone() {
1633                 WearableExtender that = new WearableExtender();
1634                 that.mFlags = this.mFlags;
1635                 that.mInProgressLabel = this.mInProgressLabel;
1636                 that.mConfirmLabel = this.mConfirmLabel;
1637                 that.mCancelLabel = this.mCancelLabel;
1638                 return that;
1639             }
1640
1641             /**
1642              * Set whether this action is available when the wearable device is not connected to
1643              * a companion device. The user can still trigger this action when the wearable device is
1644              * offline, but a visual hint will indicate that the action may not be available.
1645              * Defaults to true.
1646              */
1647             public WearableExtender setAvailableOffline(boolean availableOffline) {
1648                 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
1649                 return this;
1650             }
1651
1652             /**
1653              * Get whether this action is available when the wearable device is not connected to
1654              * a companion device. The user can still trigger this action when the wearable device is
1655              * offline, but a visual hint will indicate that the action may not be available.
1656              * Defaults to true.
1657              */
1658             public boolean isAvailableOffline() {
1659                 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
1660             }
1661
1662             private void setFlag(int mask, boolean value) {
1663                 if (value) {
1664                     mFlags |= mask;
1665                 } else {
1666                     mFlags &= ~mask;
1667                 }
1668             }
1669
1670             /**
1671              * Set a label to display while the wearable is preparing to automatically execute the
1672              * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1673              *
1674              * @param label the label to display while the action is being prepared to execute
1675              * @return this object for method chaining
1676              */
1677             public WearableExtender setInProgressLabel(CharSequence label) {
1678                 mInProgressLabel = label;
1679                 return this;
1680             }
1681
1682             /**
1683              * Get the label to display while the wearable is preparing to automatically execute
1684              * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
1685              *
1686              * @return the label to display while the action is being prepared to execute
1687              */
1688             public CharSequence getInProgressLabel() {
1689                 return mInProgressLabel;
1690             }
1691
1692             /**
1693              * Set a label to display to confirm that the action should be executed.
1694              * This is usually an imperative verb like "Send".
1695              *
1696              * @param label the label to confirm the action should be executed
1697              * @return this object for method chaining
1698              */
1699             public WearableExtender setConfirmLabel(CharSequence label) {
1700                 mConfirmLabel = label;
1701                 return this;
1702             }
1703
1704             /**
1705              * Get the label to display to confirm that the action should be executed.
1706              * This is usually an imperative verb like "Send".
1707              *
1708              * @return the label to confirm the action should be executed
1709              */
1710             public CharSequence getConfirmLabel() {
1711                 return mConfirmLabel;
1712             }
1713
1714             /**
1715              * Set a label to display to cancel the action.
1716              * This is usually an imperative verb, like "Cancel".
1717              *
1718              * @param label the label to display to cancel the action
1719              * @return this object for method chaining
1720              */
1721             public WearableExtender setCancelLabel(CharSequence label) {
1722                 mCancelLabel = label;
1723                 return this;
1724             }
1725
1726             /**
1727              * Get the label to display to cancel the action.
1728              * This is usually an imperative verb like "Cancel".
1729              *
1730              * @return the label to display to cancel the action
1731              */
1732             public CharSequence getCancelLabel() {
1733                 return mCancelLabel;
1734             }
1735
1736             /**
1737              * Set a hint that this Action will launch an {@link Activity} directly, telling the
1738              * platform that it can generate the appropriate transitions.
1739              * @param hintLaunchesActivity {@code true} if the content intent will launch
1740              * an activity and transitions should be generated, false otherwise.
1741              * @return this object for method chaining
1742              */
1743             public WearableExtender setHintLaunchesActivity(
1744                     boolean hintLaunchesActivity) {
1745                 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
1746                 return this;
1747             }
1748
1749             /**
1750              * Get a hint that this Action will launch an {@link Activity} directly, telling the
1751              * platform that it can generate the appropriate transitions
1752              * @return {@code true} if the content intent will launch an activity and transitions
1753              * should be generated, false otherwise. The default value is {@code false} if this was
1754              * never set.
1755              */
1756             public boolean getHintLaunchesActivity() {
1757                 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
1758             }
1759
1760             /**
1761              * Set a hint that this Action should be displayed inline.
1762              *
1763              * @param hintDisplayInline {@code true} if action should be displayed inline, false
1764              *        otherwise
1765              * @return this object for method chaining
1766              */
1767             public WearableExtender setHintDisplayActionInline(
1768                     boolean hintDisplayInline) {
1769                 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
1770                 return this;
1771             }
1772
1773             /**
1774              * Get a hint that this Action should be displayed inline.
1775              *
1776              * @return {@code true} if the Action should be displayed inline, {@code false}
1777              *         otherwise. The default value is {@code false} if this was never set.
1778              */
1779             public boolean getHintDisplayActionInline() {
1780                 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
1781             }
1782         }
1783     }
1784
1785     /**
1786      * Array of all {@link Action} structures attached to this notification by
1787      * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of
1788      * {@link android.service.notification.NotificationListenerService} that provide an alternative
1789      * interface for invoking actions.
1790      */
1791     public Action[] actions;
1792
1793     /**
1794      * Replacement version of this notification whose content will be shown
1795      * in an insecure context such as atop a secure keyguard. See {@link #visibility}
1796      * and {@link #VISIBILITY_PUBLIC}.
1797      */
1798     public Notification publicVersion;
1799
1800     /**
1801      * Constructs a Notification object with default values.
1802      * You might want to consider using {@link Builder} instead.
1803      */
1804     public Notification()
1805     {
1806         this.when = System.currentTimeMillis();
1807         this.creationTime = System.currentTimeMillis();
1808         this.priority = PRIORITY_DEFAULT;
1809     }
1810
1811     /**
1812      * @hide
1813      */
1814     public Notification(Context context, int icon, CharSequence tickerText, long when,
1815             CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
1816     {
1817         new Builder(context)
1818                 .setWhen(when)
1819                 .setSmallIcon(icon)
1820                 .setTicker(tickerText)
1821                 .setContentTitle(contentTitle)
1822                 .setContentText(contentText)
1823                 .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
1824                 .buildInto(this);
1825     }
1826
1827     /**
1828      * Constructs a Notification object with the information needed to
1829      * have a status bar icon without the standard expanded view.
1830      *
1831      * @param icon          The resource id of the icon to put in the status bar.
1832      * @param tickerText    The text that flows by in the status bar when the notification first
1833      *                      activates.
1834      * @param when          The time to show in the time field.  In the System.currentTimeMillis
1835      *                      timebase.
1836      *
1837      * @deprecated Use {@link Builder} instead.
1838      */
1839     @Deprecated
1840     public Notification(int icon, CharSequence tickerText, long when)
1841     {
1842         this.icon = icon;
1843         this.tickerText = tickerText;
1844         this.when = when;
1845         this.creationTime = System.currentTimeMillis();
1846     }
1847
1848     /**
1849      * Unflatten the notification from a parcel.
1850      */
1851     @SuppressWarnings("unchecked")
1852     public Notification(Parcel parcel) {
1853         // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
1854         // intents in extras are always written as the last entry.
1855         readFromParcelImpl(parcel);
1856         // Must be read last!
1857         allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
1858     }
1859
1860     private void readFromParcelImpl(Parcel parcel)
1861     {
1862         int version = parcel.readInt();
1863
1864         whitelistToken = parcel.readStrongBinder();
1865         if (whitelistToken == null) {
1866             whitelistToken = processWhitelistToken;
1867         }
1868         // Propagate this token to all pending intents that are unmarshalled from the parcel.
1869         parcel.setClassCookie(PendingIntent.class, whitelistToken);
1870
1871         when = parcel.readLong();
1872         creationTime = parcel.readLong();
1873         if (parcel.readInt() != 0) {
1874             mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
1875             if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
1876                 icon = mSmallIcon.getResId();
1877             }
1878         }
1879         number = parcel.readInt();
1880         if (parcel.readInt() != 0) {
1881             contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1882         }
1883         if (parcel.readInt() != 0) {
1884             deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1885         }
1886         if (parcel.readInt() != 0) {
1887             tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1888         }
1889         if (parcel.readInt() != 0) {
1890             tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
1891         }
1892         if (parcel.readInt() != 0) {
1893             contentView = RemoteViews.CREATOR.createFromParcel(parcel);
1894         }
1895         if (parcel.readInt() != 0) {
1896             mLargeIcon = Icon.CREATOR.createFromParcel(parcel);
1897         }
1898         defaults = parcel.readInt();
1899         flags = parcel.readInt();
1900         if (parcel.readInt() != 0) {
1901             sound = Uri.CREATOR.createFromParcel(parcel);
1902         }
1903
1904         audioStreamType = parcel.readInt();
1905         if (parcel.readInt() != 0) {
1906             audioAttributes = AudioAttributes.CREATOR.createFromParcel(parcel);
1907         }
1908         vibrate = parcel.createLongArray();
1909         ledARGB = parcel.readInt();
1910         ledOnMS = parcel.readInt();
1911         ledOffMS = parcel.readInt();
1912         iconLevel = parcel.readInt();
1913
1914         if (parcel.readInt() != 0) {
1915             fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
1916         }
1917
1918         priority = parcel.readInt();
1919
1920         category = parcel.readString();
1921
1922         mGroupKey = parcel.readString();
1923
1924         mSortKey = parcel.readString();
1925
1926         extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null
1927
1928         actions = parcel.createTypedArray(Action.CREATOR); // may be null
1929
1930         if (parcel.readInt() != 0) {
1931             bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1932         }
1933
1934         if (parcel.readInt() != 0) {
1935             headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel);
1936         }
1937
1938         visibility = parcel.readInt();
1939
1940         if (parcel.readInt() != 0) {
1941             publicVersion = Notification.CREATOR.createFromParcel(parcel);
1942         }
1943
1944         color = parcel.readInt();
1945
1946         if (parcel.readInt() != 0) {
1947             mChannelId = parcel.readString();
1948         }
1949         mTimeout = parcel.readLong();
1950
1951         if (parcel.readInt() != 0) {
1952             mShortcutId = parcel.readString();
1953         }
1954
1955         mBadgeIcon = parcel.readInt();
1956
1957         if (parcel.readInt() != 0) {
1958             mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
1959         }
1960
1961         mGroupAlertBehavior = parcel.readInt();
1962     }
1963
1964     @Override
1965     public Notification clone() {
1966         Notification that = new Notification();
1967         cloneInto(that, true);
1968         return that;
1969     }
1970
1971     /**
1972      * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
1973      * of this into that.
1974      * @hide
1975      */
1976     public void cloneInto(Notification that, boolean heavy) {
1977         that.whitelistToken = this.whitelistToken;
1978         that.when = this.when;
1979         that.creationTime = this.creationTime;
1980         that.mSmallIcon = this.mSmallIcon;
1981         that.number = this.number;
1982
1983         // PendingIntents are global, so there's no reason (or way) to clone them.
1984         that.contentIntent = this.contentIntent;
1985         that.deleteIntent = this.deleteIntent;
1986         that.fullScreenIntent = this.fullScreenIntent;
1987
1988         if (this.tickerText != null) {
1989             that.tickerText = this.tickerText.toString();
1990         }
1991         if (heavy && this.tickerView != null) {
1992             that.tickerView = this.tickerView.clone();
1993         }
1994         if (heavy && this.contentView != null) {
1995             that.contentView = this.contentView.clone();
1996         }
1997         if (heavy && this.mLargeIcon != null) {
1998             that.mLargeIcon = this.mLargeIcon;
1999         }
2000         that.iconLevel = this.iconLevel;
2001         that.sound = this.sound; // android.net.Uri is immutable
2002         that.audioStreamType = this.audioStreamType;
2003         if (this.audioAttributes != null) {
2004             that.audioAttributes = new AudioAttributes.Builder(this.audioAttributes).build();
2005         }
2006
2007         final long[] vibrate = this.vibrate;
2008         if (vibrate != null) {
2009             final int N = vibrate.length;
2010             final long[] vib = that.vibrate = new long[N];
2011             System.arraycopy(vibrate, 0, vib, 0, N);
2012         }
2013
2014         that.ledARGB = this.ledARGB;
2015         that.ledOnMS = this.ledOnMS;
2016         that.ledOffMS = this.ledOffMS;
2017         that.defaults = this.defaults;
2018
2019         that.flags = this.flags;
2020
2021         that.priority = this.priority;
2022
2023         that.category = this.category;
2024
2025         that.mGroupKey = this.mGroupKey;
2026
2027         that.mSortKey = this.mSortKey;
2028
2029         if (this.extras != null) {
2030             try {
2031                 that.extras = new Bundle(this.extras);
2032                 // will unparcel
2033                 that.extras.size();
2034             } catch (BadParcelableException e) {
2035                 Log.e(TAG, "could not unparcel extras from notification: " + this, e);
2036                 that.extras = null;
2037             }
2038         }
2039
2040         if (!ArrayUtils.isEmpty(allPendingIntents)) {
2041             that.allPendingIntents = new ArraySet<>(allPendingIntents);
2042         }
2043
2044         if (this.actions != null) {
2045             that.actions = new Action[this.actions.length];
2046             for(int i=0; i<this.actions.length; i++) {
2047                 if ( this.actions[i] != null) {
2048                     that.actions[i] = this.actions[i].clone();
2049                 }
2050             }
2051         }
2052
2053         if (heavy && this.bigContentView != null) {
2054             that.bigContentView = this.bigContentView.clone();
2055         }
2056
2057         if (heavy && this.headsUpContentView != null) {
2058             that.headsUpContentView = this.headsUpContentView.clone();
2059         }
2060
2061         that.visibility = this.visibility;
2062
2063         if (this.publicVersion != null) {
2064             that.publicVersion = new Notification();
2065             this.publicVersion.cloneInto(that.publicVersion, heavy);
2066         }
2067
2068         that.color = this.color;
2069
2070         that.mChannelId = this.mChannelId;
2071         that.mTimeout = this.mTimeout;
2072         that.mShortcutId = this.mShortcutId;
2073         that.mBadgeIcon = this.mBadgeIcon;
2074         that.mSettingsText = this.mSettingsText;
2075         that.mGroupAlertBehavior = this.mGroupAlertBehavior;
2076
2077         if (!heavy) {
2078             that.lightenPayload(); // will clean out extras
2079         }
2080     }
2081
2082     /**
2083      * Removes heavyweight parts of the Notification object for archival or for sending to
2084      * listeners when the full contents are not necessary.
2085      * @hide
2086      */
2087     public final void lightenPayload() {
2088         tickerView = null;
2089         contentView = null;
2090         bigContentView = null;
2091         headsUpContentView = null;
2092         mLargeIcon = null;
2093         if (extras != null && !extras.isEmpty()) {
2094             final Set<String> keyset = extras.keySet();
2095             final int N = keyset.size();
2096             final String[] keys = keyset.toArray(new String[N]);
2097             for (int i=0; i<N; i++) {
2098                 final String key = keys[i];
2099                 if (TvExtender.EXTRA_TV_EXTENDER.equals(key)) {
2100                     continue;
2101                 }
2102                 final Object obj = extras.get(key);
2103                 if (obj != null &&
2104                     (  obj instanceof Parcelable
2105                     || obj instanceof Parcelable[]
2106                     || obj instanceof SparseArray
2107                     || obj instanceof ArrayList)) {
2108                     extras.remove(key);
2109                 }
2110             }
2111         }
2112     }
2113
2114     /**
2115      * Make sure this CharSequence is safe to put into a bundle, which basically
2116      * means it had better not be some custom Parcelable implementation.
2117      * @hide
2118      */
2119     public static CharSequence safeCharSequence(CharSequence cs) {
2120         if (cs == null) return cs;
2121         if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
2122             cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
2123         }
2124         if (cs instanceof Parcelable) {
2125             Log.e(TAG, "warning: " + cs.getClass().getCanonicalName()
2126                     + " instance is a custom Parcelable and not allowed in Notification");
2127             return cs.toString();
2128         }
2129         return removeTextSizeSpans(cs);
2130     }
2131
2132     private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
2133         if (charSequence instanceof Spanned) {
2134             Spanned ss = (Spanned) charSequence;
2135             Object[] spans = ss.getSpans(0, ss.length(), Object.class);
2136             SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
2137             for (Object span : spans) {
2138                 Object resultSpan = span;
2139                 if (resultSpan instanceof CharacterStyle) {
2140                     resultSpan = ((CharacterStyle) span).getUnderlying();
2141                 }
2142                 if (resultSpan instanceof TextAppearanceSpan) {
2143                     TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
2144                     resultSpan = new TextAppearanceSpan(
2145                             originalSpan.getFamily(),
2146                             originalSpan.getTextStyle(),
2147                             -1,
2148                             originalSpan.getTextColor(),
2149                             originalSpan.getLinkTextColor());
2150                 } else if (resultSpan instanceof RelativeSizeSpan
2151                         || resultSpan instanceof AbsoluteSizeSpan) {
2152                     continue;
2153                 } else {
2154                     resultSpan = span;
2155                 }
2156                 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
2157                         ss.getSpanFlags(span));
2158             }
2159             return builder;
2160         }
2161         return charSequence;
2162     }
2163
2164     public int describeContents() {
2165         return 0;
2166     }
2167
2168     /**
2169      * Flatten this notification into a parcel.
2170      */
2171     public void writeToParcel(Parcel parcel, int flags) {
2172         // We need to mark all pending intents getting into the notification
2173         // system as being put there to later allow the notification ranker
2174         // to launch them and by doing so add the app to the battery saver white
2175         // list for a short period of time. The problem is that the system
2176         // cannot look into the extras as there may be parcelables there that
2177         // the platform does not know how to handle. To go around that we have
2178         // an explicit list of the pending intents in the extras bundle.
2179         final boolean collectPendingIntents = (allPendingIntents == null);
2180         if (collectPendingIntents) {
2181             PendingIntent.setOnMarshaledListener(
2182                     (PendingIntent intent, Parcel out, int outFlags) -> {
2183                 if (parcel == out) {
2184                     if (allPendingIntents == null) {
2185                         allPendingIntents = new ArraySet<>();
2186                     }
2187                     allPendingIntents.add(intent);
2188                 }
2189             });
2190         }
2191         try {
2192             // IMPORTANT: Add marshaling code in writeToParcelImpl as we
2193             // want to intercept all pending events written to the parcel.
2194             writeToParcelImpl(parcel, flags);
2195             // Must be written last!
2196             parcel.writeArraySet(allPendingIntents);
2197         } finally {
2198             if (collectPendingIntents) {
2199                 PendingIntent.setOnMarshaledListener(null);
2200             }
2201         }
2202     }
2203
2204     private void writeToParcelImpl(Parcel parcel, int flags) {
2205         parcel.writeInt(1);
2206
2207         parcel.writeStrongBinder(whitelistToken);
2208         parcel.writeLong(when);
2209         parcel.writeLong(creationTime);
2210         if (mSmallIcon == null && icon != 0) {
2211             // you snuck an icon in here without using the builder; let's try to keep it
2212             mSmallIcon = Icon.createWithResource("", icon);
2213         }
2214         if (mSmallIcon != null) {
2215             parcel.writeInt(1);
2216             mSmallIcon.writeToParcel(parcel, 0);
2217         } else {
2218             parcel.writeInt(0);
2219         }
2220         parcel.writeInt(number);
2221         if (contentIntent != null) {
2222             parcel.writeInt(1);
2223             contentIntent.writeToParcel(parcel, 0);
2224         } else {
2225             parcel.writeInt(0);
2226         }
2227         if (deleteIntent != null) {
2228             parcel.writeInt(1);
2229             deleteIntent.writeToParcel(parcel, 0);
2230         } else {
2231             parcel.writeInt(0);
2232         }
2233         if (tickerText != null) {
2234             parcel.writeInt(1);
2235             TextUtils.writeToParcel(tickerText, parcel, flags);
2236         } else {
2237             parcel.writeInt(0);
2238         }
2239         if (tickerView != null) {
2240             parcel.writeInt(1);
2241             tickerView.writeToParcel(parcel, 0);
2242         } else {
2243             parcel.writeInt(0);
2244         }
2245         if (contentView != null) {
2246             parcel.writeInt(1);
2247             contentView.writeToParcel(parcel, 0);
2248         } else {
2249             parcel.writeInt(0);
2250         }
2251         if (mLargeIcon == null && largeIcon != null) {
2252             // you snuck an icon in here without using the builder; let's try to keep it
2253             mLargeIcon = Icon.createWithBitmap(largeIcon);
2254         }
2255         if (mLargeIcon != null) {
2256             parcel.writeInt(1);
2257             mLargeIcon.writeToParcel(parcel, 0);
2258         } else {
2259             parcel.writeInt(0);
2260         }
2261
2262         parcel.writeInt(defaults);
2263         parcel.writeInt(this.flags);
2264
2265         if (sound != null) {
2266             parcel.writeInt(1);
2267             sound.writeToParcel(parcel, 0);
2268         } else {
2269             parcel.writeInt(0);
2270         }
2271         parcel.writeInt(audioStreamType);
2272
2273         if (audioAttributes != null) {
2274             parcel.writeInt(1);
2275             audioAttributes.writeToParcel(parcel, 0);
2276         } else {
2277             parcel.writeInt(0);
2278         }
2279
2280         parcel.writeLongArray(vibrate);
2281         parcel.writeInt(ledARGB);
2282         parcel.writeInt(ledOnMS);
2283         parcel.writeInt(ledOffMS);
2284         parcel.writeInt(iconLevel);
2285
2286         if (fullScreenIntent != null) {
2287             parcel.writeInt(1);
2288             fullScreenIntent.writeToParcel(parcel, 0);
2289         } else {
2290             parcel.writeInt(0);
2291         }
2292
2293         parcel.writeInt(priority);
2294
2295         parcel.writeString(category);
2296
2297         parcel.writeString(mGroupKey);
2298
2299         parcel.writeString(mSortKey);
2300
2301         parcel.writeBundle(extras); // null ok
2302
2303         parcel.writeTypedArray(actions, 0); // null ok
2304
2305         if (bigContentView != null) {
2306             parcel.writeInt(1);
2307             bigContentView.writeToParcel(parcel, 0);
2308         } else {
2309             parcel.writeInt(0);
2310         }
2311
2312         if (headsUpContentView != null) {
2313             parcel.writeInt(1);
2314             headsUpContentView.writeToParcel(parcel, 0);
2315         } else {
2316             parcel.writeInt(0);
2317         }
2318
2319         parcel.writeInt(visibility);
2320
2321         if (publicVersion != null) {
2322             parcel.writeInt(1);
2323             publicVersion.writeToParcel(parcel, 0);
2324         } else {
2325             parcel.writeInt(0);
2326         }
2327
2328         parcel.writeInt(color);
2329
2330         if (mChannelId != null) {
2331             parcel.writeInt(1);
2332             parcel.writeString(mChannelId);
2333         } else {
2334             parcel.writeInt(0);
2335         }
2336         parcel.writeLong(mTimeout);
2337
2338         if (mShortcutId != null) {
2339             parcel.writeInt(1);
2340             parcel.writeString(mShortcutId);
2341         } else {
2342             parcel.writeInt(0);
2343         }
2344
2345         parcel.writeInt(mBadgeIcon);
2346
2347         if (mSettingsText != null) {
2348             parcel.writeInt(1);
2349             TextUtils.writeToParcel(mSettingsText, parcel, flags);
2350         } else {
2351             parcel.writeInt(0);
2352         }
2353
2354         parcel.writeInt(mGroupAlertBehavior);
2355     }
2356
2357     /**
2358      * Parcelable.Creator that instantiates Notification objects
2359      */
2360     public static final Parcelable.Creator<Notification> CREATOR
2361             = new Parcelable.Creator<Notification>()
2362     {
2363         public Notification createFromParcel(Parcel parcel)
2364         {
2365             return new Notification(parcel);
2366         }
2367
2368         public Notification[] newArray(int size)
2369         {
2370             return new Notification[size];
2371         }
2372     };
2373
2374     /**
2375      * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
2376      * layout.
2377      *
2378      * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
2379      * in the view.</p>
2380      * @param context       The context for your application / activity.
2381      * @param contentTitle The title that goes in the expanded entry.
2382      * @param contentText  The text that goes in the expanded entry.
2383      * @param contentIntent The intent to launch when the user clicks the expanded notification.
2384      * If this is an activity, it must include the
2385      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
2386      * that you take care of task management as described in the
2387      * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
2388      * Stack</a> document.
2389      *
2390      * @deprecated Use {@link Builder} instead.
2391      * @removed
2392      */
2393     @Deprecated
2394     public void setLatestEventInfo(Context context,
2395             CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
2396         if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
2397             Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
2398                     new Throwable());
2399         }
2400
2401         if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2402             extras.putBoolean(EXTRA_SHOW_WHEN, true);
2403         }
2404
2405         // ensure that any information already set directly is preserved
2406         final Notification.Builder builder = new Notification.Builder(context, this);
2407
2408         // now apply the latestEventInfo fields
2409         if (contentTitle != null) {
2410             builder.setContentTitle(contentTitle);
2411         }
2412         if (contentText != null) {
2413             builder.setContentText(contentText);
2414         }
2415         builder.setContentIntent(contentIntent);
2416
2417         builder.build(); // callers expect this notification to be ready to use
2418     }
2419
2420     /**
2421      * @hide
2422      */
2423     public static void addFieldsFromContext(Context context, Notification notification) {
2424         addFieldsFromContext(context.getApplicationInfo(), notification);
2425     }
2426
2427     /**
2428      * @hide
2429      */
2430     public static void addFieldsFromContext(ApplicationInfo ai, Notification notification) {
2431         notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
2432     }
2433
2434     @Override
2435     public String toString() {
2436         StringBuilder sb = new StringBuilder();
2437         sb.append("Notification(channel=");
2438         sb.append(getChannelId());
2439         sb.append(" pri=");
2440         sb.append(priority);
2441         sb.append(" contentView=");
2442         if (contentView != null) {
2443             sb.append(contentView.getPackage());
2444             sb.append("/0x");
2445             sb.append(Integer.toHexString(contentView.getLayoutId()));
2446         } else {
2447             sb.append("null");
2448         }
2449         sb.append(" vibrate=");
2450         if ((this.defaults & DEFAULT_VIBRATE) != 0) {
2451             sb.append("default");
2452         } else if (this.vibrate != null) {
2453             int N = this.vibrate.length-1;
2454             sb.append("[");
2455             for (int i=0; i<N; i++) {
2456                 sb.append(this.vibrate[i]);
2457                 sb.append(',');
2458             }
2459             if (N != -1) {
2460                 sb.append(this.vibrate[N]);
2461             }
2462             sb.append("]");
2463         } else {
2464             sb.append("null");
2465         }
2466         sb.append(" sound=");
2467         if ((this.defaults & DEFAULT_SOUND) != 0) {
2468             sb.append("default");
2469         } else if (this.sound != null) {
2470             sb.append(this.sound.toString());
2471         } else {
2472             sb.append("null");
2473         }
2474         if (this.tickerText != null) {
2475             sb.append(" tick");
2476         }
2477         sb.append(" defaults=0x");
2478         sb.append(Integer.toHexString(this.defaults));
2479         sb.append(" flags=0x");
2480         sb.append(Integer.toHexString(this.flags));
2481         sb.append(String.format(" color=0x%08x", this.color));
2482         if (this.category != null) {
2483             sb.append(" category=");
2484             sb.append(this.category);
2485         }
2486         if (this.mGroupKey != null) {
2487             sb.append(" groupKey=");
2488             sb.append(this.mGroupKey);
2489         }
2490         if (this.mSortKey != null) {
2491             sb.append(" sortKey=");
2492             sb.append(this.mSortKey);
2493         }
2494         if (actions != null) {
2495             sb.append(" actions=");
2496             sb.append(actions.length);
2497         }
2498         sb.append(" vis=");
2499         sb.append(visibilityToString(this.visibility));
2500         if (this.publicVersion != null) {
2501             sb.append(" publicVersion=");
2502             sb.append(publicVersion.toString());
2503         }
2504         sb.append(")");
2505         return sb.toString();
2506     }
2507
2508     /**
2509      * {@hide}
2510      */
2511     public static String visibilityToString(int vis) {
2512         switch (vis) {
2513             case VISIBILITY_PRIVATE:
2514                 return "PRIVATE";
2515             case VISIBILITY_PUBLIC:
2516                 return "PUBLIC";
2517             case VISIBILITY_SECRET:
2518                 return "SECRET";
2519             default:
2520                 return "UNKNOWN(" + String.valueOf(vis) + ")";
2521         }
2522     }
2523
2524     /**
2525      * {@hide}
2526      */
2527     public static String priorityToString(@Priority int pri) {
2528         switch (pri) {
2529             case PRIORITY_MIN:
2530                 return "MIN";
2531             case PRIORITY_LOW:
2532                 return "LOW";
2533             case PRIORITY_DEFAULT:
2534                 return "DEFAULT";
2535             case PRIORITY_HIGH:
2536                 return "HIGH";
2537             case PRIORITY_MAX:
2538                 return "MAX";
2539             default:
2540                 return "UNKNOWN(" + String.valueOf(pri) + ")";
2541         }
2542     }
2543
2544     /** @removed */
2545     @Deprecated
2546     public String getChannel() {
2547         return mChannelId;
2548     }
2549
2550     /**
2551      * Returns the id of the channel this notification posts to.
2552      */
2553     public String getChannelId() {
2554         return mChannelId;
2555     }
2556
2557     /** @removed */
2558     @Deprecated
2559     public long getTimeout() {
2560         return mTimeout;
2561     }
2562
2563     /**
2564      * Returns the duration from posting after which this notification should be canceled by the
2565      * system, if it's not canceled already.
2566      */
2567     public long getTimeoutAfter() {
2568         return mTimeout;
2569     }
2570
2571     /**
2572      * Returns what icon should be shown for this notification if it is being displayed in a
2573      * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
2574      * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
2575      */
2576     public int getBadgeIconType() {
2577         return mBadgeIcon;
2578     }
2579
2580     /**
2581      * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
2582      *
2583      * <p>Used by some Launchers that display notification content to hide shortcuts that duplicate
2584      * notifications.
2585      */
2586     public String getShortcutId() {
2587         return mShortcutId;
2588     }
2589
2590
2591     /**
2592      * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
2593      */
2594     public CharSequence getSettingsText() {
2595         return mSettingsText;
2596     }
2597
2598     /**
2599      * Returns which type of notifications in a group are responsible for audibly alerting the
2600      * user. See {@link #GROUP_ALERT_ALL}, {@link #GROUP_ALERT_CHILDREN},
2601      * {@link #GROUP_ALERT_SUMMARY}.
2602      */
2603     public @GroupAlertBehavior int getGroupAlertBehavior() {
2604         return mGroupAlertBehavior;
2605     }
2606
2607     /**
2608      * The small icon representing this notification in the status bar and content view.
2609      *
2610      * @return the small icon representing this notification.
2611      *
2612      * @see Builder#getSmallIcon()
2613      * @see Builder#setSmallIcon(Icon)
2614      */
2615     public Icon getSmallIcon() {
2616         return mSmallIcon;
2617     }
2618
2619     /**
2620      * Used when notifying to clean up legacy small icons.
2621      * @hide
2622      */
2623     public void setSmallIcon(Icon icon) {
2624         mSmallIcon = icon;
2625     }
2626
2627     /**
2628      * The large icon shown in this notification's content view.
2629      * @see Builder#getLargeIcon()
2630      * @see Builder#setLargeIcon(Icon)
2631      */
2632     public Icon getLargeIcon() {
2633         return mLargeIcon;
2634     }
2635
2636     /**
2637      * @hide
2638      */
2639     public boolean isGroupSummary() {
2640         return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
2641     }
2642
2643     /**
2644      * @hide
2645      */
2646     public boolean isGroupChild() {
2647         return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) == 0;
2648     }
2649
2650     /**
2651      * @hide
2652      */
2653     public boolean suppressAlertingDueToGrouping() {
2654         if (isGroupSummary()
2655                 && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
2656             return true;
2657         } else if (isGroupChild()
2658                 && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
2659             return true;
2660         }
2661         return false;
2662     }
2663
2664     /**
2665      * Builder class for {@link Notification} objects.
2666      *
2667      * Provides a convenient way to set the various fields of a {@link Notification} and generate
2668      * content views using the platform's notification layout template. If your app supports
2669      * versions of Android as old as API level 4, you can instead use
2670      * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
2671      * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
2672      * library</a>.
2673      *
2674      * <p>Example:
2675      *
2676      * <pre class="prettyprint">
2677      * Notification noti = new Notification.Builder(mContext)
2678      *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
2679      *         .setContentText(subject)
2680      *         .setSmallIcon(R.drawable.new_mail)
2681      *         .setLargeIcon(aBitmap)
2682      *         .build();
2683      * </pre>
2684      */
2685     public static class Builder {
2686         /**
2687          * @hide
2688          */
2689         public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
2690                 "android.rebuild.contentViewActionCount";
2691         /**
2692          * @hide
2693          */
2694         public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
2695                 = "android.rebuild.bigViewActionCount";
2696         /**
2697          * @hide
2698          */
2699         public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
2700                 = "android.rebuild.hudViewActionCount";
2701
2702         private static final int MAX_ACTION_BUTTONS = 3;
2703
2704         private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
2705                 SystemProperties.getBoolean("notifications.only_title", true);
2706
2707         /**
2708          * The lightness difference that has to be added to the primary text color to obtain the
2709          * secondary text color when the background is light.
2710          */
2711         private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
2712
2713         /**
2714          * The lightness difference that has to be added to the primary text color to obtain the
2715          * secondary text color when the background is dark.
2716          * A bit less then the above value, since it looks better on dark backgrounds.
2717          */
2718         private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
2719
2720         private Context mContext;
2721         private Notification mN;
2722         private Bundle mUserExtras = new Bundle();
2723         private Style mStyle;
2724         private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
2725         private ArrayList<String> mPersonList = new ArrayList<String>();
2726         private NotificationColorUtil mColorUtil;
2727         private boolean mIsLegacy;
2728         private boolean mIsLegacyInitialized;
2729
2730         /**
2731          * Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
2732          */
2733         private int mCachedContrastColor = COLOR_INVALID;
2734         private int mCachedContrastColorIsFor = COLOR_INVALID;
2735         /**
2736          * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
2737          */
2738         private int mCachedAmbientColor = COLOR_INVALID;
2739         private int mCachedAmbientColorIsFor = COLOR_INVALID;
2740
2741         /**
2742          * Caches an instance of StandardTemplateParams. Note that this may have been used before,
2743          * so make sure to call {@link StandardTemplateParams#reset()} before using it.
2744          */
2745         StandardTemplateParams mParams = new StandardTemplateParams();
2746         private int mTextColorsAreForBackground = COLOR_INVALID;
2747         private int mPrimaryTextColor = COLOR_INVALID;
2748         private int mSecondaryTextColor = COLOR_INVALID;
2749         private int mActionBarColor = COLOR_INVALID;
2750         private int mBackgroundColor = COLOR_INVALID;
2751         private int mForegroundColor = COLOR_INVALID;
2752         private int mBackgroundColorHint = COLOR_INVALID;
2753         /**
2754          * A temporary location where actions are stored. If != null the view originally has action
2755          * but doesn't have any for this inflation.
2756          */
2757         private ArrayList<Action> mOriginalActions;
2758         private boolean mRebuildStyledRemoteViews;
2759
2760         /**
2761          * Constructs a new Builder with the defaults:
2762          *
2763          * @param context
2764          *            A {@link Context} that will be used by the Builder to construct the
2765          *            RemoteViews. The Context will not be held past the lifetime of this Builder
2766          *            object.
2767          * @param channelId
2768          *            The constructed Notification will be posted on this
2769          *            {@link NotificationChannel}. To use a NotificationChannel, it must first be
2770          *            created using {@link NotificationManager#createNotificationChannel}.
2771          */
2772         public Builder(Context context, String channelId) {
2773             this(context, (Notification) null);
2774             mN.mChannelId = channelId;
2775         }
2776
2777         /**
2778          * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
2779          * instead. All posted Notifications must specify a NotificationChannel Id.
2780          */
2781         @Deprecated
2782         public Builder(Context context) {
2783             this(context, (Notification) null);
2784         }
2785
2786         /**
2787          * @hide
2788          */
2789         public Builder(Context context, Notification toAdopt) {
2790             mContext = context;
2791
2792             if (toAdopt == null) {
2793                 mN = new Notification();
2794                 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
2795                     mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
2796                 }
2797                 mN.priority = PRIORITY_DEFAULT;
2798                 mN.visibility = VISIBILITY_PRIVATE;
2799             } else {
2800                 mN = toAdopt;
2801                 if (mN.actions != null) {
2802                     Collections.addAll(mActions, mN.actions);
2803                 }
2804
2805                 if (mN.extras.containsKey(EXTRA_PEOPLE)) {
2806                     Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
2807                 }
2808
2809                 if (mN.getSmallIcon() == null && mN.icon != 0) {
2810                     setSmallIcon(mN.icon);
2811                 }
2812
2813                 if (mN.getLargeIcon() == null && mN.largeIcon != null) {
2814                     setLargeIcon(mN.largeIcon);
2815                 }
2816
2817                 String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
2818                 if (!TextUtils.isEmpty(templateClass)) {
2819                     final Class<? extends Style> styleClass
2820                             = getNotificationStyleClass(templateClass);
2821                     if (styleClass == null) {
2822                         Log.d(TAG, "Unknown style class: " + templateClass);
2823                     } else {
2824                         try {
2825                             final Constructor<? extends Style> ctor =
2826                                     styleClass.getDeclaredConstructor();
2827                             ctor.setAccessible(true);
2828                             final Style style = ctor.newInstance();
2829                             style.restoreFromExtras(mN.extras);
2830
2831                             if (style != null) {
2832                                 setStyle(style);
2833                             }
2834                         } catch (Throwable t) {
2835                             Log.e(TAG, "Could not create Style", t);
2836                         }
2837                     }
2838                 }
2839
2840             }
2841         }
2842
2843         private NotificationColorUtil getColorUtil() {
2844             if (mColorUtil == null) {
2845                 mColorUtil = NotificationColorUtil.getInstance(mContext);
2846             }
2847             return mColorUtil;
2848         }
2849
2850         /**
2851          * If this notification is duplicative of a Launcher shortcut, sets the
2852          * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
2853          * the shortcut.
2854          *
2855          * This field will be ignored by Launchers that don't support badging, don't show
2856          * notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
2857          *
2858          * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
2859          *                   supersedes
2860          */
2861         public Builder setShortcutId(String shortcutId) {
2862             mN.mShortcutId = shortcutId;
2863             return this;
2864         }
2865
2866         /**
2867          * Sets which icon to display as a badge for this notification.
2868          *
2869          * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
2870          * {@link #BADGE_ICON_LARGE}.
2871          *
2872          * Note: This value might be ignored, for launchers that don't support badge icons.
2873          */
2874         public Builder setBadgeIconType(int icon) {
2875             mN.mBadgeIcon = icon;
2876             return this;
2877         }
2878
2879         /**
2880          * Sets the group alert behavior for this notification. Use this method to mute this
2881          * notification if alerts for this notification's group should be handled by a different
2882          * notification. This is only applicable for notifications that belong to a
2883          * {@link #setGroup(String) group}.
2884          *
2885          * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
2886          */
2887         public Builder setGroupAlertBehavior(@GroupAlertBehavior int groupAlertBehavior) {
2888             mN.mGroupAlertBehavior = groupAlertBehavior;
2889             return this;
2890         }
2891
2892         /** @removed */
2893         @Deprecated
2894         public Builder setChannel(String channelId) {
2895             mN.mChannelId = channelId;
2896             return this;
2897         }
2898
2899         /**
2900          * Specifies the channel the notification should be delivered on.
2901          */
2902         public Builder setChannelId(String channelId) {
2903             mN.mChannelId = channelId;
2904             return this;
2905         }
2906
2907         /** @removed */
2908         @Deprecated
2909         public Builder setTimeout(long durationMs) {
2910             mN.mTimeout = durationMs;
2911             return this;
2912         }
2913
2914         /**
2915          * Specifies a duration in milliseconds after which this notification should be canceled,
2916          * if it is not already canceled.
2917          */
2918         public Builder setTimeoutAfter(long durationMs) {
2919             mN.mTimeout = durationMs;
2920             return this;
2921         }
2922
2923         /**
2924          * Add a timestamp pertaining to the notification (usually the time the event occurred).
2925          *
2926          * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
2927          * shown anymore by default and must be opted into by using
2928          * {@link android.app.Notification.Builder#setShowWhen(boolean)}
2929          *
2930          * @see Notification#when
2931          */
2932         public Builder setWhen(long when) {
2933             mN.when = when;
2934             return this;
2935         }
2936
2937         /**
2938          * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
2939          * in the content view.
2940          * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
2941          * {@code false}. For earlier apps, the default is {@code true}.
2942          */
2943         public Builder setShowWhen(boolean show) {
2944             mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
2945             return this;
2946         }
2947
2948         /**
2949          * Show the {@link Notification#when} field as a stopwatch.
2950          *
2951          * Instead of presenting <code>when</code> as a timestamp, the notification will show an
2952          * automatically updating display of the minutes and seconds since <code>when</code>.
2953          *
2954          * Useful when showing an elapsed time (like an ongoing phone call).
2955          *
2956          * The counter can also be set to count down to <code>when</code> when using
2957          * {@link #setChronometerCountDown(boolean)}.
2958          *
2959          * @see android.widget.Chronometer
2960          * @see Notification#when
2961          * @see #setChronometerCountDown(boolean)
2962          */
2963         public Builder setUsesChronometer(boolean b) {
2964             mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
2965             return this;
2966         }
2967
2968         /**
2969          * Sets the Chronometer to count down instead of counting up.
2970          *
2971          * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
2972          * If it isn't set the chronometer will count up.
2973          *
2974          * @see #setUsesChronometer(boolean)
2975          */
2976         public Builder setChronometerCountDown(boolean countDown) {
2977             mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, countDown);
2978             return this;
2979         }
2980
2981         /**
2982          * Set the small icon resource, which will be used to represent the notification in the
2983          * status bar.
2984          *
2985
2986          * The platform template for the expanded view will draw this icon in the left, unless a
2987          * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
2988          * icon will be moved to the right-hand side.
2989          *
2990
2991          * @param icon
2992          *            A resource ID in the application's package of the drawable to use.
2993          * @see Notification#icon
2994          */
2995         public Builder setSmallIcon(@DrawableRes int icon) {
2996             return setSmallIcon(icon != 0
2997                     ? Icon.createWithResource(mContext, icon)
2998                     : null);
2999         }
3000
3001         /**
3002          * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
3003          * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
3004          * LevelListDrawable}.
3005          *
3006          * @param icon A resource ID in the application's package of the drawable to use.
3007          * @param level The level to use for the icon.
3008          *
3009          * @see Notification#icon
3010          * @see Notification#iconLevel
3011          */
3012         public Builder setSmallIcon(@DrawableRes int icon, int level) {
3013             mN.iconLevel = level;
3014             return setSmallIcon(icon);
3015         }
3016
3017         /**
3018          * Set the small icon, which will be used to represent the notification in the
3019          * status bar and content view (unless overriden there by a
3020          * {@link #setLargeIcon(Bitmap) large icon}).
3021          *
3022          * @param icon An Icon object to use.
3023          * @see Notification#icon
3024          */
3025         public Builder setSmallIcon(Icon icon) {
3026             mN.setSmallIcon(icon);
3027             if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
3028                 mN.icon = icon.getResId();
3029             }
3030             return this;
3031         }
3032
3033         /**
3034          * Set the first line of text in the platform notification template.
3035          */
3036         public Builder setContentTitle(CharSequence title) {
3037             mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
3038             return this;
3039         }
3040
3041         /**
3042          * Set the second line of text in the platform notification template.
3043          */
3044         public Builder setContentText(CharSequence text) {
3045             mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
3046             return this;
3047         }
3048
3049         /**
3050          * This provides some additional information that is displayed in the notification. No
3051          * guarantees are given where exactly it is displayed.
3052          *
3053          * <p>This information should only be provided if it provides an essential
3054          * benefit to the understanding of the notification. The more text you provide the
3055          * less readable it becomes. For example, an email client should only provide the account
3056          * name here if more than one email account has been added.</p>
3057          *
3058          * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
3059          * notification header area.
3060          *
3061          * On Android versions before {@link android.os.Build.VERSION_CODES#N}
3062          * this will be shown in the third line of text in the platform notification template.
3063          * You should not be using {@link #setProgress(int, int, boolean)} at the
3064          * same time on those versions; they occupy the same place.
3065          * </p>
3066          */
3067         public Builder setSubText(CharSequence text) {
3068             mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
3069             return this;
3070         }
3071
3072         /**
3073          * Provides text that will appear as a link to your application's settings.
3074          *
3075          * <p>This text does not appear within notification {@link Style templates} but may
3076          * appear when the user uses an affordance to learn more about the notification.
3077          * Additionally, this text will not appear unless you provide a valid link target by
3078          * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
3079          *
3080          * <p>This text is meant to be concise description about what the user can customize
3081          * when they click on this link. The recommended maximum length is 40 characters.
3082          * @param text
3083          * @return
3084          */
3085         public Builder setSettingsText(CharSequence text) {
3086             mN.mSettingsText = safeCharSequence(text);
3087             return this;
3088         }
3089
3090         /**
3091          * Set the remote input history.
3092          *
3093          * This should be set to the most recent inputs that have been sent
3094          * through a {@link RemoteInput} of this Notification and cleared once the it is no
3095          * longer relevant (e.g. for chat notifications once the other party has responded).
3096          *
3097          * The most recent input must be stored at the 0 index, the second most recent at the
3098          * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
3099          * and how much of each individual input is shown.
3100          *
3101          * <p>Note: The reply text will only be shown on notifications that have least one action
3102          * with a {@code RemoteInput}.</p>
3103          */
3104         public Builder setRemoteInputHistory(CharSequence[] text) {
3105             if (text == null) {
3106                 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
3107             } else {
3108                 final int N = Math.min(MAX_REPLY_HISTORY, text.length);
3109                 CharSequence[] safe = new CharSequence[N];
3110                 for (int i = 0; i < N; i++) {
3111                     safe[i] = safeCharSequence(text[i]);
3112                 }
3113                 mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
3114             }
3115             return this;
3116         }
3117
3118         /**
3119          * Sets the number of items this notification represents. May be displayed as a badge count
3120          * for Launchers that support badging.
3121          */
3122         public Builder setNumber(int number) {
3123             mN.number = number;
3124             return this;
3125         }
3126
3127         /**
3128          * A small piece of additional information pertaining to this notification.
3129          *
3130          * The platform template will draw this on the last line of the notification, at the far
3131          * right (to the right of a smallIcon if it has been placed there).
3132          *
3133          * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
3134          * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
3135          * field will still show up, but the subtext will take precedence.
3136          */
3137         @Deprecated
3138         public Builder setContentInfo(CharSequence info) {
3139             mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
3140             return this;
3141         }
3142
3143         /**
3144          * Set the progress this notification represents.
3145          *
3146          * The platform template will represent this using a {@link ProgressBar}.
3147          */
3148         public Builder setProgress(int max, int progress, boolean indeterminate) {
3149             mN.extras.putInt(EXTRA_PROGRESS, progress);
3150             mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
3151             mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
3152             return this;
3153         }
3154
3155         /**
3156          * Supply a custom RemoteViews to use instead of the platform template.
3157          *
3158          * Use {@link #setCustomContentView(RemoteViews)} instead.
3159          */
3160         @Deprecated
3161         public Builder setContent(RemoteViews views) {
3162             return setCustomContentView(views);
3163         }
3164
3165         /**
3166          * Supply custom RemoteViews to use instead of the platform template.
3167          *
3168          * This will override the layout that would otherwise be constructed by this Builder
3169          * object.
3170          */
3171         public Builder setCustomContentView(RemoteViews contentView) {
3172             mN.contentView = contentView;
3173             return this;
3174         }
3175
3176         /**
3177          * Supply custom RemoteViews to use instead of the platform template in the expanded form.
3178          *
3179          * This will override the expanded layout that would otherwise be constructed by this
3180          * Builder object.
3181          */
3182         public Builder setCustomBigContentView(RemoteViews contentView) {
3183             mN.bigContentView = contentView;
3184             return this;
3185         }
3186
3187         /**
3188          * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
3189          *
3190          * This will override the heads-up layout that would otherwise be constructed by this
3191          * Builder object.
3192          */
3193         public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
3194             mN.headsUpContentView = contentView;
3195             return this;
3196         }
3197
3198         /**
3199          * Supply a {@link PendingIntent} to be sent when the notification is clicked.
3200          *
3201          * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
3202          * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
3203          * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
3204          * to assign PendingIntents to individual views in that custom layout (i.e., to create
3205          * clickable buttons inside the notification view).
3206          *
3207          * @see Notification#contentIntent Notification.contentIntent
3208          */
3209         public Builder setContentIntent(PendingIntent intent) {
3210             mN.contentIntent = intent;
3211             return this;
3212         }
3213
3214         /**
3215          * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
3216          *
3217          * @see Notification#deleteIntent
3218          */
3219         public Builder setDeleteIntent(PendingIntent intent) {
3220             mN.deleteIntent = intent;
3221             return this;
3222         }
3223
3224         /**
3225          * An intent to launch instead of posting the notification to the status bar.
3226          * Only for use with extremely high-priority notifications demanding the user's
3227          * <strong>immediate</strong> attention, such as an incoming phone call or
3228          * alarm clock that the user has explicitly set to a particular time.
3229          * If this facility is used for something else, please give the user an option
3230          * to turn it off and use a normal notification, as this can be extremely
3231          * disruptive.
3232          *
3233          * <p>
3234          * The system UI may choose to display a heads-up notification, instead of
3235          * launching this intent, while the user is using the device.
3236          * </p>
3237          *
3238          * @param intent The pending intent to launch.
3239          * @param highPriority Passing true will cause this notification to be sent
3240          *          even if other notifications are suppressed.
3241          *
3242          * @see Notification#fullScreenIntent
3243          */
3244         public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
3245             mN.fullScreenIntent = intent;
3246             setFlag(FLAG_HIGH_PRIORITY, highPriority);
3247             return this;
3248         }
3249
3250         /**
3251          * Set the "ticker" text which is sent to accessibility services.
3252          *
3253          * @see Notification#tickerText
3254          */
3255         public Builder setTicker(CharSequence tickerText) {
3256             mN.tickerText = safeCharSequence(tickerText);
3257             return this;
3258         }
3259
3260         /**
3261          * Obsolete version of {@link #setTicker(CharSequence)}.
3262          *
3263          */
3264         @Deprecated
3265         public Builder setTicker(CharSequence tickerText, RemoteViews views) {
3266             setTicker(tickerText);
3267             // views is ignored
3268             return this;
3269         }
3270
3271         /**
3272          * Add a large icon to the notification content view.
3273          *
3274          * In the platform template, this image will be shown on the left of the notification view
3275          * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3276          * badge atop the large icon).
3277          */
3278         public Builder setLargeIcon(Bitmap b) {
3279             return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
3280         }
3281
3282         /**
3283          * Add a large icon to the notification content view.
3284          *
3285          * In the platform template, this image will be shown on the left of the notification view
3286          * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small
3287          * badge atop the large icon).
3288          */
3289         public Builder setLargeIcon(Icon icon) {
3290             mN.mLargeIcon = icon;
3291             mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
3292             return this;
3293         }
3294
3295         /**
3296          * Set the sound to play.
3297          *
3298          * It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}
3299          * for notifications.
3300          *
3301          * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
3302          */
3303         @Deprecated
3304         public Builder setSound(Uri sound) {
3305             mN.sound = sound;
3306             mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
3307             return this;
3308         }
3309
3310         /**
3311          * Set the sound to play, along with a specific stream on which to play it.
3312          *
3313          * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
3314          *
3315          * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)}.
3316          */
3317         @Deprecated
3318         public Builder setSound(Uri sound, int streamType) {
3319             PlayerBase.deprecateStreamTypeForPlayback(streamType, "Notification", "setSound()");
3320             mN.sound = sound;
3321             mN.audioStreamType = streamType;
3322             return this;
3323         }
3324
3325         /**
3326          * Set the sound to play, along with specific {@link AudioAttributes audio attributes} to
3327          * use during playback.
3328          *
3329          * @deprecated use {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
3330          * @see Notification#sound
3331          */
3332         @Deprecated
3333         public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
3334             mN.sound = sound;
3335             mN.audioAttributes = audioAttributes;
3336             return this;
3337         }
3338
3339         /**
3340          * Set the vibration pattern to use.
3341          *
3342          * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
3343          * <code>pattern</code> parameter.
3344          *
3345          * <p>
3346          * A notification that vibrates is more likely to be presented as a heads-up notification.
3347          * </p>
3348          *
3349          * @deprecated use {@link NotificationChannel#setVibrationPattern(long[])} instead.
3350          * @see Notification#vibrate
3351          */
3352         @Deprecated
3353         public Builder setVibrate(long[] pattern) {
3354             mN.vibrate = pattern;
3355             return this;
3356         }
3357
3358         /**
3359          * Set the desired color for the indicator LED on the device, as well as the
3360          * blink duty cycle (specified in milliseconds).
3361          *
3362
3363          * Not all devices will honor all (or even any) of these values.
3364          *
3365          * @deprecated use {@link NotificationChannel#enableLights(boolean)} instead.
3366          * @see Notification#ledARGB
3367          * @see Notification#ledOnMS
3368          * @see Notification#ledOffMS
3369          */
3370         @Deprecated
3371         public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
3372             mN.ledARGB = argb;
3373             mN.ledOnMS = onMs;
3374             mN.ledOffMS = offMs;
3375             if (onMs != 0 || offMs != 0) {
3376                 mN.flags |= FLAG_SHOW_LIGHTS;
3377             }
3378             return this;
3379         }
3380
3381         /**
3382          * Set whether this is an "ongoing" notification.
3383          *
3384
3385          * Ongoing notifications cannot be dismissed by the user, so your application or service
3386          * must take care of canceling them.
3387          *
3388
3389          * They are typically used to indicate a background task that the user is actively engaged
3390          * with (e.g., playing music) or is pending in some way and therefore occupying the device
3391          * (e.g., a file download, sync operation, active network connection).
3392          *
3393
3394          * @see Notification#FLAG_ONGOING_EVENT
3395          * @see Service#setForeground(boolean)
3396          */
3397         public Builder setOngoing(boolean ongoing) {
3398             setFlag(FLAG_ONGOING_EVENT, ongoing);
3399             return this;
3400         }
3401
3402         /**
3403          * Set whether this notification should be colorized. When set, the color set with
3404          * {@link #setColor(int)} will be used as the background color of this notification.
3405          * <p>
3406          * This should only be used for high priority ongoing tasks like navigation, an ongoing
3407          * call, or other similarly high-priority events for the user.
3408          * <p>
3409          * For most styles, the coloring will only be applied if the notification is for a
3410          * foreground service notification.
3411          * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
3412          * that have a media session attached there is no such requirement.
3413          *
3414          * @see Builder#setColor(int)
3415          * @see MediaStyle#setMediaSession(MediaSession.Token)
3416          */
3417         public Builder setColorized(boolean colorize) {
3418             mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
3419             return this;
3420         }
3421
3422         /**
3423          * Set this flag if you would only like the sound, vibrate
3424          * and ticker to be played if the notification is not already showing.
3425          *
3426          * @see Notification#FLAG_ONLY_ALERT_ONCE
3427          */
3428         public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
3429             setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
3430             return this;
3431         }
3432
3433         /**
3434          * Make this notification automatically dismissed when the user touches it.
3435          *
3436          * @see Notification#FLAG_AUTO_CANCEL
3437          */
3438         public Builder setAutoCancel(boolean autoCancel) {
3439             setFlag(FLAG_AUTO_CANCEL, autoCancel);
3440             return this;
3441         }
3442
3443         /**
3444          * Set whether or not this notification should not bridge to other devices.
3445          *
3446          * <p>Some notifications can be bridged to other devices for remote display.
3447          * This hint can be set to recommend this notification not be bridged.
3448          */
3449         public Builder setLocalOnly(boolean localOnly) {
3450             setFlag(FLAG_LOCAL_ONLY, localOnly);
3451             return this;
3452         }
3453
3454         /**
3455          * Set which notification properties will be inherited from system defaults.
3456          * <p>
3457          * The value should be one or more of the following fields combined with
3458          * bitwise-or:
3459          * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
3460          * <p>
3461          * For all default values, use {@link #DEFAULT_ALL}.
3462          *
3463          * @deprecated use {@link NotificationChannel#enableVibration(boolean)} and
3464          * {@link NotificationChannel#enableLights(boolean)} and
3465          * {@link NotificationChannel#setSound(Uri, AudioAttributes)} instead.
3466          */
3467         @Deprecated
3468         public Builder setDefaults(int defaults) {
3469             mN.defaults = defaults;
3470             return this;
3471         }
3472
3473         /**
3474          * Set the priority of this notification.
3475          *
3476          * @see Notification#priority
3477          * @deprecated use {@link NotificationChannel#setImportance(int)} instead.
3478          */
3479         @Deprecated
3480         public Builder setPriority(@Priority int pri) {
3481             mN.priority = pri;
3482             return this;
3483         }
3484
3485         /**
3486          * Set the notification category.
3487          *
3488          * @see Notification#category
3489          */
3490         public Builder setCategory(String category) {
3491             mN.category = category;
3492             return this;
3493         }
3494
3495         /**
3496          * Add a person that is relevant to this notification.
3497          *
3498          * <P>
3499          * Depending on user preferences, this annotation may allow the notification to pass
3500          * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
3501          * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
3502          * appear more prominently in the user interface.
3503          * </P>
3504          *
3505          * <P>
3506          * The person should be specified by the {@code String} representation of a
3507          * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
3508          * </P>
3509          *
3510          * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
3511          * URIs.  The path part of these URIs must exist in the contacts database, in the
3512          * appropriate column, or the reference will be discarded as invalid. Telephone schema
3513          * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
3514          * </P>
3515          *
3516          * @param uri A URI for the person.
3517          * @see Notification#EXTRA_PEOPLE
3518          */
3519         public Builder addPerson(String uri) {
3520             mPersonList.add(uri);
3521             return this;
3522         }
3523
3524         /**
3525          * Set this notification to be part of a group of notifications sharing the same key.
3526          * Grouped notifications may display in a cluster or stack on devices which
3527          * support such rendering.
3528          *
3529          * <p>To make this notification the summary for its group, also call
3530          * {@link #setGroupSummary}. A sort order can be specified for group members by using
3531          * {@link #setSortKey}.
3532          * @param groupKey The group key of the group.
3533          * @return this object for method chaining
3534          */
3535         public Builder setGroup(String groupKey) {
3536             mN.mGroupKey = groupKey;
3537             return this;
3538         }
3539
3540         /**
3541          * Set this notification to be the group summary for a group of notifications.
3542          * Grouped notifications may display in a cluster or stack on devices which
3543          * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
3544          * The group summary may be suppressed if too few notifications are included in the group.
3545          * @param isGroupSummary Whether this notification should be a group summary.
3546          * @return this object for method chaining
3547          */
3548         public Builder setGroupSummary(boolean isGroupSummary) {
3549             setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
3550             return this;
3551         }
3552
3553         /**
3554          * Set a sort key that orders this notification among other notifications from the
3555          * same package. This can be useful if an external sort was already applied and an app
3556          * would like to preserve this. Notifications will be sorted lexicographically using this
3557          * value, although providing different priorities in addition to providing sort key may
3558          * cause this value to be ignored.
3559          *
3560          * <p>This sort key can also be used to order members of a notification group. See
3561          * {@link #setGroup}.
3562          *
3563          * @see String#compareTo(String)
3564          */
3565         public Builder setSortKey(String sortKey) {
3566             mN.mSortKey = sortKey;
3567             return this;
3568         }
3569
3570         /**
3571          * Merge additional metadata into this notification.
3572          *
3573          * <p>Values within the Bundle will replace existing extras values in this Builder.
3574          *
3575          * @see Notification#extras
3576          */
3577         public Builder addExtras(Bundle extras) {
3578             if (extras != null) {
3579                 mUserExtras.putAll(extras);
3580             }
3581             return this;
3582         }
3583
3584         /**
3585          * Set metadata for this notification.
3586          *
3587          * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
3588          * current contents are copied into the Notification each time {@link #build()} is
3589          * called.
3590          *
3591          * <p>Replaces any existing extras values with those from the provided Bundle.
3592          * Use {@link #addExtras} to merge in metadata instead.
3593          *
3594          * @see Notification#extras
3595          */
3596         public Builder setExtras(Bundle extras) {
3597             if (extras != null) {
3598                 mUserExtras = extras;
3599             }
3600             return this;
3601         }
3602
3603         /**
3604          * Get the current metadata Bundle used by this notification Builder.
3605          *
3606          * <p>The returned Bundle is shared with this Builder.
3607          *
3608          * <p>The current contents of this Bundle are copied into the Notification each time
3609          * {@link #build()} is called.
3610          *
3611          * @see Notification#extras
3612          */
3613         public Bundle getExtras() {
3614             return mUserExtras;
3615         }
3616
3617         private Bundle getAllExtras() {
3618             final Bundle saveExtras = (Bundle) mUserExtras.clone();
3619             saveExtras.putAll(mN.extras);
3620             return saveExtras;
3621         }
3622
3623         /**
3624          * Add an action to this notification. Actions are typically displayed by
3625          * the system as a button adjacent to the notification content.
3626          * <p>
3627          * Every action must have an icon (32dp square and matching the
3628          * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3629          * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3630          * <p>
3631          * A notification in its expanded form can display up to 3 actions, from left to right in
3632          * the order they were added. Actions will not be displayed when the notification is
3633          * collapsed, however, so be sure that any essential functions may be accessed by the user
3634          * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3635          *
3636          * @param icon Resource ID of a drawable that represents the action.
3637          * @param title Text describing the action.
3638          * @param intent PendingIntent to be fired when the action is invoked.
3639          *
3640          * @deprecated Use {@link #addAction(Action)} instead.
3641          */
3642         @Deprecated
3643         public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
3644             mActions.add(new Action(icon, safeCharSequence(title), intent));
3645             return this;
3646         }
3647
3648         /**
3649          * Add an action to this notification. Actions are typically displayed by
3650          * the system as a button adjacent to the notification content.
3651          * <p>
3652          * Every action must have an icon (32dp square and matching the
3653          * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo
3654          * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.
3655          * <p>
3656          * A notification in its expanded form can display up to 3 actions, from left to right in
3657          * the order they were added. Actions will not be displayed when the notification is
3658          * collapsed, however, so be sure that any essential functions may be accessed by the user
3659          * in some other way (for example, in the Activity pointed to by {@link #contentIntent}).
3660          *
3661          * @param action The action to add.
3662          */
3663         public Builder addAction(Action action) {
3664             if (action != null) {
3665                 mActions.add(action);
3666             }
3667             return this;
3668         }
3669
3670         /**
3671          * Alter the complete list of actions attached to this notification.
3672          * @see #addAction(Action).
3673          *
3674          * @param actions
3675          * @return
3676          */
3677         public Builder setActions(Action... actions) {
3678             mActions.clear();
3679             for (int i = 0; i < actions.length; i++) {
3680                 if (actions[i] != null) {
3681                     mActions.add(actions[i]);
3682                 }
3683             }
3684             return this;
3685         }
3686
3687         /**
3688          * Add a rich notification style to be applied at build time.
3689          *
3690          * @param style Object responsible for modifying the notification style.
3691          */
3692         public Builder setStyle(Style style) {
3693             if (mStyle != style) {
3694                 mStyle = style;
3695                 if (mStyle != null) {
3696                     mStyle.setBuilder(this);
3697                     mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
3698                 }  else {
3699                     mN.extras.remove(EXTRA_TEMPLATE);
3700                 }
3701             }
3702             return this;
3703         }
3704
3705         /**
3706          * Specify the value of {@link #visibility}.
3707          *
3708          * @return The same Builder.
3709          */
3710         public Builder setVisibility(@Visibility int visibility) {
3711             mN.visibility = visibility;
3712             return this;
3713         }
3714
3715         /**
3716          * Supply a replacement Notification whose contents should be shown in insecure contexts
3717          * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.
3718          * @param n A replacement notification, presumably with some or all info redacted.
3719          * @return The same Builder.
3720          */
3721         public Builder setPublicVersion(Notification n) {
3722             if (n != null) {
3723                 mN.publicVersion = new Notification();
3724                 n.cloneInto(mN.publicVersion, /*heavy=*/ true);
3725             } else {
3726                 mN.publicVersion = null;
3727             }
3728             return this;
3729         }
3730
3731         /**
3732          * Apply an extender to this notification builder. Extenders may be used to add
3733          * metadata or change options on this builder.
3734          */
3735         public Builder extend(Extender extender) {
3736             extender.extend(this);
3737             return this;
3738         }
3739
3740         /**
3741          * @hide
3742          */
3743         public Builder setFlag(int mask, boolean value) {
3744             if (value) {
3745                 mN.flags |= mask;
3746             } else {
3747                 mN.flags &= ~mask;
3748             }
3749             return this;
3750         }
3751
3752         /**
3753          * Sets {@link Notification#color}.
3754          *
3755          * @param argb The accent color to use
3756          *
3757          * @return The same Builder.
3758          */
3759         public Builder setColor(@ColorInt int argb) {
3760             mN.color = argb;
3761             sanitizeColor();
3762             return this;
3763         }
3764
3765         private Drawable getProfileBadgeDrawable() {
3766             if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
3767                 // This user can never be a badged profile,
3768                 // and also includes USER_ALL system notifications.
3769                 return null;
3770             }
3771             // Note: This assumes that the current user can read the profile badge of the
3772             // originating user.
3773             return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
3774                     new UserHandle(mContext.getUserId()), 0);
3775         }
3776
3777         private Bitmap getProfileBadge() {
3778             Drawable badge = getProfileBadgeDrawable();
3779             if (badge == null) {
3780                 return null;
3781             }
3782             final int size = mContext.getResources().getDimensionPixelSize(
3783                     R.dimen.notification_badge_size);
3784             Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
3785             Canvas canvas = new Canvas(bitmap);
3786             badge.setBounds(0, 0, size, size);
3787             badge.draw(canvas);
3788             return bitmap;
3789         }
3790
3791         private void bindProfileBadge(RemoteViews contentView) {
3792             Bitmap profileBadge = getProfileBadge();
3793
3794             if (profileBadge != null) {
3795                 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
3796                 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
3797                 if (isColorized()) {
3798                     contentView.setDrawableParameters(R.id.profile_badge, false, -1,
3799                             getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
3800                 }
3801             }
3802         }
3803
3804         private void resetStandardTemplate(RemoteViews contentView) {
3805             resetNotificationHeader(contentView);
3806             resetContentMargins(contentView);
3807             contentView.setViewVisibility(R.id.right_icon, View.GONE);
3808             contentView.setViewVisibility(R.id.title, View.GONE);
3809             contentView.setTextViewText(R.id.title, null);
3810             contentView.setViewVisibility(R.id.text, View.GONE);
3811             contentView.setTextViewText(R.id.text, null);
3812             contentView.setViewVisibility(R.id.text_line_1, View.GONE);
3813             contentView.setTextViewText(R.id.text_line_1, null);
3814         }
3815
3816         /**
3817          * Resets the notification header to its original state
3818          */
3819         private void resetNotificationHeader(RemoteViews contentView) {
3820             // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
3821             // re-using the drawable when the notification is updated.
3822             contentView.setBoolean(R.id.notification_header, "setExpanded", false);
3823             contentView.setTextViewText(R.id.app_name_text, null);
3824             contentView.setViewVisibility(R.id.chronometer, View.GONE);
3825             contentView.setViewVisibility(R.id.header_text, View.GONE);
3826             contentView.setTextViewText(R.id.header_text, null);
3827             contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
3828             contentView.setViewVisibility(R.id.time_divider, View.GONE);
3829             contentView.setViewVisibility(R.id.time, View.GONE);
3830             contentView.setImageViewIcon(R.id.profile_badge, null);
3831             contentView.setViewVisibility(R.id.profile_badge, View.GONE);
3832         }
3833
3834         private void resetContentMargins(RemoteViews contentView) {
3835             contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
3836             contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
3837         }
3838
3839         private RemoteViews applyStandardTemplate(int resId) {
3840             return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
3841         }
3842
3843         /**
3844          * @param hasProgress whether the progress bar should be shown and set
3845          */
3846         private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
3847             return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
3848                     .fillTextsFrom(this));
3849         }
3850
3851         private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
3852             RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
3853
3854             resetStandardTemplate(contentView);
3855
3856             final Bundle ex = mN.extras;
3857             updateBackgroundColor(contentView);
3858             bindNotificationHeader(contentView, p.ambient);
3859             bindLargeIcon(contentView);
3860             boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
3861             if (p.title != null) {
3862                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
3863                 contentView.setTextViewText(R.id.title, p.title);
3864                 if (!p.ambient) {
3865                     setTextViewColorPrimary(contentView, R.id.title);
3866                 }
3867                 contentView.setViewLayoutWidth(R.id.title, showProgress
3868                         ? ViewGroup.LayoutParams.WRAP_CONTENT
3869                         : ViewGroup.LayoutParams.MATCH_PARENT);
3870             }
3871             if (p.text != null) {
3872                 int textId = showProgress ? com.android.internal.R.id.text_line_1
3873                         : com.android.internal.R.id.text;
3874                 contentView.setTextViewText(textId, p.text);
3875                 if (!p.ambient) {
3876                     setTextViewColorSecondary(contentView, textId);
3877                 }
3878                 contentView.setViewVisibility(textId, View.VISIBLE);
3879             }
3880
3881             setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
3882
3883             return contentView;
3884         }
3885
3886         private void setTextViewColorPrimary(RemoteViews contentView, int id) {
3887             ensureColors();
3888             contentView.setTextColor(id, mPrimaryTextColor);
3889         }
3890
3891         /**
3892          * @return the primary text color
3893          * @hide
3894          */
3895         @VisibleForTesting
3896         public int getPrimaryTextColor() {
3897             ensureColors();
3898             return mPrimaryTextColor;
3899         }
3900
3901         /**
3902          * @return the secondary text color
3903          * @hide
3904          */
3905         @VisibleForTesting
3906         public int getSecondaryTextColor() {
3907             ensureColors();
3908             return mSecondaryTextColor;
3909         }
3910
3911         private int getActionBarColor() {
3912             ensureColors();
3913             return mActionBarColor;
3914         }
3915
3916         private int getActionBarColorDeEmphasized() {
3917             int backgroundColor = getBackgroundColor();
3918             return NotificationColorUtil.getShiftedColor(backgroundColor, 12);
3919         }
3920
3921         private void setTextViewColorSecondary(RemoteViews contentView, int id) {
3922             ensureColors();
3923             contentView.setTextColor(id, mSecondaryTextColor);
3924         }
3925
3926         private void ensureColors() {
3927             int backgroundColor = getBackgroundColor();
3928             if (mPrimaryTextColor == COLOR_INVALID
3929                     || mSecondaryTextColor == COLOR_INVALID
3930                     || mActionBarColor == COLOR_INVALID
3931                     || mTextColorsAreForBackground != backgroundColor) {
3932                 mTextColorsAreForBackground = backgroundColor;
3933                 if (mForegroundColor == COLOR_INVALID || !isColorized()) {
3934                     mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(mContext,
3935                             backgroundColor);
3936                     mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
3937                             backgroundColor);
3938                     if (backgroundColor != COLOR_DEFAULT
3939                             && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
3940                         mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3941                                 mPrimaryTextColor, backgroundColor, 4.5);
3942                         mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
3943                                 mSecondaryTextColor, backgroundColor, 4.5);
3944                     }
3945                 } else {
3946                     double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
3947                     double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
3948                     double contrast = NotificationColorUtil.calculateContrast(mForegroundColor,
3949                             backgroundColor);
3950                     // We only respect the given colors if worst case Black or White still has
3951                     // contrast
3952                     boolean backgroundLight = backLum > textLum
3953                                     && satisfiesTextContrast(backgroundColor, Color.BLACK)
3954                             || backLum <= textLum
3955                                     && !satisfiesTextContrast(backgroundColor, Color.WHITE);
3956                     if (contrast < 4.5f) {
3957                         if (backgroundLight) {
3958                             mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3959                                     mForegroundColor,
3960                                     backgroundColor,
3961                                     true /* findFG */,
3962                                     4.5f);
3963                             mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3964                                     mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
3965                         } else {
3966                             mSecondaryTextColor =
3967                                     NotificationColorUtil.findContrastColorAgainstDark(
3968                                     mForegroundColor,
3969                                     backgroundColor,
3970                                     true /* findFG */,
3971                                     4.5f);
3972                             mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3973                                     mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
3974                         }
3975                     } else {
3976                         mPrimaryTextColor = mForegroundColor;
3977                         mSecondaryTextColor = NotificationColorUtil.changeColorLightness(
3978                                 mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
3979                                         : LIGHTNESS_TEXT_DIFFERENCE_DARK);
3980                         if (NotificationColorUtil.calculateContrast(mSecondaryTextColor,
3981                                 backgroundColor) < 4.5f) {
3982                             // oh well the secondary is not good enough
3983                             if (backgroundLight) {
3984                                 mSecondaryTextColor = NotificationColorUtil.findContrastColor(
3985                                         mSecondaryTextColor,
3986                                         backgroundColor,
3987                                         true /* findFG */,
3988                                         4.5f);
3989                             } else {
3990                                 mSecondaryTextColor
3991                                         = NotificationColorUtil.findContrastColorAgainstDark(
3992                                         mSecondaryTextColor,
3993                                         backgroundColor,
3994                                         true /* findFG */,
3995                                         4.5f);
3996                             }
3997                             mPrimaryTextColor = NotificationColorUtil.changeColorLightness(
3998                                     mSecondaryTextColor, backgroundLight
3999                                             ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
4000                                             : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
4001                         }
4002                     }
4003                 }
4004                 mActionBarColor = NotificationColorUtil.resolveActionBarColor(mContext,
4005                         backgroundColor);
4006             }
4007         }
4008
4009         private void updateBackgroundColor(RemoteViews contentView) {
4010             if (isColorized()) {
4011                 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
4012                         getBackgroundColor());
4013             } else {
4014                 // Clear it!
4015                 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
4016                         0);
4017             }
4018         }
4019
4020         /**
4021          * @param remoteView the remote view to update the minheight in
4022          * @param hasMinHeight does it have a mimHeight
4023          * @hide
4024          */
4025         void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
4026             int minHeight = 0;
4027             if (hasMinHeight) {
4028                 // we need to set the minHeight of the notification
4029                 minHeight = mContext.getResources().getDimensionPixelSize(
4030                         com.android.internal.R.dimen.notification_min_content_height);
4031             }
4032             remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
4033         }
4034
4035         private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
4036             final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
4037             final int progress = ex.getInt(EXTRA_PROGRESS, 0);
4038             final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
4039             if (hasProgress && (max != 0 || ind)) {
4040                 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
4041                 contentView.setProgressBar(
4042                         R.id.progress, max, progress, ind);
4043                 contentView.setProgressBackgroundTintList(
4044                         R.id.progress, ColorStateList.valueOf(mContext.getColor(
4045                                 R.color.notification_progress_background_color)));
4046                 if (mN.color != COLOR_DEFAULT) {
4047                     ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
4048                     contentView.setProgressTintList(R.id.progress, colorStateList);
4049                     contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
4050                 }
4051                 return true;
4052             } else {
4053                 contentView.setViewVisibility(R.id.progress, View.GONE);
4054                 return false;
4055             }
4056         }
4057
4058         private void bindLargeIcon(RemoteViews contentView) {
4059             if (mN.mLargeIcon == null && mN.largeIcon != null) {
4060                 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
4061             }
4062             if (mN.mLargeIcon != null) {
4063                 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
4064                 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
4065                 processLargeLegacyIcon(mN.mLargeIcon, contentView);
4066                 int endMargin = R.dimen.notification_content_picture_margin;
4067                 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
4068                 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
4069                 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
4070                 // Bind the reply action
4071                 Action action = findReplyAction();
4072                 contentView.setViewVisibility(R.id.reply_icon_action, action != null
4073                         ? View.VISIBLE
4074                         : View.GONE);
4075
4076                 if (action != null) {
4077                     int contrastColor = resolveContrastColor();
4078                     contentView.setDrawableParameters(R.id.reply_icon_action,
4079                             true /* targetBackground */,
4080                             -1,
4081                             contrastColor,
4082                             PorterDuff.Mode.SRC_ATOP, -1);
4083                     int iconColor = NotificationColorUtil.isColorLight(contrastColor)
4084                             ? Color.BLACK : Color.WHITE;
4085                     contentView.setDrawableParameters(R.id.reply_icon_action,
4086                             false /* targetBackground */,
4087                             -1,
4088                             iconColor,
4089                             PorterDuff.Mode.SRC_ATOP, -1);
4090                     contentView.setOnClickPendingIntent(R.id.right_icon,
4091                             action.actionIntent);
4092                     contentView.setOnClickPendingIntent(R.id.reply_icon_action,
4093                             action.actionIntent);
4094                     contentView.setRemoteInputs(R.id.right_icon, action.mRemoteInputs);
4095                     contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
4096
4097                 }
4098             }
4099             contentView.setViewVisibility(R.id.right_icon_container, mN.mLargeIcon != null
4100                     ? View.VISIBLE
4101                     : View.GONE);
4102         }
4103
4104         private Action findReplyAction() {
4105             ArrayList<Action> actions = mActions;
4106             if (mOriginalActions != null) {
4107                 actions = mOriginalActions;
4108             }
4109             int numActions = actions.size();
4110             for (int i = 0; i < numActions; i++) {
4111                 Action action = actions.get(i);
4112                 if (hasValidRemoteInput(action)) {
4113                     return action;
4114                 }
4115             }
4116             return null;
4117         }
4118
4119         private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
4120             bindSmallIcon(contentView, ambient);
4121             bindHeaderAppName(contentView, ambient);
4122             if (!ambient) {
4123                 // Ambient view does not have these
4124                 bindHeaderText(contentView);
4125                 bindHeaderChronometerAndTime(contentView);
4126                 bindProfileBadge(contentView);
4127             }
4128             bindExpandButton(contentView);
4129         }
4130
4131         private void bindExpandButton(RemoteViews contentView) {
4132             int color = getPrimaryHighlightColor();
4133             contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
4134                     PorterDuff.Mode.SRC_ATOP, -1);
4135             contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
4136                     color);
4137         }
4138
4139         /**
4140          * @return the color that is used as the first primary highlight color. This is applied
4141          * in several places like the action buttons or the app name in the header.
4142          */
4143         private int getPrimaryHighlightColor() {
4144             return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
4145         }
4146
4147         private void bindHeaderChronometerAndTime(RemoteViews contentView) {
4148             if (showsTimeOrChronometer()) {
4149                 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
4150                 setTextViewColorSecondary(contentView, R.id.time_divider);
4151                 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
4152                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
4153                     contentView.setLong(R.id.chronometer, "setBase",
4154                             mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
4155                     contentView.setBoolean(R.id.chronometer, "setStarted", true);
4156                     boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
4157                     contentView.setChronometerCountDown(R.id.chronometer, countsDown);
4158                     setTextViewColorSecondary(contentView, R.id.chronometer);
4159                 } else {
4160                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
4161                     contentView.setLong(R.id.time, "setTime", mN.when);
4162                     setTextViewColorSecondary(contentView, R.id.time);
4163                 }
4164             } else {
4165                 // We still want a time to be set but gone, such that we can show and hide it
4166                 // on demand in case it's a child notification without anything in the header
4167                 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
4168             }
4169         }
4170
4171         private void bindHeaderText(RemoteViews contentView) {
4172             CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4173             if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
4174                     && mStyle.hasSummaryInHeader()) {
4175                 headerText = mStyle.mSummaryText;
4176             }
4177             if (headerText == null
4178                     && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4179                     && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
4180                 headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
4181             }
4182             if (headerText != null) {
4183                 // TODO: Remove the span entirely to only have the string with propper formating.
4184                 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
4185                 setTextViewColorSecondary(contentView, R.id.header_text);
4186                 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
4187                 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
4188                 setTextViewColorSecondary(contentView, R.id.header_text_divider);
4189             }
4190         }
4191
4192         /**
4193          * @hide
4194          */
4195         public String loadHeaderAppName() {
4196             CharSequence name = null;
4197             final PackageManager pm = mContext.getPackageManager();
4198             if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
4199                 // only system packages which lump together a bunch of unrelated stuff
4200                 // may substitute a different name to make the purpose of the
4201                 // notification more clear. the correct package label should always
4202                 // be accessible via SystemUI.
4203                 final String pkg = mContext.getPackageName();
4204                 final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
4205                 if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
4206                         android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
4207                     name = subName;
4208                 } else {
4209                     Log.w(TAG, "warning: pkg "
4210                             + pkg + " attempting to substitute app name '" + subName
4211                             + "' without holding perm "
4212                             + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
4213                 }
4214             }
4215             if (TextUtils.isEmpty(name)) {
4216                 name = pm.getApplicationLabel(mContext.getApplicationInfo());
4217             }
4218             if (TextUtils.isEmpty(name)) {
4219                 // still nothing?
4220                 return null;
4221             }
4222
4223             return String.valueOf(name);
4224         }
4225         private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
4226             contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
4227             if (isColorized() && !ambient) {
4228                 setTextViewColorPrimary(contentView, R.id.app_name_text);
4229             } else {
4230                 contentView.setTextColor(R.id.app_name_text,
4231                         ambient ? resolveAmbientColor() : resolveContrastColor());
4232             }
4233         }
4234
4235         private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
4236             if (mN.mSmallIcon == null && mN.icon != 0) {
4237                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
4238             }
4239             contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
4240             contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
4241                     -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
4242             processSmallIconColor(mN.mSmallIcon, contentView, ambient);
4243         }
4244
4245         /**
4246          * @return true if the built notification will show the time or the chronometer; false
4247          *         otherwise
4248          */
4249         private boolean showsTimeOrChronometer() {
4250             return mN.showsTime() || mN.showsChronometer();
4251         }
4252
4253         private void resetStandardTemplateWithActions(RemoteViews big) {
4254             // actions_container is only reset when there are no actions to avoid focus issues with
4255             // remote inputs.
4256             big.setViewVisibility(R.id.actions, View.GONE);
4257             big.removeAllViews(R.id.actions);
4258
4259             big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
4260             big.setTextViewText(R.id.notification_material_reply_text_1, null);
4261
4262             big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
4263             big.setTextViewText(R.id.notification_material_reply_text_2, null);
4264             big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
4265             big.setTextViewText(R.id.notification_material_reply_text_3, null);
4266
4267             big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
4268         }
4269
4270         private RemoteViews applyStandardTemplateWithActions(int layoutId) {
4271             return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
4272         }
4273
4274         private RemoteViews applyStandardTemplateWithActions(int layoutId,
4275             StandardTemplateParams p) {
4276             RemoteViews big = applyStandardTemplate(layoutId, p);
4277
4278             resetStandardTemplateWithActions(big);
4279
4280             boolean validRemoteInput = false;
4281
4282             int N = mActions.size();
4283             boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
4284             big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
4285             if (N > 0) {
4286                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
4287                 big.setViewVisibility(R.id.actions, View.VISIBLE);
4288                 if (p.ambient) {
4289                     big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
4290                 } else if (isColorized()) {
4291                     big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
4292                 } else {
4293                     big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
4294                             R.color.notification_action_list));
4295                 }
4296                 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
4297                         R.dimen.notification_action_list_height);
4298                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
4299                 for (int i=0; i<N; i++) {
4300                     Action action = mActions.get(i);
4301                     validRemoteInput |= hasValidRemoteInput(action);
4302
4303                     final RemoteViews button = generateActionButton(action, emphazisedMode,
4304                             i % 2 != 0, p.ambient);
4305                     big.addView(R.id.actions, button);
4306                 }
4307             } else {
4308                 big.setViewVisibility(R.id.actions_container, View.GONE);
4309             }
4310
4311             CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
4312             if (!p.ambient && validRemoteInput && replyText != null
4313                     && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
4314                 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
4315                 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
4316                 setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
4317
4318                 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
4319                     big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
4320                     big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
4321                     setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
4322
4323                     if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
4324                         big.setViewVisibility(
4325                                 R.id.notification_material_reply_text_3, View.VISIBLE);
4326                         big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
4327                         setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
4328                     }
4329                 }
4330             }
4331
4332             return big;
4333         }
4334
4335         private boolean hasValidRemoteInput(Action action) {
4336             if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
4337                 // Weird actions
4338                 return false;
4339             }
4340
4341             RemoteInput[] remoteInputs = action.getRemoteInputs();
4342             if (remoteInputs == null) {
4343                 return false;
4344             }
4345
4346             for (RemoteInput r : remoteInputs) {
4347                 CharSequence[] choices = r.getChoices();
4348                 if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
4349                     return true;
4350                 }
4351             }
4352             return false;
4353         }
4354
4355         /**
4356          * Construct a RemoteViews for the final 1U notification layout. In order:
4357          *   1. Custom contentView from the caller
4358          *   2. Style's proposed content view
4359          *   3. Standard template view
4360          */
4361         public RemoteViews createContentView() {
4362             return createContentView(false /* increasedheight */ );
4363         }
4364
4365         /**
4366          * Construct a RemoteViews for the smaller content view.
4367          *
4368          *   @param increasedHeight true if this layout be created with an increased height. Some
4369          *   styles may support showing more then just that basic 1U size
4370          *   and the system may decide to render important notifications
4371          *   slightly bigger even when collapsed.
4372          *
4373          *   @hide
4374          */
4375         public RemoteViews createContentView(boolean increasedHeight) {
4376             if (mN.contentView != null && useExistingRemoteView()) {
4377                 return mN.contentView;
4378             } else if (mStyle != null) {
4379                 final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
4380                 if (styleView != null) {
4381                     return styleView;
4382                 }
4383             }
4384             return applyStandardTemplate(getBaseLayoutResource());
4385         }
4386
4387         private boolean useExistingRemoteView() {
4388             return mStyle == null || (!mStyle.displayCustomViewInline()
4389                     && !mRebuildStyledRemoteViews);
4390         }
4391
4392         /**
4393          * Construct a RemoteViews for the final big notification layout.
4394          */
4395         public RemoteViews createBigContentView() {
4396             RemoteViews result = null;
4397             if (mN.bigContentView != null && useExistingRemoteView()) {
4398                 return mN.bigContentView;
4399             } else if (mStyle != null) {
4400                 result = mStyle.makeBigContentView();
4401                 hideLine1Text(result);
4402             } else if (mActions.size() != 0) {
4403                 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
4404             }
4405             makeHeaderExpanded(result);
4406             return result;
4407         }
4408
4409         /**
4410          * Construct a RemoteViews for the final notification header only. This will not be
4411          * colorized.
4412          *
4413          * @param ambient if true, generate the header for the ambient display layout.
4414          * @hide
4415          */
4416         public RemoteViews makeNotificationHeader(boolean ambient) {
4417             Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
4418             mN.extras.putBoolean(EXTRA_COLORIZED, false);
4419             RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
4420                     ambient ? R.layout.notification_template_ambient_header
4421                             : R.layout.notification_template_header);
4422             resetNotificationHeader(header);
4423             bindNotificationHeader(header, ambient);
4424             if (colorized != null) {
4425                 mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
4426             } else {
4427                 mN.extras.remove(EXTRA_COLORIZED);
4428             }
4429             return header;
4430         }
4431
4432         /**
4433          * Construct a RemoteViews for the ambient version of the notification.
4434          *
4435          * @hide
4436          */
4437         public RemoteViews makeAmbientNotification() {
4438             RemoteViews ambient = applyStandardTemplateWithActions(
4439                     R.layout.notification_template_material_ambient,
4440                     mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
4441             return ambient;
4442         }
4443
4444         private void hideLine1Text(RemoteViews result) {
4445             if (result != null) {
4446                 result.setViewVisibility(R.id.text_line_1, View.GONE);
4447             }
4448         }
4449
4450         /**
4451          * Adapt the Notification header if this view is used as an expanded view.
4452          *
4453          * @hide
4454          */
4455         public static void makeHeaderExpanded(RemoteViews result) {
4456             if (result != null) {
4457                 result.setBoolean(R.id.notification_header, "setExpanded", true);
4458             }
4459         }
4460
4461         /**
4462          * Construct a RemoteViews for the final heads-up notification layout.
4463          *
4464          * @param increasedHeight true if this layout be created with an increased height. Some
4465          * styles may support showing more then just that basic 1U size
4466          * and the system may decide to render important notifications
4467          * slightly bigger even when collapsed.
4468          *
4469          * @hide
4470          */
4471         public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
4472             if (mN.headsUpContentView != null && useExistingRemoteView()) {
4473                 return mN.headsUpContentView;
4474             } else if (mStyle != null) {
4475                 final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
4476                 if (styleView != null) {
4477                     return styleView;
4478                 }
4479             } else if (mActions.size() == 0) {
4480                 return null;
4481             }
4482
4483             return applyStandardTemplateWithActions(getBigBaseLayoutResource());
4484         }
4485
4486         /**
4487          * Construct a RemoteViews for the final heads-up notification layout.
4488          */
4489         public RemoteViews createHeadsUpContentView() {
4490             return createHeadsUpContentView(false /* useIncreasedHeight */);
4491         }
4492
4493         /**
4494          * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4495          *
4496          * @hide
4497          */
4498         public RemoteViews makePublicContentView() {
4499             return makePublicView(false /* ambient */);
4500         }
4501
4502         /**
4503          * Construct a RemoteViews for the display in public contexts like on the lockscreen.
4504          *
4505          * @hide
4506          */
4507         public RemoteViews makePublicAmbientNotification() {
4508             return makePublicView(true /* ambient */);
4509         }
4510
4511         private RemoteViews makePublicView(boolean ambient) {
4512             if (mN.publicVersion != null) {
4513                 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
4514                 return ambient ? builder.makeAmbientNotification() : builder.createContentView();
4515             }
4516             Bundle savedBundle = mN.extras;
4517             Style style = mStyle;
4518             mStyle = null;
4519             Icon largeIcon = mN.mLargeIcon;
4520             mN.mLargeIcon = null;
4521             Bitmap largeIconLegacy = mN.largeIcon;
4522             mN.largeIcon = null;
4523             ArrayList<Action> actions = mActions;
4524             mActions = new ArrayList<>();
4525             Bundle publicExtras = new Bundle();
4526             publicExtras.putBoolean(EXTRA_SHOW_WHEN,
4527                     savedBundle.getBoolean(EXTRA_SHOW_WHEN));
4528             publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
4529                     savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
4530             publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
4531                     savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
4532             publicExtras.putCharSequence(EXTRA_TITLE,
4533                     mContext.getString(com.android.internal.R.string.notification_hidden_text));
4534             mN.extras = publicExtras;
4535             final RemoteViews view = ambient ? makeAmbientNotification()
4536                     : applyStandardTemplate(getBaseLayoutResource());
4537             mN.extras = savedBundle;
4538             mN.mLargeIcon = largeIcon;
4539             mN.largeIcon = largeIconLegacy;
4540             mActions = actions;
4541             mStyle = style;
4542             return view;
4543         }
4544
4545         /**
4546          * Construct a content view for the display when low - priority
4547          *
4548          * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
4549          *                          a new subtext is created consisting of the content of the
4550          *                          notification.
4551          * @hide
4552          */
4553         public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
4554             int color = mN.color;
4555             mN.color = COLOR_DEFAULT;
4556             CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
4557             if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
4558                 CharSequence newSummary = createSummaryText();
4559                 if (!TextUtils.isEmpty(newSummary)) {
4560                     mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
4561                 }
4562             }
4563
4564             RemoteViews header = makeNotificationHeader(false /* ambient */);
4565             header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
4566             if (summary != null) {
4567                 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
4568             } else {
4569                 mN.extras.remove(EXTRA_SUB_TEXT);
4570             }
4571             mN.color = color;
4572             return header;
4573         }
4574
4575         private CharSequence createSummaryText() {
4576             CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
4577             if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
4578                 return titleText;
4579             }
4580             SpannableStringBuilder summary = new SpannableStringBuilder();
4581             if (titleText == null) {
4582                 titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
4583             }
4584             BidiFormatter bidi = BidiFormatter.getInstance();
4585             if (titleText != null) {
4586                 summary.append(bidi.unicodeWrap(titleText));
4587             }
4588             CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
4589             if (titleText != null && contentText != null) {
4590                 summary.append(bidi.unicodeWrap(mContext.getText(
4591                         R.string.notification_header_divider_symbol_with_spaces)));
4592             }
4593             if (contentText != null) {
4594                 summary.append(bidi.unicodeWrap(contentText));
4595             }
4596             return summary;
4597         }
4598
4599         private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
4600                 boolean oddAction, boolean ambient) {
4601             final boolean tombstone = (action.actionIntent == null);
4602             RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
4603                     emphazisedMode ? getEmphasizedActionLayoutResource()
4604                             : tombstone ? getActionTombstoneLayoutResource()
4605                                     : getActionLayoutResource());
4606             if (!tombstone) {
4607                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
4608             }
4609             button.setContentDescription(R.id.action0, action.title);
4610             if (action.mRemoteInputs != null) {
4611                 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
4612             }
4613             // TODO: handle emphasized mode / actions right
4614             if (emphazisedMode) {
4615                 // change the background bgColor
4616                 int bgColor;
4617                 if (isColorized()) {
4618                     bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized();
4619                 } else {
4620                     bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
4621                             : R.color.notification_action_list_dark);
4622                 }
4623                 button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
4624                         PorterDuff.Mode.SRC_ATOP, -1);
4625                 CharSequence title = action.title;
4626                 ColorStateList[] outResultColor = null;
4627                 if (isLegacy()) {
4628                     title = clearColorSpans(title);
4629                 } else {
4630                     outResultColor = new ColorStateList[1];
4631                     title = ensureColorSpanContrast(title, bgColor, outResultColor);
4632                 }
4633                 button.setTextViewText(R.id.action0, title);
4634                 setTextViewColorPrimary(button, R.id.action0);
4635                 if (outResultColor != null && outResultColor[0] != null) {
4636                     // We need to set the text color as well since changing a text to uppercase
4637                     // clears its spans.
4638                     button.setTextColor(R.id.action0, outResultColor[0]);
4639                 } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
4640                     button.setTextColor(R.id.action0,resolveContrastColor());
4641                 }
4642             } else {
4643                 button.setTextViewText(R.id.action0, processLegacyText(action.title));
4644                 if (isColorized() && !ambient) {
4645                     setTextViewColorPrimary(button, R.id.action0);
4646                 } else if (mN.color != COLOR_DEFAULT) {
4647                     button.setTextColor(R.id.action0,
4648                             ambient ? resolveAmbientColor() : resolveContrastColor());
4649                 }
4650             }
4651             return button;
4652         }
4653
4654         /**
4655          * Clears all color spans of a text
4656          * @param charSequence the input text
4657          * @return the same text but without color spans
4658          */
4659         private CharSequence clearColorSpans(CharSequence charSequence) {
4660             if (charSequence instanceof Spanned) {
4661                 Spanned ss = (Spanned) charSequence;
4662                 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4663                 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4664                 for (Object span : spans) {
4665                     Object resultSpan = span;
4666                     if (resultSpan instanceof CharacterStyle) {
4667                         resultSpan = ((CharacterStyle) span).getUnderlying();
4668                     }
4669                     if (resultSpan instanceof TextAppearanceSpan) {
4670                         TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4671                         if (originalSpan.getTextColor() != null) {
4672                             resultSpan = new TextAppearanceSpan(
4673                                     originalSpan.getFamily(),
4674                                     originalSpan.getTextStyle(),
4675                                     originalSpan.getTextSize(),
4676                                     null,
4677                                     originalSpan.getLinkTextColor());
4678                         }
4679                     } else if (resultSpan instanceof ForegroundColorSpan
4680                             || (resultSpan instanceof BackgroundColorSpan)) {
4681                         continue;
4682                     } else {
4683                         resultSpan = span;
4684                     }
4685                     builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
4686                             ss.getSpanFlags(span));
4687                 }
4688                 return builder;
4689             }
4690             return charSequence;
4691         }
4692
4693         /**
4694          * Ensures contrast on color spans against a background color. also returns the color of the
4695          * text if a span was found that spans over the whole text.
4696          *
4697          * @param charSequence the charSequence on which the spans are
4698          * @param background the background color to ensure the contrast against
4699          * @param outResultColor an array in which a color will be returned as the first element if
4700          *                    there exists a full length color span.
4701          * @return the contrasted charSequence
4702          */
4703         private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
4704                 ColorStateList[] outResultColor) {
4705             if (charSequence instanceof Spanned) {
4706                 Spanned ss = (Spanned) charSequence;
4707                 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
4708                 SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
4709                 for (Object span : spans) {
4710                     Object resultSpan = span;
4711                     int spanStart = ss.getSpanStart(span);
4712                     int spanEnd = ss.getSpanEnd(span);
4713                     boolean fullLength = (spanEnd - spanStart) == charSequence.length();
4714                     if (resultSpan instanceof CharacterStyle) {
4715                         resultSpan = ((CharacterStyle) span).getUnderlying();
4716                     }
4717                     if (resultSpan instanceof TextAppearanceSpan) {
4718                         TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
4719                         ColorStateList textColor = originalSpan.getTextColor();
4720                         if (textColor != null) {
4721                             int[] colors = textColor.getColors();
4722                             int[] newColors = new int[colors.length];
4723                             for (int i = 0; i < newColors.length; i++) {
4724                                 newColors[i] = NotificationColorUtil.ensureLargeTextContrast(
4725                                         colors[i], background);
4726                             }
4727                             textColor = new ColorStateList(textColor.getStates().clone(),
4728                                     newColors);
4729                             resultSpan = new TextAppearanceSpan(
4730                                     originalSpan.getFamily(),
4731                                     originalSpan.getTextStyle(),
4732                                     originalSpan.getTextSize(),
4733                                     textColor,
4734                                     originalSpan.getLinkTextColor());
4735                             if (fullLength) {
4736                                 outResultColor[0] = new ColorStateList(
4737                                         textColor.getStates().clone(), newColors);
4738                             }
4739                         }
4740                     } else if (resultSpan instanceof ForegroundColorSpan) {
4741                         ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
4742                         int foregroundColor = originalSpan.getForegroundColor();
4743                         foregroundColor = NotificationColorUtil.ensureLargeTextContrast(
4744                                 foregroundColor, background);
4745                         resultSpan = new ForegroundColorSpan(foregroundColor);
4746                         if (fullLength) {
4747                             outResultColor[0] = ColorStateList.valueOf(foregroundColor);
4748                         }
4749                     } else {
4750                         resultSpan = span;
4751                     }
4752
4753                     builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
4754                 }
4755                 return builder;
4756             }
4757             return charSequence;
4758         }
4759
4760         /**
4761          * @return Whether we are currently building a notification from a legacy (an app that
4762          *         doesn't create material notifications by itself) app.
4763          */
4764         private boolean isLegacy() {
4765             if (!mIsLegacyInitialized) {
4766                 mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
4767                         < Build.VERSION_CODES.LOLLIPOP;
4768                 mIsLegacyInitialized = true;
4769             }
4770             return mIsLegacy;
4771         }
4772
4773         private CharSequence processLegacyText(CharSequence charSequence) {
4774             return processLegacyText(charSequence, false /* ambient */);
4775         }
4776
4777         private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
4778             boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
4779             boolean wantLightText = ambient;
4780             if (isAlreadyLightText != wantLightText) {
4781                 return getColorUtil().invertCharSequenceColors(charSequence);
4782             } else {
4783                 return charSequence;
4784             }
4785         }
4786
4787         /**
4788          * Apply any necessariy colors to the small icon
4789          */
4790         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
4791                 boolean ambient) {
4792             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
4793             int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
4794             if (colorable) {
4795                 contentView.setDrawableParameters(R.id.icon, false, -1, color,
4796                         PorterDuff.Mode.SRC_ATOP, -1);
4797
4798             }
4799             contentView.setInt(R.id.notification_header, "setOriginalIconColor",
4800                     colorable ? color : NotificationHeaderView.NO_COLOR);
4801         }
4802
4803         /**
4804          * Make the largeIcon dark if it's a fake smallIcon (that is,
4805          * if it's grayscale).
4806          */
4807         // TODO: also check bounds, transparency, that sort of thing.
4808         private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
4809             if (largeIcon != null && isLegacy()
4810                     && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
4811                 // resolve color will fall back to the default when legacy
4812                 contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
4813                         PorterDuff.Mode.SRC_ATOP, -1);
4814             }
4815         }
4816
4817         private void sanitizeColor() {
4818             if (mN.color != COLOR_DEFAULT) {
4819                 mN.color |= 0xFF000000; // no alpha for custom colors
4820             }
4821         }
4822
4823         int resolveContrastColor() {
4824             if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
4825                 return mCachedContrastColor;
4826             }
4827
4828             int color;
4829             int background = mBackgroundColorHint;
4830             if (mBackgroundColorHint == COLOR_INVALID) {
4831                 background = mContext.getColor(
4832                         com.android.internal.R.color.notification_material_background_color);
4833             }
4834             if (mN.color == COLOR_DEFAULT) {
4835                 ensureColors();
4836                 color = mSecondaryTextColor;
4837             } else {
4838                 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
4839                         background);
4840             }
4841             if (Color.alpha(color) < 255) {
4842                 // alpha doesn't go well for color filters, so let's blend it manually
4843                 color = NotificationColorUtil.compositeColors(color, background);
4844             }
4845             mCachedContrastColorIsFor = mN.color;
4846             return mCachedContrastColor = color;
4847         }
4848
4849         int resolveAmbientColor() {
4850             if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
4851                 return mCachedAmbientColor;
4852             }
4853             final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
4854
4855             mCachedAmbientColorIsFor = mN.color;
4856             return mCachedAmbientColor = contrasted;
4857         }
4858
4859         /**
4860          * Apply the unstyled operations and return a new {@link Notification} object.
4861          * @hide
4862          */
4863         public Notification buildUnstyled() {
4864             if (mActions.size() > 0) {
4865                 mN.actions = new Action[mActions.size()];
4866                 mActions.toArray(mN.actions);
4867             }
4868             if (!mPersonList.isEmpty()) {
4869                 mN.extras.putStringArray(EXTRA_PEOPLE,
4870                         mPersonList.toArray(new String[mPersonList.size()]));
4871             }
4872             if (mN.bigContentView != null || mN.contentView != null
4873                     || mN.headsUpContentView != null) {
4874                 mN.extras.putBoolean(EXTRA_CONTAINS_CUSTOM_VIEW, true);
4875             }
4876             return mN;
4877         }
4878
4879         /**
4880          * Creates a Builder from an existing notification so further changes can be made.
4881          * @param context The context for your application / activity.
4882          * @param n The notification to create a Builder from.
4883          */
4884         public static Notification.Builder recoverBuilder(Context context, Notification n) {
4885             // Re-create notification context so we can access app resources.
4886             ApplicationInfo applicationInfo = n.extras.getParcelable(
4887                     EXTRA_BUILDER_APPLICATION_INFO);
4888             Context builderContext;
4889             if (applicationInfo != null) {
4890                 try {
4891                     builderContext = context.createApplicationContext(applicationInfo,
4892                             Context.CONTEXT_RESTRICTED);
4893                 } catch (NameNotFoundException e) {
4894                     Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
4895                     builderContext = context;  // try with our context
4896                 }
4897             } else {
4898                 builderContext = context; // try with given context
4899             }
4900
4901             return new Builder(builderContext, n);
4902         }
4903
4904         /**
4905          * @deprecated Use {@link #build()} instead.
4906          */
4907         @Deprecated
4908         public Notification getNotification() {
4909             return build();
4910         }
4911
4912         /**
4913          * Combine all of the options that have been set and return a new {@link Notification}
4914          * object.
4915          */
4916         public Notification build() {
4917             // first, add any extras from the calling code
4918             if (mUserExtras != null) {
4919                 mN.extras = getAllExtras();
4920             }
4921
4922             mN.creationTime = System.currentTimeMillis();
4923
4924             // lazy stuff from mContext; see comment in Builder(Context, Notification)
4925             Notification.addFieldsFromContext(mContext, mN);
4926
4927             buildUnstyled();
4928
4929             if (mStyle != null) {
4930                 mStyle.buildStyled(mN);
4931             }
4932
4933             if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
4934                     && (useExistingRemoteView())) {
4935                 if (mN.contentView == null) {
4936                     mN.contentView = createContentView();
4937                     mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
4938                             mN.contentView.getSequenceNumber());
4939                 }
4940                 if (mN.bigContentView == null) {
4941                     mN.bigContentView = createBigContentView();
4942                     if (mN.bigContentView != null) {
4943                         mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
4944                                 mN.bigContentView.getSequenceNumber());
4945                     }
4946                 }
4947                 if (mN.headsUpContentView == null) {
4948                     mN.headsUpContentView = createHeadsUpContentView();
4949                     if (mN.headsUpContentView != null) {
4950                         mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
4951                                 mN.headsUpContentView.getSequenceNumber());
4952                     }
4953                 }
4954             }
4955
4956             if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
4957                 mN.flags |= FLAG_SHOW_LIGHTS;
4958             }
4959
4960             return mN;
4961         }
4962
4963         /**
4964          * Apply this Builder to an existing {@link Notification} object.
4965          *
4966          * @hide
4967          */
4968         public Notification buildInto(Notification n) {
4969             build().cloneInto(n, true);
4970             return n;
4971         }
4972
4973         /**
4974          * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
4975          * change.
4976          *
4977          * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
4978          *
4979          * @hide
4980          */
4981         public static Notification maybeCloneStrippedForDelivery(Notification n) {
4982             String templateClass = n.extras.getString(EXTRA_TEMPLATE);
4983
4984             // Only strip views for known Styles because we won't know how to
4985             // re-create them otherwise.
4986             if (!TextUtils.isEmpty(templateClass)
4987                     && getNotificationStyleClass(templateClass) == null) {
4988                 return n;
4989             }
4990
4991             // Only strip unmodified BuilderRemoteViews.
4992             boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
4993                     n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
4994                             n.contentView.getSequenceNumber();
4995             boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
4996                     n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
4997                             n.bigContentView.getSequenceNumber();
4998             boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
4999                     n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
5000                             n.headsUpContentView.getSequenceNumber();
5001
5002             // Nothing to do here, no need to clone.
5003             if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
5004                 return n;
5005             }
5006
5007             Notification clone = n.clone();
5008             if (stripContentView) {
5009                 clone.contentView = null;
5010                 clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
5011             }
5012             if (stripBigContentView) {
5013                 clone.bigContentView = null;
5014                 clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
5015             }
5016             if (stripHeadsUpContentView) {
5017                 clone.headsUpContentView = null;
5018                 clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
5019             }
5020             return clone;
5021         }
5022
5023         private int getBaseLayoutResource() {
5024             return R.layout.notification_template_material_base;
5025         }
5026
5027         private int getBigBaseLayoutResource() {
5028             return R.layout.notification_template_material_big_base;
5029         }
5030
5031         private int getBigPictureLayoutResource() {
5032             return R.layout.notification_template_material_big_picture;
5033         }
5034
5035         private int getBigTextLayoutResource() {
5036             return R.layout.notification_template_material_big_text;
5037         }
5038
5039         private int getInboxLayoutResource() {
5040             return R.layout.notification_template_material_inbox;
5041         }
5042
5043         private int getMessagingLayoutResource() {
5044             return R.layout.notification_template_material_messaging;
5045         }
5046
5047         private int getActionLayoutResource() {
5048             return R.layout.notification_material_action;
5049         }
5050
5051         private int getEmphasizedActionLayoutResource() {
5052             return R.layout.notification_material_action_emphasized;
5053         }
5054
5055         private int getActionTombstoneLayoutResource() {
5056             return R.layout.notification_material_action_tombstone;
5057         }
5058
5059         private int getBackgroundColor() {
5060             if (isColorized()) {
5061                 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
5062             } else {
5063                 return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
5064                         : COLOR_DEFAULT;
5065             }
5066         }
5067
5068         private boolean isColorized() {
5069             return mN.isColorized();
5070         }
5071
5072         private boolean textColorsNeedInversion() {
5073             if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
5074                 return false;
5075             }
5076             int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
5077             return targetSdkVersion > Build.VERSION_CODES.M
5078                     && targetSdkVersion < Build.VERSION_CODES.O;
5079         }
5080
5081         /**
5082          * Set a color palette to be used as the background and textColors
5083          *
5084          * @param backgroundColor the color to be used as the background
5085          * @param foregroundColor the color to be used as the foreground
5086          *
5087          * @hide
5088          */
5089         public void setColorPalette(int backgroundColor, int foregroundColor) {
5090             mBackgroundColor = backgroundColor;
5091             mForegroundColor = foregroundColor;
5092             mTextColorsAreForBackground = COLOR_INVALID;
5093             ensureColors();
5094         }
5095
5096         /**
5097          * Sets the background color for this notification to be a different one then the default.
5098          * This is mainly used to calculate contrast and won't necessarily be applied to the
5099          * background.
5100          *
5101          * @hide
5102          */
5103         public void setBackgroundColorHint(int backgroundColor) {
5104             mBackgroundColorHint = backgroundColor;
5105         }
5106
5107
5108         /**
5109          * Forces all styled remoteViews to be built from scratch and not use any cached
5110          * RemoteViews.
5111          * This is needed for legacy apps that are baking in their remoteviews into the
5112          * notification.
5113          *
5114          * @hide
5115          */
5116         public void setRebuildStyledRemoteViews(boolean rebuild) {
5117             mRebuildStyledRemoteViews = rebuild;
5118         }
5119     }
5120
5121     /**
5122      * @return whether this notification is a foreground service notification
5123      */
5124     private boolean isForegroundService() {
5125         return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
5126     }
5127
5128     /**
5129      * @return whether this notification has a media session attached
5130      * @hide
5131      */
5132     public boolean hasMediaSession() {
5133         return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
5134     }
5135
5136     /**
5137      * @return the style class of this notification
5138      * @hide
5139      */
5140     public Class<? extends Notification.Style> getNotificationStyle() {
5141         String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
5142
5143         if (!TextUtils.isEmpty(templateClass)) {
5144             return Notification.getNotificationStyleClass(templateClass);
5145         }
5146         return null;
5147     }
5148
5149     /**
5150      * @return true if this notification is colorized.
5151      *
5152      * @hide
5153      */
5154     public boolean isColorized() {
5155         if (isColorizedMedia()) {
5156             return true;
5157         }
5158         return extras.getBoolean(EXTRA_COLORIZED)
5159                 && (hasColorizedPermission() || isForegroundService());
5160     }
5161
5162     /**
5163      * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
5164      * permission. The permission is checked when a notification is enqueued.
5165      */
5166     private boolean hasColorizedPermission() {
5167         return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
5168     }
5169
5170     /**
5171      * @return true if this notification is colorized and it is a media notification
5172      *
5173      * @hide
5174      */
5175     public boolean isColorizedMedia() {
5176         Class<? extends Style> style = getNotificationStyle();
5177         if (MediaStyle.class.equals(style)) {
5178             Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
5179             if ((colorized == null || colorized) && hasMediaSession()) {
5180                 return true;
5181             }
5182         } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5183             if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
5184                 return true;
5185             }
5186         }
5187         return false;
5188     }
5189
5190
5191     /**
5192      * @return true if this is a media notification
5193      *
5194      * @hide
5195      */
5196     public boolean isMediaNotification() {
5197         Class<? extends Style> style = getNotificationStyle();
5198         if (MediaStyle.class.equals(style)) {
5199             return true;
5200         } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
5201             return true;
5202         }
5203         return false;
5204     }
5205
5206     private boolean hasLargeIcon() {
5207         return mLargeIcon != null || largeIcon != null;
5208     }
5209
5210     /**
5211      * @return true if the notification will show the time; false otherwise
5212      * @hide
5213      */
5214     public boolean showsTime() {
5215         return when != 0 && extras.getBoolean(EXTRA_SHOW_WHEN);
5216     }
5217
5218     /**
5219      * @return true if the notification will show a chronometer; false otherwise
5220      * @hide
5221      */
5222     public boolean showsChronometer() {
5223         return when != 0 && extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
5224     }
5225
5226     /**
5227      * @hide
5228      */
5229     @SystemApi
5230     public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
5231         Class<? extends Style>[] classes = new Class[] {
5232                 BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
5233                 DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
5234                 MessagingStyle.class };
5235         for (Class<? extends Style> innerClass : classes) {
5236             if (templateClass.equals(innerClass.getName())) {
5237                 return innerClass;
5238             }
5239         }
5240         return null;
5241     }
5242
5243     /**
5244      * An object that can apply a rich notification style to a {@link Notification.Builder}
5245      * object.
5246      */
5247     public static abstract class Style {
5248         private CharSequence mBigContentTitle;
5249
5250         /**
5251          * @hide
5252          */
5253         protected CharSequence mSummaryText = null;
5254
5255         /**
5256          * @hide
5257          */
5258         protected boolean mSummaryTextSet = false;
5259
5260         protected Builder mBuilder;
5261
5262         /**
5263          * Overrides ContentTitle in the big form of the template.
5264          * This defaults to the value passed to setContentTitle().
5265          */
5266         protected void internalSetBigContentTitle(CharSequence title) {
5267             mBigContentTitle = title;
5268         }
5269
5270         /**
5271          * Set the first line of text after the detail section in the big form of the template.
5272          */
5273         protected void internalSetSummaryText(CharSequence cs) {
5274             mSummaryText = cs;
5275             mSummaryTextSet = true;
5276         }
5277
5278         public void setBuilder(Builder builder) {
5279             if (mBuilder != builder) {
5280                 mBuilder = builder;
5281                 if (mBuilder != null) {
5282                     mBuilder.setStyle(this);
5283                 }
5284             }
5285         }
5286
5287         protected void checkBuilder() {
5288             if (mBuilder == null) {
5289                 throw new IllegalArgumentException("Style requires a valid Builder object");
5290             }
5291         }
5292
5293         protected RemoteViews getStandardView(int layoutId) {
5294             checkBuilder();
5295
5296             // Nasty.
5297             CharSequence oldBuilderContentTitle =
5298                     mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
5299             if (mBigContentTitle != null) {
5300                 mBuilder.setContentTitle(mBigContentTitle);
5301             }
5302
5303             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
5304
5305             mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
5306
5307             if (mBigContentTitle != null && mBigContentTitle.equals("")) {
5308                 contentView.setViewVisibility(R.id.line1, View.GONE);
5309             } else {
5310                 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
5311             }
5312
5313             return contentView;
5314         }
5315
5316         /**
5317          * Construct a Style-specific RemoteViews for the collapsed notification layout.
5318          * The default implementation has nothing additional to add.
5319          *
5320          * @param increasedHeight true if this layout be created with an increased height.
5321          * @hide
5322          */
5323         public RemoteViews makeContentView(boolean increasedHeight) {
5324             return null;
5325         }
5326
5327         /**
5328          * Construct a Style-specific RemoteViews for the final big notification layout.
5329          * @hide
5330          */
5331         public RemoteViews makeBigContentView() {
5332             return null;
5333         }
5334
5335         /**
5336          * Construct a Style-specific RemoteViews for the final HUN layout.
5337          *
5338          * @param increasedHeight true if this layout be created with an increased height.
5339          * @hide
5340          */
5341         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5342             return null;
5343         }
5344
5345         /**
5346          * Apply any style-specific extras to this notification before shipping it out.
5347          * @hide
5348          */
5349         public void addExtras(Bundle extras) {
5350             if (mSummaryTextSet) {
5351                 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText);
5352             }
5353             if (mBigContentTitle != null) {
5354                 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle);
5355             }
5356             extras.putString(EXTRA_TEMPLATE, this.getClass().getName());
5357         }
5358
5359         /**
5360          * Reconstruct the internal state of this Style object from extras.
5361          * @hide
5362          */
5363         protected void restoreFromExtras(Bundle extras) {
5364             if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
5365                 mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
5366                 mSummaryTextSet = true;
5367             }
5368             if (extras.containsKey(EXTRA_TITLE_BIG)) {
5369                 mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
5370             }
5371         }
5372
5373
5374         /**
5375          * @hide
5376          */
5377         public Notification buildStyled(Notification wip) {
5378             addExtras(wip.extras);
5379             return wip;
5380         }
5381
5382         /**
5383          * @hide
5384          */
5385         public void purgeResources() {}
5386
5387         /**
5388          * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
5389          * attached to.
5390          *
5391          * @return the fully constructed Notification.
5392          */
5393         public Notification build() {
5394             checkBuilder();
5395             return mBuilder.build();
5396         }
5397
5398         /**
5399          * @hide
5400          * @return true if the style positions the progress bar on the second line; false if the
5401          *         style hides the progress bar
5402          */
5403         protected boolean hasProgress() {
5404             return true;
5405         }
5406
5407         /**
5408          * @hide
5409          * @return Whether we should put the summary be put into the notification header
5410          */
5411         public boolean hasSummaryInHeader() {
5412             return true;
5413         }
5414
5415         /**
5416          * @hide
5417          * @return Whether custom content views are displayed inline in the style
5418          */
5419         public boolean displayCustomViewInline() {
5420             return false;
5421         }
5422     }
5423
5424     /**
5425      * Helper class for generating large-format notifications that include a large image attachment.
5426      *
5427      * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
5428      * <pre class="prettyprint">
5429      * Notification notif = new Notification.Builder(mContext)
5430      *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
5431      *     .setContentText(subject)
5432      *     .setSmallIcon(R.drawable.new_post)
5433      *     .setLargeIcon(aBitmap)
5434      *     .setStyle(new Notification.BigPictureStyle()
5435      *         .bigPicture(aBigBitmap))
5436      *     .build();
5437      * </pre>
5438      *
5439      * @see Notification#bigContentView
5440      */
5441     public static class BigPictureStyle extends Style {
5442         private Bitmap mPicture;
5443         private Icon mBigLargeIcon;
5444         private boolean mBigLargeIconSet = false;
5445
5446         public BigPictureStyle() {
5447         }
5448
5449         /**
5450          * @deprecated use {@code BigPictureStyle()}.
5451          */
5452         @Deprecated
5453         public BigPictureStyle(Builder builder) {
5454             setBuilder(builder);
5455         }
5456
5457         /**
5458          * Overrides ContentTitle in the big form of the template.
5459          * This defaults to the value passed to setContentTitle().
5460          */
5461         public BigPictureStyle setBigContentTitle(CharSequence title) {
5462             internalSetBigContentTitle(safeCharSequence(title));
5463             return this;
5464         }
5465
5466         /**
5467          * Set the first line of text after the detail section in the big form of the template.
5468          */
5469         public BigPictureStyle setSummaryText(CharSequence cs) {
5470             internalSetSummaryText(safeCharSequence(cs));
5471             return this;
5472         }
5473
5474         /**
5475          * Provide the bitmap to be used as the payload for the BigPicture notification.
5476          */
5477         public BigPictureStyle bigPicture(Bitmap b) {
5478             mPicture = b;
5479             return this;
5480         }
5481
5482         /**
5483          * Override the large icon when the big notification is shown.
5484          */
5485         public BigPictureStyle bigLargeIcon(Bitmap b) {
5486             return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
5487         }
5488
5489         /**
5490          * Override the large icon when the big notification is shown.
5491          */
5492         public BigPictureStyle bigLargeIcon(Icon icon) {
5493             mBigLargeIconSet = true;
5494             mBigLargeIcon = icon;
5495             return this;
5496         }
5497
5498         /** @hide */
5499         public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
5500
5501         /**
5502          * @hide
5503          */
5504         @Override
5505         public void purgeResources() {
5506             super.purgeResources();
5507             if (mPicture != null &&
5508                 mPicture.isMutable() &&
5509                 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
5510                 mPicture = mPicture.createAshmemBitmap();
5511             }
5512             if (mBigLargeIcon != null) {
5513                 mBigLargeIcon.convertToAshmem();
5514             }
5515         }
5516
5517         /**
5518          * @hide
5519          */
5520         public RemoteViews makeBigContentView() {
5521             // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
5522             // This covers the following cases:
5523             //   1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
5524             //          mN.mLargeIcon
5525             //   2. !mBigLargeIconSet -> mN.mLargeIcon applies
5526             Icon oldLargeIcon = null;
5527             Bitmap largeIconLegacy = null;
5528             if (mBigLargeIconSet) {
5529                 oldLargeIcon = mBuilder.mN.mLargeIcon;
5530                 mBuilder.mN.mLargeIcon = mBigLargeIcon;
5531                 // The legacy largeIcon might not allow us to clear the image, as it's taken in
5532                 // replacement if the other one is null. Because we're restoring these legacy icons
5533                 // for old listeners, this is in general non-null.
5534                 largeIconLegacy = mBuilder.mN.largeIcon;
5535                 mBuilder.mN.largeIcon = null;
5536             }
5537
5538             RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
5539             if (mSummaryTextSet) {
5540                 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
5541                 mBuilder.setTextViewColorSecondary(contentView, R.id.text);
5542                 contentView.setViewVisibility(R.id.text, View.VISIBLE);
5543             }
5544             mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
5545
5546             if (mBigLargeIconSet) {
5547                 mBuilder.mN.mLargeIcon = oldLargeIcon;
5548                 mBuilder.mN.largeIcon = largeIconLegacy;
5549             }
5550
5551             contentView.setImageViewBitmap(R.id.big_picture, mPicture);
5552             return contentView;
5553         }
5554
5555         /**
5556          * @hide
5557          */
5558         public void addExtras(Bundle extras) {
5559             super.addExtras(extras);
5560
5561             if (mBigLargeIconSet) {
5562                 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon);
5563             }
5564             extras.putParcelable(EXTRA_PICTURE, mPicture);
5565         }
5566
5567         /**
5568          * @hide
5569          */
5570         @Override
5571         protected void restoreFromExtras(Bundle extras) {
5572             super.restoreFromExtras(extras);
5573
5574             if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
5575                 mBigLargeIconSet = true;
5576                 mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
5577             }
5578             mPicture = extras.getParcelable(EXTRA_PICTURE);
5579         }
5580
5581         /**
5582          * @hide
5583          */
5584         @Override
5585         public boolean hasSummaryInHeader() {
5586             return false;
5587         }
5588     }
5589
5590     /**
5591      * Helper class for generating large-format notifications that include a lot of text.
5592      *
5593      * Here's how you'd set the <code>BigTextStyle</code> on a notification:
5594      * <pre class="prettyprint">
5595      * Notification notif = new Notification.Builder(mContext)
5596      *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
5597      *     .setContentText(subject)
5598      *     .setSmallIcon(R.drawable.new_mail)
5599      *     .setLargeIcon(aBitmap)
5600      *     .setStyle(new Notification.BigTextStyle()
5601      *         .bigText(aVeryLongString))
5602      *     .build();
5603      * </pre>
5604      *
5605      * @see Notification#bigContentView
5606      */
5607     public static class BigTextStyle extends Style {
5608
5609         private CharSequence mBigText;
5610
5611         public BigTextStyle() {
5612         }
5613
5614         /**
5615          * @deprecated use {@code BigTextStyle()}.
5616          */
5617         @Deprecated
5618         public BigTextStyle(Builder builder) {
5619             setBuilder(builder);
5620         }
5621
5622         /**
5623          * Overrides ContentTitle in the big form of the template.
5624          * This defaults to the value passed to setContentTitle().
5625          */
5626         public BigTextStyle setBigContentTitle(CharSequence title) {
5627             internalSetBigContentTitle(safeCharSequence(title));
5628             return this;
5629         }
5630
5631         /**
5632          * Set the first line of text after the detail section in the big form of the template.
5633          */
5634         public BigTextStyle setSummaryText(CharSequence cs) {
5635             internalSetSummaryText(safeCharSequence(cs));
5636             return this;
5637         }
5638
5639         /**
5640          * Provide the longer text to be displayed in the big form of the
5641          * template in place of the content text.
5642          */
5643         public BigTextStyle bigText(CharSequence cs) {
5644             mBigText = safeCharSequence(cs);
5645             return this;
5646         }
5647
5648         /**
5649          * @hide
5650          */
5651         public void addExtras(Bundle extras) {
5652             super.addExtras(extras);
5653
5654             extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
5655         }
5656
5657         /**
5658          * @hide
5659          */
5660         @Override
5661         protected void restoreFromExtras(Bundle extras) {
5662             super.restoreFromExtras(extras);
5663
5664             mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
5665         }
5666
5667         /**
5668          * @param increasedHeight true if this layout be created with an increased height.
5669          *
5670          * @hide
5671          */
5672         @Override
5673         public RemoteViews makeContentView(boolean increasedHeight) {
5674             if (increasedHeight) {
5675                 mBuilder.mOriginalActions = mBuilder.mActions;
5676                 mBuilder.mActions = new ArrayList<>();
5677                 RemoteViews remoteViews = makeBigContentView();
5678                 mBuilder.mActions = mBuilder.mOriginalActions;
5679                 mBuilder.mOriginalActions = null;
5680                 return remoteViews;
5681             }
5682             return super.makeContentView(increasedHeight);
5683         }
5684
5685         /**
5686          * @hide
5687          */
5688         @Override
5689         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
5690             if (increasedHeight && mBuilder.mActions.size() > 0) {
5691                 return makeBigContentView();
5692             }
5693             return super.makeHeadsUpContentView(increasedHeight);
5694         }
5695
5696         /**
5697          * @hide
5698          */
5699         public RemoteViews makeBigContentView() {
5700
5701             // Nasty
5702             CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
5703             mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
5704
5705             RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
5706
5707             mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
5708
5709             CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
5710             if (TextUtils.isEmpty(bigTextText)) {
5711                 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
5712                 // experience
5713                 bigTextText = mBuilder.processLegacyText(text);
5714             }
5715             applyBigTextContentView(mBuilder, contentView, bigTextText);
5716
5717             return contentView;
5718         }
5719
5720         static void applyBigTextContentView(Builder builder,
5721                 RemoteViews contentView, CharSequence bigTextText) {
5722             contentView.setTextViewText(R.id.big_text, bigTextText);
5723             builder.setTextViewColorSecondary(contentView, R.id.big_text);
5724             contentView.setViewVisibility(R.id.big_text,
5725                     TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
5726             contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
5727         }
5728     }
5729
5730     /**
5731      * Helper class for generating large-format notifications that include multiple back-and-forth
5732      * messages of varying types between any number of people.
5733      *
5734      * <br>
5735      * If the platform does not provide large-format notifications, this method has no effect. The
5736      * user will always see the normal notification view.
5737      * <br>
5738      * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
5739      * so:
5740      * <pre class="prettyprint">
5741      *
5742      * Notification noti = new Notification.Builder()
5743      *     .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
5744      *     .setContentText(subject)
5745      *     .setSmallIcon(R.drawable.new_message)
5746      *     .setLargeIcon(aBitmap)
5747      *     .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
5748      *         .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
5749      *         .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
5750      *     .build();
5751      * </pre>
5752      */
5753     public static class MessagingStyle extends Style {
5754
5755         /**
5756          * The maximum number of messages that will be retained in the Notification itself (the
5757          * number displayed is up to the platform).
5758          */
5759         public static final int MAXIMUM_RETAINED_MESSAGES = 25;
5760
5761         CharSequence mUserDisplayName;
5762         CharSequence mConversationTitle;
5763         List<Message> mMessages = new ArrayList<>();
5764         List<Message> mHistoricMessages = new ArrayList<>();
5765
5766         MessagingStyle() {
5767         }
5768
5769         /**
5770          * @param userDisplayName Required - the name to be displayed for any replies sent by the
5771          * user before the posting app reposts the notification with those messages after they've
5772          * been actually sent and in previous messages sent by the user added in
5773          * {@link #addMessage(Notification.MessagingStyle.Message)}
5774          */
5775         public MessagingStyle(@NonNull CharSequence userDisplayName) {
5776             mUserDisplayName = userDisplayName;
5777         }
5778
5779         /**
5780          * Returns the name to be displayed for any replies sent by the user
5781          */
5782         public CharSequence getUserDisplayName() {
5783             return mUserDisplayName;
5784         }
5785
5786         /**
5787          * Sets the title to be displayed on this conversation. This should only be used for
5788          * group messaging and left unset for one-on-one conversations.
5789          * @param conversationTitle
5790          * @return this object for method chaining.
5791          */
5792         public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
5793             mConversationTitle = conversationTitle;
5794             return this;
5795         }
5796
5797         /**
5798          * Return the title to be displayed on this conversation. Can be <code>null</code> and
5799          * should be for one-on-one conversations
5800          */
5801         public CharSequence getConversationTitle() {
5802             return mConversationTitle;
5803         }
5804
5805         /**
5806          * Adds a message for display by this notification. Convenience call for a simple
5807          * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
5808          * @param text A {@link CharSequence} to be displayed as the message content
5809          * @param timestamp Time at which the message arrived
5810          * @param sender A {@link CharSequence} to be used for displaying the name of the
5811          * sender. Should be <code>null</code> for messages by the current user, in which case
5812          * the platform will insert {@link #getUserDisplayName()}.
5813          * Should be unique amongst all individuals in the conversation, and should be
5814          * consistent during re-posts of the notification.
5815          *
5816          * @see Message#Message(CharSequence, long, CharSequence)
5817          *
5818          * @return this object for method chaining
5819          */
5820         public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
5821             return addMessage(new Message(text, timestamp, sender));
5822         }
5823
5824         /**
5825          * Adds a {@link Message} for display in this notification.
5826          *
5827          * <p>The messages should be added in chronologic order, i.e. the oldest first,
5828          * the newest last.
5829          *
5830          * @param message The {@link Message} to be displayed
5831          * @return this object for method chaining
5832          */
5833         public MessagingStyle addMessage(Message message) {
5834             mMessages.add(message);
5835             if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5836                 mMessages.remove(0);
5837             }
5838             return this;
5839         }
5840
5841         /**
5842          * Adds a {@link Message} for historic context in this notification.
5843          *
5844          * <p>Messages should be added as historic if they are not the main subject of the
5845          * notification but may give context to a conversation. The system may choose to present
5846          * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
5847          *
5848          * <p>The messages should be added in chronologic order, i.e. the oldest first,
5849          * the newest last.
5850          *
5851          * @param message The historic {@link Message} to be added
5852          * @return this object for method chaining
5853          */
5854         public MessagingStyle addHistoricMessage(Message message) {
5855             mHistoricMessages.add(message);
5856             if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
5857                 mHistoricMessages.remove(0);
5858             }
5859             return this;
5860         }
5861
5862         /**
5863          * Gets the list of {@code Message} objects that represent the notification
5864          */
5865         public List<Message> getMessages() {
5866             return mMessages;
5867         }
5868
5869         /**
5870          * Gets the list of historic {@code Message}s in the notification.
5871          */
5872         public List<Message> getHistoricMessages() {
5873             return mHistoricMessages;
5874         }
5875
5876         /**
5877          * @hide
5878          */
5879         @Override
5880         public void addExtras(Bundle extras) {
5881             super.addExtras(extras);
5882             if (mUserDisplayName != null) {
5883                 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
5884             }
5885             if (mConversationTitle != null) {
5886                 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
5887             }
5888             if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
5889                     Message.getBundleArrayForMessages(mMessages));
5890             }
5891             if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
5892                     Message.getBundleArrayForMessages(mHistoricMessages));
5893             }
5894
5895             fixTitleAndTextExtras(extras);
5896         }
5897
5898         private void fixTitleAndTextExtras(Bundle extras) {
5899             Message m = findLatestIncomingMessage();
5900             CharSequence text = (m == null) ? null : m.mText;
5901             CharSequence sender = m == null ? null
5902                     : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
5903             CharSequence title;
5904             if (!TextUtils.isEmpty(mConversationTitle)) {
5905                 if (!TextUtils.isEmpty(sender)) {
5906                     BidiFormatter bidi = BidiFormatter.getInstance();
5907                     title = mBuilder.mContext.getString(
5908                             com.android.internal.R.string.notification_messaging_title_template,
5909                             bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
5910                 } else {
5911                     title = mConversationTitle;
5912                 }
5913             } else {
5914                 title = sender;
5915             }
5916
5917             if (title != null) {
5918                 extras.putCharSequence(EXTRA_TITLE, title);
5919             }
5920             if (text != null) {
5921                 extras.putCharSequence(EXTRA_TEXT, text);
5922             }
5923         }
5924
5925         /**
5926          * @hide
5927          */
5928         @Override
5929         protected void restoreFromExtras(Bundle extras) {
5930             super.restoreFromExtras(extras);
5931
5932             mMessages.clear();
5933             mHistoricMessages.clear();
5934             mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
5935             mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
5936             Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
5937             if (messages != null && messages instanceof Parcelable[]) {
5938                 mMessages = Message.getMessagesFromBundleArray(messages);
5939             }
5940             Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
5941             if (histMessages != null && histMessages instanceof Parcelable[]) {
5942                 mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
5943             }
5944         }
5945
5946         /**
5947          * @hide
5948          */
5949         @Override
5950         public RemoteViews makeContentView(boolean increasedHeight) {
5951             if (!increasedHeight) {
5952                 Message m = findLatestIncomingMessage();
5953                 CharSequence title = mConversationTitle != null
5954                         ? mConversationTitle
5955                         : (m == null) ? null : m.mSender;
5956                 CharSequence text = (m == null)
5957                         ? null
5958                         : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
5959
5960                 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
5961                         mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
5962             } else {
5963                 mBuilder.mOriginalActions = mBuilder.mActions;
5964                 mBuilder.mActions = new ArrayList<>();
5965                 RemoteViews remoteViews = makeBigContentView();
5966                 mBuilder.mActions = mBuilder.mOriginalActions;
5967                 mBuilder.mOriginalActions = null;
5968                 return remoteViews;
5969             }
5970         }
5971
5972         private Message findLatestIncomingMessage() {
5973             for (int i = mMessages.size() - 1; i >= 0; i--) {
5974                 Message m = mMessages.get(i);
5975                 // Incoming messages have a non-empty sender.
5976                 if (!TextUtils.isEmpty(m.mSender)) {
5977                     return m;
5978                 }
5979             }
5980             if (!mMessages.isEmpty()) {
5981                 // No incoming messages, fall back to outgoing message
5982                 return mMessages.get(mMessages.size() - 1);
5983             }
5984             return null;
5985         }
5986
5987         /**
5988          * @hide
5989          */
5990         @Override
5991         public RemoteViews makeBigContentView() {
5992             CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
5993                     ? super.mBigContentTitle
5994                     : mConversationTitle;
5995             boolean hasTitle = !TextUtils.isEmpty(title);
5996
5997             if (mMessages.size() == 1) {
5998                 // Special case for a single message: Use the big text style
5999                 // so the collapsed and expanded versions match nicely.
6000                 CharSequence bigTitle;
6001                 CharSequence text;
6002                 if (hasTitle) {
6003                     bigTitle = title;
6004                     text = makeMessageLine(mMessages.get(0), mBuilder);
6005                 } else {
6006                     bigTitle = mMessages.get(0).mSender;
6007                     text = mMessages.get(0).mText;
6008                 }
6009                 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
6010                         mBuilder.getBigTextLayoutResource(),
6011                         mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
6012                 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
6013                 return contentView;
6014             }
6015
6016             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
6017                     mBuilder.getMessagingLayoutResource(),
6018                     mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
6019
6020             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
6021                     R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
6022
6023             // Make sure all rows are gone in case we reuse a view.
6024             for (int rowId : rowIds) {
6025                 contentView.setViewVisibility(rowId, View.GONE);
6026             }
6027
6028             int i=0;
6029             contentView.setViewLayoutMarginBottomDimen(R.id.line1,
6030                     hasTitle ? R.dimen.notification_messaging_spacing : 0);
6031             contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
6032                     !mBuilder.mN.hasLargeIcon() ? 0 : (hasTitle ? 1 : 2));
6033
6034             int contractedChildId = View.NO_ID;
6035             Message contractedMessage = findLatestIncomingMessage();
6036             int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
6037                     - (rowIds.length - mMessages.size()));
6038             while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
6039                 Message m = mHistoricMessages.get(firstHistoricMessage + i);
6040                 int rowId = rowIds[i];
6041
6042                 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
6043
6044                 if (contractedMessage == m) {
6045                     contractedChildId = rowId;
6046                 }
6047
6048                 i++;
6049             }
6050
6051             int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
6052             while (firstMessage + i < mMessages.size() && i < rowIds.length) {
6053                 Message m = mMessages.get(firstMessage + i);
6054                 int rowId = rowIds[i];
6055
6056                 contentView.setViewVisibility(rowId, View.VISIBLE);
6057                 contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
6058                 mBuilder.setTextViewColorSecondary(contentView, rowId);
6059
6060                 if (contractedMessage == m) {
6061                     contractedChildId = rowId;
6062                 }
6063
6064                 i++;
6065             }
6066             // Clear the remaining views for reapply. Ensures that historic message views can
6067             // reliably be identified as being GONE and having non-null text.
6068             while (i < rowIds.length) {
6069                 int rowId = rowIds[i];
6070                 contentView.setTextViewText(rowId, null);
6071                 i++;
6072             }
6073
6074             // Record this here to allow transformation between the contracted and expanded views.
6075             contentView.setInt(R.id.notification_messaging, "setContractedChildId",
6076                     contractedChildId);
6077             return contentView;
6078         }
6079
6080         private CharSequence makeMessageLine(Message m, Builder builder) {
6081             BidiFormatter bidi = BidiFormatter.getInstance();
6082             SpannableStringBuilder sb = new SpannableStringBuilder();
6083             boolean colorize = builder.isColorized();
6084             if (TextUtils.isEmpty(m.mSender)) {
6085                 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
6086                 sb.append(bidi.unicodeWrap(replyName),
6087                         makeFontColorSpan(colorize
6088                                 ? builder.getPrimaryTextColor()
6089                                 : mBuilder.resolveContrastColor()),
6090                         0 /* flags */);
6091             } else {
6092                 sb.append(bidi.unicodeWrap(m.mSender),
6093                         makeFontColorSpan(colorize
6094                                 ? builder.getPrimaryTextColor()
6095                                 : Color.BLACK),
6096                         0 /* flags */);
6097             }
6098             CharSequence text = m.mText == null ? "" : m.mText;
6099             sb.append("  ").append(bidi.unicodeWrap(text));
6100             return sb;
6101         }
6102
6103         /**
6104          * @hide
6105          */
6106         @Override
6107         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6108             if (increasedHeight) {
6109                 return makeBigContentView();
6110             }
6111             Message m = findLatestIncomingMessage();
6112             CharSequence title = mConversationTitle != null
6113                     ? mConversationTitle
6114                     : (m == null) ? null : m.mSender;
6115             CharSequence text = (m == null)
6116                     ? null
6117                     : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
6118
6119             return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
6120                     mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
6121         }
6122
6123         private static TextAppearanceSpan makeFontColorSpan(int color) {
6124             return new TextAppearanceSpan(null, 0, 0,
6125                     ColorStateList.valueOf(color), null);
6126         }
6127
6128         public static final class Message {
6129
6130             static final String KEY_TEXT = "text";
6131             static final String KEY_TIMESTAMP = "time";
6132             static final String KEY_SENDER = "sender";
6133             static final String KEY_DATA_MIME_TYPE = "type";
6134             static final String KEY_DATA_URI= "uri";
6135             static final String KEY_EXTRAS_BUNDLE = "extras";
6136
6137             private final CharSequence mText;
6138             private final long mTimestamp;
6139             private final CharSequence mSender;
6140
6141             private Bundle mExtras = new Bundle();
6142             private String mDataMimeType;
6143             private Uri mDataUri;
6144
6145             /**
6146              * Constructor
6147              * @param text A {@link CharSequence} to be displayed as the message content
6148              * @param timestamp Time at which the message arrived
6149              * @param sender A {@link CharSequence} to be used for displaying the name of the
6150              * sender. Should be <code>null</code> for messages by the current user, in which case
6151              * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
6152              * Should be unique amongst all individuals in the conversation, and should be
6153              * consistent during re-posts of the notification.
6154              */
6155             public Message(CharSequence text, long timestamp, CharSequence sender){
6156                 mText = text;
6157                 mTimestamp = timestamp;
6158                 mSender = sender;
6159             }
6160
6161             /**
6162              * Sets a binary blob of data and an associated MIME type for a message. In the case
6163              * where the platform doesn't support the MIME type, the original text provided in the
6164              * constructor will be used.
6165              * @param dataMimeType The MIME type of the content. See
6166              * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
6167              * types on Android and Android Wear.
6168              * @param dataUri The uri containing the content whose type is given by the MIME type.
6169              * <p class="note">
6170              * <ol>
6171              *   <li>Notification Listeners including the System UI need permission to access the
6172              *       data the Uri points to. The recommended ways to do this are:</li>
6173              *   <li>Store the data in your own ContentProvider, making sure that other apps have
6174              *       the correct permission to access your provider. The preferred mechanism for
6175              *       providing access is to use per-URI permissions which are temporary and only
6176              *       grant access to the receiving application. An easy way to create a
6177              *       ContentProvider like this is to use the FileProvider helper class.</li>
6178              *   <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
6179              *       and image MIME types, however beginning with Android 3.0 (API level 11) it can
6180              *       also store non-media types (see MediaStore.Files for more info). Files can be
6181              *       inserted into the MediaStore using scanFile() after which a content:// style
6182              *       Uri suitable for sharing is passed to the provided onScanCompleted() callback.
6183              *       Note that once added to the system MediaStore the content is accessible to any
6184              *       app on the device.</li>
6185              * </ol>
6186              * @return this object for method chaining
6187              */
6188             public Message setData(String dataMimeType, Uri dataUri) {
6189                 mDataMimeType = dataMimeType;
6190                 mDataUri = dataUri;
6191                 return this;
6192             }
6193
6194             /**
6195              * Get the text to be used for this message, or the fallback text if a type and content
6196              * Uri have been set
6197              */
6198             public CharSequence getText() {
6199                 return mText;
6200             }
6201
6202             /**
6203              * Get the time at which this message arrived
6204              */
6205             public long getTimestamp() {
6206                 return mTimestamp;
6207             }
6208
6209             /**
6210              * Get the extras Bundle for this message.
6211              */
6212             public Bundle getExtras() {
6213                 return mExtras;
6214             }
6215
6216             /**
6217              * Get the text used to display the contact's name in the messaging experience
6218              */
6219             public CharSequence getSender() {
6220                 return mSender;
6221             }
6222
6223             /**
6224              * Get the MIME type of the data pointed to by the Uri
6225              */
6226             public String getDataMimeType() {
6227                 return mDataMimeType;
6228             }
6229
6230             /**
6231              * Get the the Uri pointing to the content of the message. Can be null, in which case
6232              * {@see #getText()} is used.
6233              */
6234             public Uri getDataUri() {
6235                 return mDataUri;
6236             }
6237
6238             private Bundle toBundle() {
6239                 Bundle bundle = new Bundle();
6240                 if (mText != null) {
6241                     bundle.putCharSequence(KEY_TEXT, mText);
6242                 }
6243                 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
6244                 if (mSender != null) {
6245                     bundle.putCharSequence(KEY_SENDER, mSender);
6246                 }
6247                 if (mDataMimeType != null) {
6248                     bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
6249                 }
6250                 if (mDataUri != null) {
6251                     bundle.putParcelable(KEY_DATA_URI, mDataUri);
6252                 }
6253                 if (mExtras != null) {
6254                     bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
6255                 }
6256                 return bundle;
6257             }
6258
6259             static Bundle[] getBundleArrayForMessages(List<Message> messages) {
6260                 Bundle[] bundles = new Bundle[messages.size()];
6261                 final int N = messages.size();
6262                 for (int i = 0; i < N; i++) {
6263                     bundles[i] = messages.get(i).toBundle();
6264                 }
6265                 return bundles;
6266             }
6267
6268             static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
6269                 List<Message> messages = new ArrayList<>(bundles.length);
6270                 for (int i = 0; i < bundles.length; i++) {
6271                     if (bundles[i] instanceof Bundle) {
6272                         Message message = getMessageFromBundle((Bundle)bundles[i]);
6273                         if (message != null) {
6274                             messages.add(message);
6275                         }
6276                     }
6277                 }
6278                 return messages;
6279             }
6280
6281             static Message getMessageFromBundle(Bundle bundle) {
6282                 try {
6283                     if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
6284                         return null;
6285                     } else {
6286                         Message message = new Message(bundle.getCharSequence(KEY_TEXT),
6287                                 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
6288                         if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
6289                                 bundle.containsKey(KEY_DATA_URI)) {
6290                             message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
6291                                     (Uri) bundle.getParcelable(KEY_DATA_URI));
6292                         }
6293                         if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
6294                             message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
6295                         }
6296                         return message;
6297                     }
6298                 } catch (ClassCastException e) {
6299                     return null;
6300                 }
6301             }
6302         }
6303     }
6304
6305     /**
6306      * Helper class for generating large-format notifications that include a list of (up to 5) strings.
6307      *
6308      * Here's how you'd set the <code>InboxStyle</code> on a notification:
6309      * <pre class="prettyprint">
6310      * Notification notif = new Notification.Builder(mContext)
6311      *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
6312      *     .setContentText(subject)
6313      *     .setSmallIcon(R.drawable.new_mail)
6314      *     .setLargeIcon(aBitmap)
6315      *     .setStyle(new Notification.InboxStyle()
6316      *         .addLine(str1)
6317      *         .addLine(str2)
6318      *         .setContentTitle(&quot;&quot;)
6319      *         .setSummaryText(&quot;+3 more&quot;))
6320      *     .build();
6321      * </pre>
6322      *
6323      * @see Notification#bigContentView
6324      */
6325     public static class InboxStyle extends Style {
6326         private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
6327
6328         public InboxStyle() {
6329         }
6330
6331         /**
6332          * @deprecated use {@code InboxStyle()}.
6333          */
6334         @Deprecated
6335         public InboxStyle(Builder builder) {
6336             setBuilder(builder);
6337         }
6338
6339         /**
6340          * Overrides ContentTitle in the big form of the template.
6341          * This defaults to the value passed to setContentTitle().
6342          */
6343         public InboxStyle setBigContentTitle(CharSequence title) {
6344             internalSetBigContentTitle(safeCharSequence(title));
6345             return this;
6346         }
6347
6348         /**
6349          * Set the first line of text after the detail section in the big form of the template.
6350          */
6351         public InboxStyle setSummaryText(CharSequence cs) {
6352             internalSetSummaryText(safeCharSequence(cs));
6353             return this;
6354         }
6355
6356         /**
6357          * Append a line to the digest section of the Inbox notification.
6358          */
6359         public InboxStyle addLine(CharSequence cs) {
6360             mTexts.add(safeCharSequence(cs));
6361             return this;
6362         }
6363
6364         /**
6365          * @hide
6366          */
6367         public void addExtras(Bundle extras) {
6368             super.addExtras(extras);
6369
6370             CharSequence[] a = new CharSequence[mTexts.size()];
6371             extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
6372         }
6373
6374         /**
6375          * @hide
6376          */
6377         @Override
6378         protected void restoreFromExtras(Bundle extras) {
6379             super.restoreFromExtras(extras);
6380
6381             mTexts.clear();
6382             if (extras.containsKey(EXTRA_TEXT_LINES)) {
6383                 Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
6384             }
6385         }
6386
6387         /**
6388          * @hide
6389          */
6390         public RemoteViews makeBigContentView() {
6391             // Remove the content text so it disappears unless you have a summary
6392             // Nasty
6393             CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
6394             mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
6395
6396             RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
6397
6398             mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
6399
6400             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
6401                     R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
6402
6403             // Make sure all rows are gone in case we reuse a view.
6404             for (int rowId : rowIds) {
6405                 contentView.setViewVisibility(rowId, View.GONE);
6406             }
6407
6408             int i=0;
6409             int topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6410                     R.dimen.notification_inbox_item_top_padding);
6411             boolean first = true;
6412             int onlyViewId = 0;
6413             int maxRows = rowIds.length;
6414             if (mBuilder.mActions.size() > 0) {
6415                 maxRows--;
6416             }
6417             while (i < mTexts.size() && i < maxRows) {
6418                 CharSequence str = mTexts.get(i);
6419                 if (!TextUtils.isEmpty(str)) {
6420                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
6421                     contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
6422                     mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
6423                     contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
6424                     handleInboxImageMargin(contentView, rowIds[i], first);
6425                     if (first) {
6426                         onlyViewId = rowIds[i];
6427                     } else {
6428                         onlyViewId = 0;
6429                     }
6430                     first = false;
6431                 }
6432                 i++;
6433             }
6434             if (onlyViewId != 0) {
6435                 // We only have 1 entry, lets make it look like the normal Text of a Bigtext
6436                 topPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
6437                         R.dimen.notification_text_margin_top);
6438                 contentView.setViewPadding(onlyViewId, 0, topPadding, 0, 0);
6439             }
6440
6441             return contentView;
6442         }
6443
6444         private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
6445             int endMargin = 0;
6446             if (first) {
6447                 final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
6448                 final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
6449                 boolean hasProgress = max != 0 || ind;
6450                 if (mBuilder.mN.hasLargeIcon() && !hasProgress) {
6451                     endMargin = R.dimen.notification_content_picture_margin;
6452                 }
6453             }
6454             contentView.setViewLayoutMarginEndDimen(id, endMargin);
6455         }
6456     }
6457
6458     /**
6459      * Notification style for media playback notifications.
6460      *
6461      * In the expanded form, {@link Notification#bigContentView}, up to 5
6462      * {@link Notification.Action}s specified with
6463      * {@link Notification.Builder#addAction(Action) addAction} will be
6464      * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
6465      * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
6466      * treated as album artwork.
6467      * <p>
6468      * Unlike the other styles provided here, MediaStyle can also modify the standard-size
6469      * {@link Notification#contentView}; by providing action indices to
6470      * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
6471      * in the standard view alongside the usual content.
6472      * <p>
6473      * Notifications created with MediaStyle will have their category set to
6474      * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
6475      * category using {@link Notification.Builder#setCategory(String) setCategory()}.
6476      * <p>
6477      * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
6478      * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
6479      * the System UI can identify this as a notification representing an active media session
6480      * and respond accordingly (by showing album artwork in the lockscreen, for example).
6481      *
6482      * <p>
6483      * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
6484      * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
6485      * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
6486      * <p>
6487      *
6488      * To use this style with your Notification, feed it to
6489      * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6490      * <pre class="prettyprint">
6491      * Notification noti = new Notification.Builder()
6492      *     .setSmallIcon(R.drawable.ic_stat_player)
6493      *     .setContentTitle(&quot;Track title&quot;)
6494      *     .setContentText(&quot;Artist - Album&quot;)
6495      *     .setLargeIcon(albumArtBitmap))
6496      *     .setStyle(<b>new Notification.MediaStyle()</b>
6497      *         .setMediaSession(mySession))
6498      *     .build();
6499      * </pre>
6500      *
6501      * @see Notification#bigContentView
6502      * @see Notification.Builder#setColorized(boolean)
6503      */
6504     public static class MediaStyle extends Style {
6505         static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
6506         static final int MAX_MEDIA_BUTTONS = 5;
6507
6508         private int[] mActionsToShowInCompact = null;
6509         private MediaSession.Token mToken;
6510
6511         public MediaStyle() {
6512         }
6513
6514         /**
6515          * @deprecated use {@code MediaStyle()}.
6516          */
6517         @Deprecated
6518         public MediaStyle(Builder builder) {
6519             setBuilder(builder);
6520         }
6521
6522         /**
6523          * Request up to 3 actions (by index in the order of addition) to be shown in the compact
6524          * notification view.
6525          *
6526          * @param actions the indices of the actions to show in the compact notification view
6527          */
6528         public MediaStyle setShowActionsInCompactView(int...actions) {
6529             mActionsToShowInCompact = actions;
6530             return this;
6531         }
6532
6533         /**
6534          * Attach a {@link android.media.session.MediaSession.Token} to this Notification
6535          * to provide additional playback information and control to the SystemUI.
6536          */
6537         public MediaStyle setMediaSession(MediaSession.Token token) {
6538             mToken = token;
6539             return this;
6540         }
6541
6542         /**
6543          * @hide
6544          */
6545         @Override
6546         public Notification buildStyled(Notification wip) {
6547             super.buildStyled(wip);
6548             if (wip.category == null) {
6549                 wip.category = Notification.CATEGORY_TRANSPORT;
6550             }
6551             return wip;
6552         }
6553
6554         /**
6555          * @hide
6556          */
6557         @Override
6558         public RemoteViews makeContentView(boolean increasedHeight) {
6559             return makeMediaContentView();
6560         }
6561
6562         /**
6563          * @hide
6564          */
6565         @Override
6566         public RemoteViews makeBigContentView() {
6567             return makeMediaBigContentView();
6568         }
6569
6570         /**
6571          * @hide
6572          */
6573         @Override
6574         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6575             RemoteViews expanded = makeMediaBigContentView();
6576             return expanded != null ? expanded : makeMediaContentView();
6577         }
6578
6579         /** @hide */
6580         @Override
6581         public void addExtras(Bundle extras) {
6582             super.addExtras(extras);
6583
6584             if (mToken != null) {
6585                 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
6586             }
6587             if (mActionsToShowInCompact != null) {
6588                 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
6589             }
6590         }
6591
6592         /**
6593          * @hide
6594          */
6595         @Override
6596         protected void restoreFromExtras(Bundle extras) {
6597             super.restoreFromExtras(extras);
6598
6599             if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
6600                 mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
6601             }
6602             if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
6603                 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
6604             }
6605         }
6606
6607         private RemoteViews generateMediaActionButton(Action action, int color) {
6608             final boolean tombstone = (action.actionIntent == null);
6609             RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
6610                     R.layout.notification_material_media_action);
6611             button.setImageViewIcon(R.id.action0, action.getIcon());
6612             button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
6613                     -1);
6614             if (!tombstone) {
6615                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
6616             }
6617             button.setContentDescription(R.id.action0, action.title);
6618             return button;
6619         }
6620
6621         private RemoteViews makeMediaContentView() {
6622             RemoteViews view = mBuilder.applyStandardTemplate(
6623                     R.layout.notification_template_material_media, false /* hasProgress */);
6624
6625             final int numActions = mBuilder.mActions.size();
6626             final int N = mActionsToShowInCompact == null
6627                     ? 0
6628                     : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6629             if (N > 0) {
6630                 view.removeAllViews(com.android.internal.R.id.media_actions);
6631                 for (int i = 0; i < N; i++) {
6632                     if (i >= numActions) {
6633                         throw new IllegalArgumentException(String.format(
6634                                 "setShowActionsInCompactView: action %d out of bounds (max %d)",
6635                                 i, numActions - 1));
6636                     }
6637
6638                     final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
6639                     final RemoteViews button = generateMediaActionButton(action,
6640                             getPrimaryHighlightColor());
6641                     view.addView(com.android.internal.R.id.media_actions, button);
6642                 }
6643             }
6644             handleImage(view);
6645             // handle the content margin
6646             int endMargin = R.dimen.notification_content_margin_end;
6647             if (mBuilder.mN.hasLargeIcon()) {
6648                 endMargin = R.dimen.notification_content_plus_picture_margin_end;
6649             }
6650             view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
6651             return view;
6652         }
6653
6654         private int getPrimaryHighlightColor() {
6655             return mBuilder.getPrimaryHighlightColor();
6656         }
6657
6658         private RemoteViews makeMediaBigContentView() {
6659             final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
6660             // Dont add an expanded view if there is no more content to be revealed
6661             int actionsInCompact = mActionsToShowInCompact == null
6662                     ? 0
6663                     : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
6664             if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
6665                 return null;
6666             }
6667             RemoteViews big = mBuilder.applyStandardTemplate(
6668                     R.layout.notification_template_material_big_media,
6669                     false);
6670
6671             if (actionCount > 0) {
6672                 big.removeAllViews(com.android.internal.R.id.media_actions);
6673                 for (int i = 0; i < actionCount; i++) {
6674                     final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
6675                             getPrimaryHighlightColor());
6676                     big.addView(com.android.internal.R.id.media_actions, button);
6677                 }
6678             }
6679             handleImage(big);
6680             return big;
6681         }
6682
6683         private void handleImage(RemoteViews contentView) {
6684             if (mBuilder.mN.hasLargeIcon()) {
6685                 contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
6686                 contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
6687             }
6688         }
6689
6690         /**
6691          * @hide
6692          */
6693         @Override
6694         protected boolean hasProgress() {
6695             return false;
6696         }
6697     }
6698
6699     /**
6700      * Notification style for custom views that are decorated by the system
6701      *
6702      * <p>Instead of providing a notification that is completely custom, a developer can set this
6703      * style and still obtain system decorations like the notification header with the expand
6704      * affordance and actions.
6705      *
6706      * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6707      * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6708      * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6709      * corresponding custom views to display.
6710      *
6711      * To use this style with your Notification, feed it to
6712      * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6713      * <pre class="prettyprint">
6714      * Notification noti = new Notification.Builder()
6715      *     .setSmallIcon(R.drawable.ic_stat_player)
6716      *     .setLargeIcon(albumArtBitmap))
6717      *     .setCustomContentView(contentView);
6718      *     .setStyle(<b>new Notification.DecoratedCustomViewStyle()</b>)
6719      *     .build();
6720      * </pre>
6721      */
6722     public static class DecoratedCustomViewStyle extends Style {
6723
6724         public DecoratedCustomViewStyle() {
6725         }
6726
6727         /**
6728          * @hide
6729          */
6730         public boolean displayCustomViewInline() {
6731             return true;
6732         }
6733
6734         /**
6735          * @hide
6736          */
6737         @Override
6738         public RemoteViews makeContentView(boolean increasedHeight) {
6739             return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
6740         }
6741
6742         /**
6743          * @hide
6744          */
6745         @Override
6746         public RemoteViews makeBigContentView() {
6747             return makeDecoratedBigContentView();
6748         }
6749
6750         /**
6751          * @hide
6752          */
6753         @Override
6754         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6755             return makeDecoratedHeadsUpContentView();
6756         }
6757
6758         private RemoteViews makeDecoratedHeadsUpContentView() {
6759             RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null
6760                     ? mBuilder.mN.contentView
6761                     : mBuilder.mN.headsUpContentView;
6762             if (mBuilder.mActions.size() == 0) {
6763                return makeStandardTemplateWithCustomContent(headsUpContentView);
6764             }
6765             RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6766                         mBuilder.getBigBaseLayoutResource());
6767             buildIntoRemoteViewContent(remoteViews, headsUpContentView);
6768             return remoteViews;
6769         }
6770
6771         private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) {
6772             RemoteViews remoteViews = mBuilder.applyStandardTemplate(
6773                     mBuilder.getBaseLayoutResource());
6774             buildIntoRemoteViewContent(remoteViews, customContent);
6775             return remoteViews;
6776         }
6777
6778         private RemoteViews makeDecoratedBigContentView() {
6779             RemoteViews bigContentView = mBuilder.mN.bigContentView == null
6780                     ? mBuilder.mN.contentView
6781                     : mBuilder.mN.bigContentView;
6782             if (mBuilder.mActions.size() == 0) {
6783                 return makeStandardTemplateWithCustomContent(bigContentView);
6784             }
6785             RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions(
6786                     mBuilder.getBigBaseLayoutResource());
6787             buildIntoRemoteViewContent(remoteViews, bigContentView);
6788             return remoteViews;
6789         }
6790
6791         private void buildIntoRemoteViewContent(RemoteViews remoteViews,
6792                 RemoteViews customContent) {
6793             if (customContent != null) {
6794                 // Need to clone customContent before adding, because otherwise it can no longer be
6795                 // parceled independently of remoteViews.
6796                 customContent = customContent.clone();
6797                 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
6798                 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
6799             }
6800             // also update the end margin if there is an image
6801             int endMargin = R.dimen.notification_content_margin_end;
6802             if (mBuilder.mN.hasLargeIcon()) {
6803                 endMargin = R.dimen.notification_content_plus_picture_margin_end;
6804             }
6805             remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
6806         }
6807     }
6808
6809     /**
6810      * Notification style for media custom views that are decorated by the system
6811      *
6812      * <p>Instead of providing a media notification that is completely custom, a developer can set
6813      * this style and still obtain system decorations like the notification header with the expand
6814      * affordance and actions.
6815      *
6816      * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)},
6817      * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
6818      * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
6819      * corresponding custom views to display.
6820      * <p>
6821      * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
6822      * notification by using {@link Notification.Builder#setColorized(boolean)}.
6823      * <p>
6824      * To use this style with your Notification, feed it to
6825      * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
6826      * <pre class="prettyprint">
6827      * Notification noti = new Notification.Builder()
6828      *     .setSmallIcon(R.drawable.ic_stat_player)
6829      *     .setLargeIcon(albumArtBitmap))
6830      *     .setCustomContentView(contentView);
6831      *     .setStyle(<b>new Notification.DecoratedMediaCustomViewStyle()</b>
6832      *          .setMediaSession(mySession))
6833      *     .build();
6834      * </pre>
6835      *
6836      * @see android.app.Notification.DecoratedCustomViewStyle
6837      * @see android.app.Notification.MediaStyle
6838      */
6839     public static class DecoratedMediaCustomViewStyle extends MediaStyle {
6840
6841         public DecoratedMediaCustomViewStyle() {
6842         }
6843
6844         /**
6845          * @hide
6846          */
6847         public boolean displayCustomViewInline() {
6848             return true;
6849         }
6850
6851         /**
6852          * @hide
6853          */
6854         @Override
6855         public RemoteViews makeContentView(boolean increasedHeight) {
6856             RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
6857             return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6858                     mBuilder.mN.contentView);
6859         }
6860
6861         /**
6862          * @hide
6863          */
6864         @Override
6865         public RemoteViews makeBigContentView() {
6866             RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
6867                     ? mBuilder.mN.bigContentView
6868                     : mBuilder.mN.contentView;
6869             return makeBigContentViewWithCustomContent(customRemoteView);
6870         }
6871
6872         private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
6873             RemoteViews remoteViews = super.makeBigContentView();
6874             if (remoteViews != null) {
6875                 return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
6876                         customRemoteView);
6877             } else if (customRemoteView != mBuilder.mN.contentView){
6878                 remoteViews = super.makeContentView(false /* increasedHeight */);
6879                 return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
6880                         customRemoteView);
6881             } else {
6882                 return null;
6883             }
6884         }
6885
6886         /**
6887          * @hide
6888          */
6889         @Override
6890         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
6891             RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
6892                     ? mBuilder.mN.headsUpContentView
6893                     : mBuilder.mN.contentView;
6894             return makeBigContentViewWithCustomContent(customRemoteView);
6895         }
6896
6897         private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
6898                 RemoteViews customContent) {
6899             if (customContent != null) {
6900                 // Need to clone customContent before adding, because otherwise it can no longer be
6901                 // parceled independently of remoteViews.
6902                 customContent = customContent.clone();
6903                 remoteViews.removeAllViews(id);
6904                 remoteViews.addView(id, customContent);
6905             }
6906             return remoteViews;
6907         }
6908     }
6909
6910     // When adding a new Style subclass here, don't forget to update
6911     // Builder.getNotificationStyleClass.
6912
6913     /**
6914      * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
6915      * metadata or change options on a notification builder.
6916      */
6917     public interface Extender {
6918         /**
6919          * Apply this extender to a notification builder.
6920          * @param builder the builder to be modified.
6921          * @return the build object for chaining.
6922          */
6923         public Builder extend(Builder builder);
6924     }
6925
6926     /**
6927      * Helper class to add wearable extensions to notifications.
6928      * <p class="note"> See
6929      * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
6930      * for Android Wear</a> for more information on how to use this class.
6931      * <p>
6932      * To create a notification with wearable extensions:
6933      * <ol>
6934      *   <li>Create a {@link android.app.Notification.Builder}, setting any desired
6935      *   properties.
6936      *   <li>Create a {@link android.app.Notification.WearableExtender}.
6937      *   <li>Set wearable-specific properties using the
6938      *   {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
6939      *   <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
6940      *   notification.
6941      *   <li>Post the notification to the notification system with the
6942      *   {@code NotificationManager.notify(...)} methods.
6943      * </ol>
6944      *
6945      * <pre class="prettyprint">
6946      * Notification notif = new Notification.Builder(mContext)
6947      *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
6948      *         .setContentText(subject)
6949      *         .setSmallIcon(R.drawable.new_mail)
6950      *         .extend(new Notification.WearableExtender()
6951      *                 .setContentIcon(R.drawable.new_mail))
6952      *         .build();
6953      * NotificationManager notificationManger =
6954      *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
6955      * notificationManger.notify(0, notif);</pre>
6956      *
6957      * <p>Wearable extensions can be accessed on an existing notification by using the
6958      * {@code WearableExtender(Notification)} constructor,
6959      * and then using the {@code get} methods to access values.
6960      *
6961      * <pre class="prettyprint">
6962      * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
6963      *         notification);
6964      * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
6965      */
6966     public static final class WearableExtender implements Extender {
6967         /**
6968          * Sentinel value for an action index that is unset.
6969          */
6970         public static final int UNSET_ACTION_INDEX = -1;
6971
6972         /**
6973          * Size value for use with {@link #setCustomSizePreset} to show this notification with
6974          * default sizing.
6975          * <p>For custom display notifications created using {@link #setDisplayIntent},
6976          * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
6977          * on their content.
6978          */
6979         public static final int SIZE_DEFAULT = 0;
6980
6981         /**
6982          * Size value for use with {@link #setCustomSizePreset} to show this notification
6983          * with an extra small size.
6984          * <p>This value is only applicable for custom display notifications created using
6985          * {@link #setDisplayIntent}.
6986          */
6987         public static final int SIZE_XSMALL = 1;
6988
6989         /**
6990          * Size value for use with {@link #setCustomSizePreset} to show this notification
6991          * with a small size.
6992          * <p>This value is only applicable for custom display notifications created using
6993          * {@link #setDisplayIntent}.
6994          */
6995         public static final int SIZE_SMALL = 2;
6996
6997         /**
6998          * Size value for use with {@link #setCustomSizePreset} to show this notification
6999          * with a medium size.
7000          * <p>This value is only applicable for custom display notifications created using
7001          * {@link #setDisplayIntent}.
7002          */
7003         public static final int SIZE_MEDIUM = 3;
7004
7005         /**
7006          * Size value for use with {@link #setCustomSizePreset} to show this notification
7007          * with a large size.
7008          * <p>This value is only applicable for custom display notifications created using
7009          * {@link #setDisplayIntent}.
7010          */
7011         public static final int SIZE_LARGE = 4;
7012
7013         /**
7014          * Size value for use with {@link #setCustomSizePreset} to show this notification
7015          * full screen.
7016          * <p>This value is only applicable for custom display notifications created using
7017          * {@link #setDisplayIntent}.
7018          */
7019         public static final int SIZE_FULL_SCREEN = 5;
7020
7021         /**
7022          * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
7023          * short amount of time when this notification is displayed on the screen. This
7024          * is the default value.
7025          */
7026         public static final int SCREEN_TIMEOUT_SHORT = 0;
7027
7028         /**
7029          * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
7030          * for a longer amount of time when this notification is displayed on the screen.
7031          */
7032         public static final int SCREEN_TIMEOUT_LONG = -1;
7033
7034         /** Notification extra which contains wearable extensions */
7035         private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
7036
7037         // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
7038         private static final String KEY_ACTIONS = "actions";
7039         private static final String KEY_FLAGS = "flags";
7040         private static final String KEY_DISPLAY_INTENT = "displayIntent";
7041         private static final String KEY_PAGES = "pages";
7042         private static final String KEY_BACKGROUND = "background";
7043         private static final String KEY_CONTENT_ICON = "contentIcon";
7044         private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
7045         private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
7046         private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
7047         private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
7048         private static final String KEY_GRAVITY = "gravity";
7049         private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
7050         private static final String KEY_DISMISSAL_ID = "dismissalId";
7051         private static final String KEY_BRIDGE_TAG = "bridgeTag";
7052
7053         // Flags bitwise-ored to mFlags
7054         private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
7055         private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
7056         private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
7057         private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
7058         private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
7059         private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
7060         private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
7061
7062         // Default value for flags integer
7063         private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
7064
7065         private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
7066         private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
7067
7068         private ArrayList<Action> mActions = new ArrayList<Action>();
7069         private int mFlags = DEFAULT_FLAGS;
7070         private PendingIntent mDisplayIntent;
7071         private ArrayList<Notification> mPages = new ArrayList<Notification>();
7072         private Bitmap mBackground;
7073         private int mContentIcon;
7074         private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
7075         private int mContentActionIndex = UNSET_ACTION_INDEX;
7076         private int mCustomSizePreset = SIZE_DEFAULT;
7077         private int mCustomContentHeight;
7078         private int mGravity = DEFAULT_GRAVITY;
7079         private int mHintScreenTimeout;
7080         private String mDismissalId;
7081         private String mBridgeTag;
7082
7083         /**
7084          * Create a {@link android.app.Notification.WearableExtender} with default
7085          * options.
7086          */
7087         public WearableExtender() {
7088         }
7089
7090         public WearableExtender(Notification notif) {
7091             Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
7092             if (wearableBundle != null) {
7093                 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
7094                 if (actions != null) {
7095                     mActions.addAll(actions);
7096                 }
7097
7098                 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
7099                 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
7100
7101                 Notification[] pages = getNotificationArrayFromBundle(
7102                         wearableBundle, KEY_PAGES);
7103                 if (pages != null) {
7104                     Collections.addAll(mPages, pages);
7105                 }
7106
7107                 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
7108                 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
7109                 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
7110                         DEFAULT_CONTENT_ICON_GRAVITY);
7111                 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
7112                         UNSET_ACTION_INDEX);
7113                 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
7114                         SIZE_DEFAULT);
7115                 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
7116                 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
7117                 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
7118                 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
7119                 mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
7120             }
7121         }
7122
7123         /**
7124          * Apply wearable extensions to a notification that is being built. This is typically
7125          * called by the {@link android.app.Notification.Builder#extend} method of
7126          * {@link android.app.Notification.Builder}.
7127          */
7128         @Override
7129         public Notification.Builder extend(Notification.Builder builder) {
7130             Bundle wearableBundle = new Bundle();
7131
7132             if (!mActions.isEmpty()) {
7133                 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
7134             }
7135             if (mFlags != DEFAULT_FLAGS) {
7136                 wearableBundle.putInt(KEY_FLAGS, mFlags);
7137             }
7138             if (mDisplayIntent != null) {
7139                 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
7140             }
7141             if (!mPages.isEmpty()) {
7142                 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
7143                         new Notification[mPages.size()]));
7144             }
7145             if (mBackground != null) {
7146                 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
7147             }
7148             if (mContentIcon != 0) {
7149                 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
7150             }
7151             if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
7152                 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
7153             }
7154             if (mContentActionIndex != UNSET_ACTION_INDEX) {
7155                 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
7156                         mContentActionIndex);
7157             }
7158             if (mCustomSizePreset != SIZE_DEFAULT) {
7159                 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
7160             }
7161             if (mCustomContentHeight != 0) {
7162                 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
7163             }
7164             if (mGravity != DEFAULT_GRAVITY) {
7165                 wearableBundle.putInt(KEY_GRAVITY, mGravity);
7166             }
7167             if (mHintScreenTimeout != 0) {
7168                 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
7169             }
7170             if (mDismissalId != null) {
7171                 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
7172             }
7173             if (mBridgeTag != null) {
7174                 wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
7175             }
7176
7177             builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
7178             return builder;
7179         }
7180
7181         @Override
7182         public WearableExtender clone() {
7183             WearableExtender that = new WearableExtender();
7184             that.mActions = new ArrayList<Action>(this.mActions);
7185             that.mFlags = this.mFlags;
7186             that.mDisplayIntent = this.mDisplayIntent;
7187             that.mPages = new ArrayList<Notification>(this.mPages);
7188             that.mBackground = this.mBackground;
7189             that.mContentIcon = this.mContentIcon;
7190             that.mContentIconGravity = this.mContentIconGravity;
7191             that.mContentActionIndex = this.mContentActionIndex;
7192             that.mCustomSizePreset = this.mCustomSizePreset;
7193             that.mCustomContentHeight = this.mCustomContentHeight;
7194             that.mGravity = this.mGravity;
7195             that.mHintScreenTimeout = this.mHintScreenTimeout;
7196             that.mDismissalId = this.mDismissalId;
7197             that.mBridgeTag = this.mBridgeTag;
7198             return that;
7199         }
7200
7201         /**
7202          * Add a wearable action to this notification.
7203          *
7204          * <p>When wearable actions are added using this method, the set of actions that
7205          * show on a wearable device splits from devices that only show actions added
7206          * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7207          * of which actions display on different devices.
7208          *
7209          * @param action the action to add to this notification
7210          * @return this object for method chaining
7211          * @see android.app.Notification.Action
7212          */
7213         public WearableExtender addAction(Action action) {
7214             mActions.add(action);
7215             return this;
7216         }
7217
7218         /**
7219          * Adds wearable actions to this notification.
7220          *
7221          * <p>When wearable actions are added using this method, the set of actions that
7222          * show on a wearable device splits from devices that only show actions added
7223          * using {@link android.app.Notification.Builder#addAction}. This allows for customization
7224          * of which actions display on different devices.
7225          *
7226          * @param actions the actions to add to this notification
7227          * @return this object for method chaining
7228          * @see android.app.Notification.Action
7229          */
7230         public WearableExtender addActions(List<Action> actions) {
7231             mActions.addAll(actions);
7232             return this;
7233         }
7234
7235         /**
7236          * Clear all wearable actions present on this builder.
7237          * @return this object for method chaining.
7238          * @see #addAction
7239          */
7240         public WearableExtender clearActions() {
7241             mActions.clear();
7242             return this;
7243         }
7244
7245         /**
7246          * Get the wearable actions present on this notification.
7247          */
7248         public List<Action> getActions() {
7249             return mActions;
7250         }
7251
7252         /**
7253          * Set an intent to launch inside of an activity view when displaying
7254          * this notification. The {@link PendingIntent} provided should be for an activity.
7255          *
7256          * <pre class="prettyprint">
7257          * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
7258          * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
7259          *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
7260          * Notification notif = new Notification.Builder(context)
7261          *         .extend(new Notification.WearableExtender()
7262          *                 .setDisplayIntent(displayPendingIntent)
7263          *                 .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
7264          *         .build();</pre>
7265          *
7266          * <p>The activity to launch needs to allow embedding, must be exported, and
7267          * should have an empty task affinity. It is also recommended to use the device
7268          * default light theme.
7269          *
7270          * <p>Example AndroidManifest.xml entry:
7271          * <pre class="prettyprint">
7272          * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
7273          *     android:exported=&quot;true&quot;
7274          *     android:allowEmbedded=&quot;true&quot;
7275          *     android:taskAffinity=&quot;&quot;
7276          *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
7277          *
7278          * @param intent the {@link PendingIntent} for an activity
7279          * @return this object for method chaining
7280          * @see android.app.Notification.WearableExtender#getDisplayIntent
7281          */
7282         public WearableExtender setDisplayIntent(PendingIntent intent) {
7283             mDisplayIntent = intent;
7284             return this;
7285         }
7286
7287         /**
7288          * Get the intent to launch inside of an activity view when displaying this
7289          * notification. This {@code PendingIntent} should be for an activity.
7290          */
7291         public PendingIntent getDisplayIntent() {
7292             return mDisplayIntent;
7293         }
7294
7295         /**
7296          * Add an additional page of content to display with this notification. The current
7297          * notification forms the first page, and pages added using this function form
7298          * subsequent pages. This field can be used to separate a notification into multiple
7299          * sections.
7300          *
7301          * @param page the notification to add as another page
7302          * @return this object for method chaining
7303          * @see android.app.Notification.WearableExtender#getPages
7304          */
7305         public WearableExtender addPage(Notification page) {
7306             mPages.add(page);
7307             return this;
7308         }
7309
7310         /**
7311          * Add additional pages of content to display with this notification. The current
7312          * notification forms the first page, and pages added using this function form
7313          * subsequent pages. This field can be used to separate a notification into multiple
7314          * sections.
7315          *
7316          * @param pages a list of notifications
7317          * @return this object for method chaining
7318          * @see android.app.Notification.WearableExtender#getPages
7319          */
7320         public WearableExtender addPages(List<Notification> pages) {
7321             mPages.addAll(pages);
7322             return this;
7323         }
7324
7325         /**
7326          * Clear all additional pages present on this builder.
7327          * @return this object for method chaining.
7328          * @see #addPage
7329          */
7330         public WearableExtender clearPages() {
7331             mPages.clear();
7332             return this;
7333         }
7334
7335         /**
7336          * Get the array of additional pages of content for displaying this notification. The
7337          * current notification forms the first page, and elements within this array form
7338          * subsequent pages. This field can be used to separate a notification into multiple
7339          * sections.
7340          * @return the pages for this notification
7341          */
7342         public List<Notification> getPages() {
7343             return mPages;
7344         }
7345
7346         /**
7347          * Set a background image to be displayed behind the notification content.
7348          * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7349          * will work with any notification style.
7350          *
7351          * @param background the background bitmap
7352          * @return this object for method chaining
7353          * @see android.app.Notification.WearableExtender#getBackground
7354          */
7355         public WearableExtender setBackground(Bitmap background) {
7356             mBackground = background;
7357             return this;
7358         }
7359
7360         /**
7361          * Get a background image to be displayed behind the notification content.
7362          * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
7363          * will work with any notification style.
7364          *
7365          * @return the background image
7366          * @see android.app.Notification.WearableExtender#setBackground
7367          */
7368         public Bitmap getBackground() {
7369             return mBackground;
7370         }
7371
7372         /**
7373          * Set an icon that goes with the content of this notification.
7374          */
7375         public WearableExtender setContentIcon(int icon) {
7376             mContentIcon = icon;
7377             return this;
7378         }
7379
7380         /**
7381          * Get an icon that goes with the content of this notification.
7382          */
7383         public int getContentIcon() {
7384             return mContentIcon;
7385         }
7386
7387         /**
7388          * Set the gravity that the content icon should have within the notification display.
7389          * Supported values include {@link android.view.Gravity#START} and
7390          * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7391          * @see #setContentIcon
7392          */
7393         public WearableExtender setContentIconGravity(int contentIconGravity) {
7394             mContentIconGravity = contentIconGravity;
7395             return this;
7396         }
7397
7398         /**
7399          * Get the gravity that the content icon should have within the notification display.
7400          * Supported values include {@link android.view.Gravity#START} and
7401          * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
7402          * @see #getContentIcon
7403          */
7404         public int getContentIconGravity() {
7405             return mContentIconGravity;
7406         }
7407
7408         /**
7409          * Set an action from this notification's actions to be clickable with the content of
7410          * this notification. This action will no longer display separately from the
7411          * notification's content.
7412          *
7413          * <p>For notifications with multiple pages, child pages can also have content actions
7414          * set, although the list of available actions comes from the main notification and not
7415          * from the child page's notification.
7416          *
7417          * @param actionIndex The index of the action to hoist onto the current notification page.
7418          *                    If wearable actions were added to the main notification, this index
7419          *                    will apply to that list, otherwise it will apply to the regular
7420          *                    actions list.
7421          */
7422         public WearableExtender setContentAction(int actionIndex) {
7423             mContentActionIndex = actionIndex;
7424             return this;
7425         }
7426
7427         /**
7428          * Get the index of the notification action, if any, that was specified as being clickable
7429          * with the content of this notification. This action will no longer display separately
7430          * from the notification's content.
7431          *
7432          * <p>For notifications with multiple pages, child pages can also have content actions
7433          * set, although the list of available actions comes from the main notification and not
7434          * from the child page's notification.
7435          *
7436          * <p>If wearable specific actions were added to the main notification, this index will
7437          * apply to that list, otherwise it will apply to the regular actions list.
7438          *
7439          * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
7440          */
7441         public int getContentAction() {
7442             return mContentActionIndex;
7443         }
7444
7445         /**
7446          * Set the gravity that this notification should have within the available viewport space.
7447          * Supported values include {@link android.view.Gravity#TOP},
7448          * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7449          * The default value is {@link android.view.Gravity#BOTTOM}.
7450          */
7451         public WearableExtender setGravity(int gravity) {
7452             mGravity = gravity;
7453             return this;
7454         }
7455
7456         /**
7457          * Get the gravity that this notification should have within the available viewport space.
7458          * Supported values include {@link android.view.Gravity#TOP},
7459          * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
7460          * The default value is {@link android.view.Gravity#BOTTOM}.
7461          */
7462         public int getGravity() {
7463             return mGravity;
7464         }
7465
7466         /**
7467          * Set the custom size preset for the display of this notification out of the available
7468          * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7469          * {@link #SIZE_LARGE}.
7470          * <p>Some custom size presets are only applicable for custom display notifications created
7471          * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
7472          * documentation for the preset in question. See also
7473          * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
7474          */
7475         public WearableExtender setCustomSizePreset(int sizePreset) {
7476             mCustomSizePreset = sizePreset;
7477             return this;
7478         }
7479
7480         /**
7481          * Get the custom size preset for the display of this notification out of the available
7482          * presets found in {@link android.app.Notification.WearableExtender}, e.g.
7483          * {@link #SIZE_LARGE}.
7484          * <p>Some custom size presets are only applicable for custom display notifications created
7485          * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
7486          * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
7487          */
7488         public int getCustomSizePreset() {
7489             return mCustomSizePreset;
7490         }
7491
7492         /**
7493          * Set the custom height in pixels for the display of this notification's content.
7494          * <p>This option is only available for custom display notifications created
7495          * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
7496          * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
7497          * {@link #getCustomContentHeight}.
7498          */
7499         public WearableExtender setCustomContentHeight(int height) {
7500             mCustomContentHeight = height;
7501             return this;
7502         }
7503
7504         /**
7505          * Get the custom height in pixels for the display of this notification's content.
7506          * <p>This option is only available for custom display notifications created
7507          * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
7508          * {@link #setCustomContentHeight}.
7509          */
7510         public int getCustomContentHeight() {
7511             return mCustomContentHeight;
7512         }
7513
7514         /**
7515          * Set whether the scrolling position for the contents of this notification should start
7516          * at the bottom of the contents instead of the top when the contents are too long to
7517          * display within the screen.  Default is false (start scroll at the top).
7518          */
7519         public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
7520             setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
7521             return this;
7522         }
7523
7524         /**
7525          * Get whether the scrolling position for the contents of this notification should start
7526          * at the bottom of the contents instead of the top when the contents are too long to
7527          * display within the screen. Default is false (start scroll at the top).
7528          */
7529         public boolean getStartScrollBottom() {
7530             return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
7531         }
7532
7533         /**
7534          * Set whether the content intent is available when the wearable device is not connected
7535          * to a companion device.  The user can still trigger this intent when the wearable device
7536          * is offline, but a visual hint will indicate that the content intent may not be available.
7537          * Defaults to true.
7538          */
7539         public WearableExtender setContentIntentAvailableOffline(
7540                 boolean contentIntentAvailableOffline) {
7541             setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
7542             return this;
7543         }
7544
7545         /**
7546          * Get whether the content intent is available when the wearable device is not connected
7547          * to a companion device.  The user can still trigger this intent when the wearable device
7548          * is offline, but a visual hint will indicate that the content intent may not be available.
7549          * Defaults to true.
7550          */
7551         public boolean getContentIntentAvailableOffline() {
7552             return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
7553         }
7554
7555         /**
7556          * Set a hint that this notification's icon should not be displayed.
7557          * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
7558          * @return this object for method chaining
7559          */
7560         public WearableExtender setHintHideIcon(boolean hintHideIcon) {
7561             setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
7562             return this;
7563         }
7564
7565         /**
7566          * Get a hint that this notification's icon should not be displayed.
7567          * @return {@code true} if this icon should not be displayed, false otherwise.
7568          * The default value is {@code false} if this was never set.
7569          */
7570         public boolean getHintHideIcon() {
7571             return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
7572         }
7573
7574         /**
7575          * Set a visual hint that only the background image of this notification should be
7576          * displayed, and other semantic content should be hidden. This hint is only applicable
7577          * to sub-pages added using {@link #addPage}.
7578          */
7579         public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
7580             setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
7581             return this;
7582         }
7583
7584         /**
7585          * Get a visual hint that only the background image of this notification should be
7586          * displayed, and other semantic content should be hidden. This hint is only applicable
7587          * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
7588          */
7589         public boolean getHintShowBackgroundOnly() {
7590             return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
7591         }
7592
7593         /**
7594          * Set a hint that this notification's background should not be clipped if possible,
7595          * and should instead be resized to fully display on the screen, retaining the aspect
7596          * ratio of the image. This can be useful for images like barcodes or qr codes.
7597          * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
7598          * @return this object for method chaining
7599          */
7600         public WearableExtender setHintAvoidBackgroundClipping(
7601                 boolean hintAvoidBackgroundClipping) {
7602             setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
7603             return this;
7604         }
7605
7606         /**
7607          * Get a hint that this notification's background should not be clipped if possible,
7608          * and should instead be resized to fully display on the screen, retaining the aspect
7609          * ratio of the image. This can be useful for images like barcodes or qr codes.
7610          * @return {@code true} if it's ok if the background is clipped on the screen, false
7611          * otherwise. The default value is {@code false} if this was never set.
7612          */
7613         public boolean getHintAvoidBackgroundClipping() {
7614             return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
7615         }
7616
7617         /**
7618          * Set a hint that the screen should remain on for at least this duration when
7619          * this notification is displayed on the screen.
7620          * @param timeout The requested screen timeout in milliseconds. Can also be either
7621          *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7622          * @return this object for method chaining
7623          */
7624         public WearableExtender setHintScreenTimeout(int timeout) {
7625             mHintScreenTimeout = timeout;
7626             return this;
7627         }
7628
7629         /**
7630          * Get the duration, in milliseconds, that the screen should remain on for
7631          * when this notification is displayed.
7632          * @return the duration in milliseconds if > 0, or either one of the sentinel values
7633          *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
7634          */
7635         public int getHintScreenTimeout() {
7636             return mHintScreenTimeout;
7637         }
7638
7639         /**
7640          * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
7641          * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7642          * qr codes, as well as other simple black-and-white tickets.
7643          * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
7644          * @return this object for method chaining
7645          */
7646         public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
7647             setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
7648             return this;
7649         }
7650
7651         /**
7652          * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
7653          * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
7654          * qr codes, as well as other simple black-and-white tickets.
7655          * @return {@code true} if it should be displayed in ambient, false otherwise
7656          * otherwise. The default value is {@code false} if this was never set.
7657          */
7658         public boolean getHintAmbientBigPicture() {
7659             return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
7660         }
7661
7662         /**
7663          * Set a hint that this notification's content intent will launch an {@link Activity}
7664          * directly, telling the platform that it can generate the appropriate transitions.
7665          * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
7666          * an activity and transitions should be generated, false otherwise.
7667          * @return this object for method chaining
7668          */
7669         public WearableExtender setHintContentIntentLaunchesActivity(
7670                 boolean hintContentIntentLaunchesActivity) {
7671             setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
7672             return this;
7673         }
7674
7675         /**
7676          * Get a hint that this notification's content intent will launch an {@link Activity}
7677          * directly, telling the platform that it can generate the appropriate transitions
7678          * @return {@code true} if the content intent will launch an activity and transitions should
7679          * be generated, false otherwise. The default value is {@code false} if this was never set.
7680          */
7681         public boolean getHintContentIntentLaunchesActivity() {
7682             return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
7683         }
7684
7685         /**
7686          * Sets the dismissal id for this notification. If a notification is posted with a
7687          * dismissal id, then when that notification is canceled, notifications on other wearables
7688          * and the paired Android phone having that same dismissal id will also be canceled. See
7689          * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
7690          * Notifications</a> for more information.
7691          * @param dismissalId the dismissal id of the notification.
7692          * @return this object for method chaining
7693          */
7694         public WearableExtender setDismissalId(String dismissalId) {
7695             mDismissalId = dismissalId;
7696             return this;
7697         }
7698
7699         /**
7700          * Returns the dismissal id of the notification.
7701          * @return the dismissal id of the notification or null if it has not been set.
7702          */
7703         public String getDismissalId() {
7704             return mDismissalId;
7705         }
7706
7707         /**
7708          * Sets a bridge tag for this notification. A bridge tag can be set for notifications
7709          * posted from a phone to provide finer-grained control on what notifications are bridged
7710          * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
7711          * Features to Notifications</a> for more information.
7712          * @param bridgeTag the bridge tag of the notification.
7713          * @return this object for method chaining
7714          */
7715         public WearableExtender setBridgeTag(String bridgeTag) {
7716             mBridgeTag = bridgeTag;
7717             return this;
7718         }
7719
7720         /**
7721          * Returns the bridge tag of the notification.
7722          * @return the bridge tag or null if not present.
7723          */
7724         public String getBridgeTag() {
7725             return mBridgeTag;
7726         }
7727
7728         private void setFlag(int mask, boolean value) {
7729             if (value) {
7730                 mFlags |= mask;
7731             } else {
7732                 mFlags &= ~mask;
7733             }
7734         }
7735     }
7736
7737     /**
7738      * <p>Helper class to add Android Auto extensions to notifications. To create a notification
7739      * with car extensions:
7740      *
7741      * <ol>
7742      *  <li>Create an {@link Notification.Builder}, setting any desired
7743      *  properties.
7744      *  <li>Create a {@link CarExtender}.
7745      *  <li>Set car-specific properties using the {@code add} and {@code set} methods of
7746      *  {@link CarExtender}.
7747      *  <li>Call {@link Notification.Builder#extend(Notification.Extender)}
7748      *  to apply the extensions to a notification.
7749      * </ol>
7750      *
7751      * <pre class="prettyprint">
7752      * Notification notification = new Notification.Builder(context)
7753      *         ...
7754      *         .extend(new CarExtender()
7755      *                 .set*(...))
7756      *         .build();
7757      * </pre>
7758      *
7759      * <p>Car extensions can be accessed on an existing notification by using the
7760      * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
7761      * to access values.
7762      */
7763     public static final class CarExtender implements Extender {
7764         private static final String TAG = "CarExtender";
7765
7766         private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
7767         private static final String EXTRA_LARGE_ICON = "large_icon";
7768         private static final String EXTRA_CONVERSATION = "car_conversation";
7769         private static final String EXTRA_COLOR = "app_color";
7770
7771         private Bitmap mLargeIcon;
7772         private UnreadConversation mUnreadConversation;
7773         private int mColor = Notification.COLOR_DEFAULT;
7774
7775         /**
7776          * Create a {@link CarExtender} with default options.
7777          */
7778         public CarExtender() {
7779         }
7780
7781         /**
7782          * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
7783          *
7784          * @param notif The notification from which to copy options.
7785          */
7786         public CarExtender(Notification notif) {
7787             Bundle carBundle = notif.extras == null ?
7788                     null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
7789             if (carBundle != null) {
7790                 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
7791                 mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
7792
7793                 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
7794                 mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
7795             }
7796         }
7797
7798         /**
7799          * Apply car extensions to a notification that is being built. This is typically called by
7800          * the {@link Notification.Builder#extend(Notification.Extender)}
7801          * method of {@link Notification.Builder}.
7802          */
7803         @Override
7804         public Notification.Builder extend(Notification.Builder builder) {
7805             Bundle carExtensions = new Bundle();
7806
7807             if (mLargeIcon != null) {
7808                 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
7809             }
7810             if (mColor != Notification.COLOR_DEFAULT) {
7811                 carExtensions.putInt(EXTRA_COLOR, mColor);
7812             }
7813
7814             if (mUnreadConversation != null) {
7815                 Bundle b = mUnreadConversation.getBundleForUnreadConversation();
7816                 carExtensions.putBundle(EXTRA_CONVERSATION, b);
7817             }
7818
7819             builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
7820             return builder;
7821         }
7822
7823         /**
7824          * Sets the accent color to use when Android Auto presents the notification.
7825          *
7826          * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
7827          * to accent the displayed notification. However, not all colors are acceptable in an
7828          * automotive setting. This method can be used to override the color provided in the
7829          * notification in such a situation.
7830          */
7831         public CarExtender setColor(@ColorInt int color) {
7832             mColor = color;
7833             return this;
7834         }
7835
7836         /**
7837          * Gets the accent color.
7838          *
7839          * @see #setColor
7840          */
7841         @ColorInt
7842         public int getColor() {
7843             return mColor;
7844         }
7845
7846         /**
7847          * Sets the large icon of the car notification.
7848          *
7849          * If no large icon is set in the extender, Android Auto will display the icon
7850          * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
7851          *
7852          * @param largeIcon The large icon to use in the car notification.
7853          * @return This object for method chaining.
7854          */
7855         public CarExtender setLargeIcon(Bitmap largeIcon) {
7856             mLargeIcon = largeIcon;
7857             return this;
7858         }
7859
7860         /**
7861          * Gets the large icon used in this car notification, or null if no icon has been set.
7862          *
7863          * @return The large icon for the car notification.
7864          * @see CarExtender#setLargeIcon
7865          */
7866         public Bitmap getLargeIcon() {
7867             return mLargeIcon;
7868         }
7869
7870         /**
7871          * Sets the unread conversation in a message notification.
7872          *
7873          * @param unreadConversation The unread part of the conversation this notification conveys.
7874          * @return This object for method chaining.
7875          */
7876         public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
7877             mUnreadConversation = unreadConversation;
7878             return this;
7879         }
7880
7881         /**
7882          * Returns the unread conversation conveyed by this notification.
7883          * @see #setUnreadConversation(UnreadConversation)
7884          */
7885         public UnreadConversation getUnreadConversation() {
7886             return mUnreadConversation;
7887         }
7888
7889         /**
7890          * A class which holds the unread messages from a conversation.
7891          */
7892         public static class UnreadConversation {
7893             private static final String KEY_AUTHOR = "author";
7894             private static final String KEY_TEXT = "text";
7895             private static final String KEY_MESSAGES = "messages";
7896             private static final String KEY_REMOTE_INPUT = "remote_input";
7897             private static final String KEY_ON_REPLY = "on_reply";
7898             private static final String KEY_ON_READ = "on_read";
7899             private static final String KEY_PARTICIPANTS = "participants";
7900             private static final String KEY_TIMESTAMP = "timestamp";
7901
7902             private final String[] mMessages;
7903             private final RemoteInput mRemoteInput;
7904             private final PendingIntent mReplyPendingIntent;
7905             private final PendingIntent mReadPendingIntent;
7906             private final String[] mParticipants;
7907             private final long mLatestTimestamp;
7908
7909             UnreadConversation(String[] messages, RemoteInput remoteInput,
7910                     PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
7911                     String[] participants, long latestTimestamp) {
7912                 mMessages = messages;
7913                 mRemoteInput = remoteInput;
7914                 mReadPendingIntent = readPendingIntent;
7915                 mReplyPendingIntent = replyPendingIntent;
7916                 mParticipants = participants;
7917                 mLatestTimestamp = latestTimestamp;
7918             }
7919
7920             /**
7921              * Gets the list of messages conveyed by this notification.
7922              */
7923             public String[] getMessages() {
7924                 return mMessages;
7925             }
7926
7927             /**
7928              * Gets the remote input that will be used to convey the response to a message list, or
7929              * null if no such remote input exists.
7930              */
7931             public RemoteInput getRemoteInput() {
7932                 return mRemoteInput;
7933             }
7934
7935             /**
7936              * Gets the pending intent that will be triggered when the user replies to this
7937              * notification.
7938              */
7939             public PendingIntent getReplyPendingIntent() {
7940                 return mReplyPendingIntent;
7941             }
7942
7943             /**
7944              * Gets the pending intent that Android Auto will send after it reads aloud all messages
7945              * in this object's message list.
7946              */
7947             public PendingIntent getReadPendingIntent() {
7948                 return mReadPendingIntent;
7949             }
7950
7951             /**
7952              * Gets the participants in the conversation.
7953              */
7954             public String[] getParticipants() {
7955                 return mParticipants;
7956             }
7957
7958             /**
7959              * Gets the firs participant in the conversation.
7960              */
7961             public String getParticipant() {
7962                 return mParticipants.length > 0 ? mParticipants[0] : null;
7963             }
7964
7965             /**
7966              * Gets the timestamp of the conversation.
7967              */
7968             public long getLatestTimestamp() {
7969                 return mLatestTimestamp;
7970             }
7971
7972             Bundle getBundleForUnreadConversation() {
7973                 Bundle b = new Bundle();
7974                 String author = null;
7975                 if (mParticipants != null && mParticipants.length > 1) {
7976                     author = mParticipants[0];
7977                 }
7978                 Parcelable[] messages = new Parcelable[mMessages.length];
7979                 for (int i = 0; i < messages.length; i++) {
7980                     Bundle m = new Bundle();
7981                     m.putString(KEY_TEXT, mMessages[i]);
7982                     m.putString(KEY_AUTHOR, author);
7983                     messages[i] = m;
7984                 }
7985                 b.putParcelableArray(KEY_MESSAGES, messages);
7986                 if (mRemoteInput != null) {
7987                     b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
7988                 }
7989                 b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
7990                 b.putParcelable(KEY_ON_READ, mReadPendingIntent);
7991                 b.putStringArray(KEY_PARTICIPANTS, mParticipants);
7992                 b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
7993                 return b;
7994             }
7995
7996             static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
7997                 if (b == null) {
7998                     return null;
7999                 }
8000                 Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
8001                 String[] messages = null;
8002                 if (parcelableMessages != null) {
8003                     String[] tmp = new String[parcelableMessages.length];
8004                     boolean success = true;
8005                     for (int i = 0; i < tmp.length; i++) {
8006                         if (!(parcelableMessages[i] instanceof Bundle)) {
8007                             success = false;
8008                             break;
8009                         }
8010                         tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
8011                         if (tmp[i] == null) {
8012                             success = false;
8013                             break;
8014                         }
8015                     }
8016                     if (success) {
8017                         messages = tmp;
8018                     } else {
8019                         return null;
8020                     }
8021                 }
8022
8023                 PendingIntent onRead = b.getParcelable(KEY_ON_READ);
8024                 PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
8025
8026                 RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
8027
8028                 String[] participants = b.getStringArray(KEY_PARTICIPANTS);
8029                 if (participants == null || participants.length != 1) {
8030                     return null;
8031                 }
8032
8033                 return new UnreadConversation(messages,
8034                         remoteInput,
8035                         onReply,
8036                         onRead,
8037                         participants, b.getLong(KEY_TIMESTAMP));
8038             }
8039         };
8040
8041         /**
8042          * Builder class for {@link CarExtender.UnreadConversation} objects.
8043          */
8044         public static class Builder {
8045             private final List<String> mMessages = new ArrayList<String>();
8046             private final String mParticipant;
8047             private RemoteInput mRemoteInput;
8048             private PendingIntent mReadPendingIntent;
8049             private PendingIntent mReplyPendingIntent;
8050             private long mLatestTimestamp;
8051
8052             /**
8053              * Constructs a new builder for {@link CarExtender.UnreadConversation}.
8054              *
8055              * @param name The name of the other participant in the conversation.
8056              */
8057             public Builder(String name) {
8058                 mParticipant = name;
8059             }
8060
8061             /**
8062              * Appends a new unread message to the list of messages for this conversation.
8063              *
8064              * The messages should be added from oldest to newest.
8065              *
8066              * @param message The text of the new unread message.
8067              * @return This object for method chaining.
8068              */
8069             public Builder addMessage(String message) {
8070                 mMessages.add(message);
8071                 return this;
8072             }
8073
8074             /**
8075              * Sets the pending intent and remote input which will convey the reply to this
8076              * notification.
8077              *
8078              * @param pendingIntent The pending intent which will be triggered on a reply.
8079              * @param remoteInput The remote input parcelable which will carry the reply.
8080              * @return This object for method chaining.
8081              *
8082              * @see CarExtender.UnreadConversation#getRemoteInput
8083              * @see CarExtender.UnreadConversation#getReplyPendingIntent
8084              */
8085             public Builder setReplyAction(
8086                     PendingIntent pendingIntent, RemoteInput remoteInput) {
8087                 mRemoteInput = remoteInput;
8088                 mReplyPendingIntent = pendingIntent;
8089
8090                 return this;
8091             }
8092
8093             /**
8094              * Sets the pending intent that will be sent once the messages in this notification
8095              * are read.
8096              *
8097              * @param pendingIntent The pending intent to use.
8098              * @return This object for method chaining.
8099              */
8100             public Builder setReadPendingIntent(PendingIntent pendingIntent) {
8101                 mReadPendingIntent = pendingIntent;
8102                 return this;
8103             }
8104
8105             /**
8106              * Sets the timestamp of the most recent message in an unread conversation.
8107              *
8108              * If a messaging notification has been posted by your application and has not
8109              * yet been cancelled, posting a later notification with the same id and tag
8110              * but without a newer timestamp may result in Android Auto not displaying a
8111              * heads up notification for the later notification.
8112              *
8113              * @param timestamp The timestamp of the most recent message in the conversation.
8114              * @return This object for method chaining.
8115              */
8116             public Builder setLatestTimestamp(long timestamp) {
8117                 mLatestTimestamp = timestamp;
8118                 return this;
8119             }
8120
8121             /**
8122              * Builds a new unread conversation object.
8123              *
8124              * @return The new unread conversation object.
8125              */
8126             public UnreadConversation build() {
8127                 String[] messages = mMessages.toArray(new String[mMessages.size()]);
8128                 String[] participants = { mParticipant };
8129                 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
8130                         mReadPendingIntent, participants, mLatestTimestamp);
8131             }
8132         }
8133     }
8134
8135     /**
8136      * <p>Helper class to add Android TV extensions to notifications. To create a notification
8137      * with a TV extension:
8138      *
8139      * <ol>
8140      *  <li>Create an {@link Notification.Builder}, setting any desired properties.
8141      *  <li>Create a {@link TvExtender}.
8142      *  <li>Set TV-specific properties using the {@code set} methods of
8143      *  {@link TvExtender}.
8144      *  <li>Call {@link Notification.Builder#extend(Notification.Extender)}
8145      *  to apply the extension to a notification.
8146      * </ol>
8147      *
8148      * <pre class="prettyprint">
8149      * Notification notification = new Notification.Builder(context)
8150      *         ...
8151      *         .extend(new TvExtender()
8152      *                 .set*(...))
8153      *         .build();
8154      * </pre>
8155      *
8156      * <p>TV extensions can be accessed on an existing notification by using the
8157      * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
8158      * to access values.
8159      *
8160      * @hide
8161      */
8162     @SystemApi
8163     public static final class TvExtender implements Extender {
8164         private static final String TAG = "TvExtender";
8165
8166         private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
8167         private static final String EXTRA_FLAGS = "flags";
8168         private static final String EXTRA_CONTENT_INTENT = "content_intent";
8169         private static final String EXTRA_DELETE_INTENT = "delete_intent";
8170         private static final String EXTRA_CHANNEL_ID = "channel_id";
8171
8172         // Flags bitwise-ored to mFlags
8173         private static final int FLAG_AVAILABLE_ON_TV = 0x1;
8174
8175         private int mFlags;
8176         private String mChannelId;
8177         private PendingIntent mContentIntent;
8178         private PendingIntent mDeleteIntent;
8179
8180         /**
8181          * Create a {@link TvExtender} with default options.
8182          */
8183         public TvExtender() {
8184             mFlags = FLAG_AVAILABLE_ON_TV;
8185         }
8186
8187         /**
8188          * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
8189          *
8190          * @param notif The notification from which to copy options.
8191          */
8192         public TvExtender(Notification notif) {
8193             Bundle bundle = notif.extras == null ?
8194                 null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
8195             if (bundle != null) {
8196                 mFlags = bundle.getInt(EXTRA_FLAGS);
8197                 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
8198                 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
8199                 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
8200             }
8201         }
8202
8203         /**
8204          * Apply a TV extension to a notification that is being built. This is typically called by
8205          * the {@link Notification.Builder#extend(Notification.Extender)}
8206          * method of {@link Notification.Builder}.
8207          */
8208         @Override
8209         public Notification.Builder extend(Notification.Builder builder) {
8210             Bundle bundle = new Bundle();
8211
8212             bundle.putInt(EXTRA_FLAGS, mFlags);
8213             bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
8214             if (mContentIntent != null) {
8215                 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
8216             }
8217
8218             if (mDeleteIntent != null) {
8219                 bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
8220             }
8221
8222             builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
8223             return builder;
8224         }
8225
8226         /**
8227          * Returns true if this notification should be shown on TV. This method return true
8228          * if the notification was extended with a TvExtender.
8229          */
8230         public boolean isAvailableOnTv() {
8231             return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
8232         }
8233
8234         /**
8235          * Specifies the channel the notification should be delivered on when shown on TV.
8236          * It can be different from the channel that the notification is delivered to when
8237          * posting on a non-TV device.
8238          */
8239         public TvExtender setChannel(String channelId) {
8240             mChannelId = channelId;
8241             return this;
8242         }
8243
8244         /**
8245          * Specifies the channel the notification should be delivered on when shown on TV.
8246          * It can be different from the channel that the notification is delivered to when
8247          * posting on a non-TV device.
8248          */
8249         public TvExtender setChannelId(String channelId) {
8250             mChannelId = channelId;
8251             return this;
8252         }
8253
8254         /** @removed */
8255         @Deprecated
8256         public String getChannel() {
8257             return mChannelId;
8258         }
8259
8260         /**
8261          * Returns the id of the channel this notification posts to on TV.
8262          */
8263         public String getChannelId() {
8264             return mChannelId;
8265         }
8266
8267         /**
8268          * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
8269          * If provided, it is used instead of the content intent specified
8270          * at the level of Notification.
8271          */
8272         public TvExtender setContentIntent(PendingIntent intent) {
8273             mContentIntent = intent;
8274             return this;
8275         }
8276
8277         /**
8278          * Returns the TV-specific content intent.  If this method returns null, the
8279          * main content intent on the notification should be used.
8280          *
8281          * @see {@link Notification#contentIntent}
8282          */
8283         public PendingIntent getContentIntent() {
8284             return mContentIntent;
8285         }
8286
8287         /**
8288          * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
8289          * by the user on TV.  If provided, it is used instead of the delete intent specified
8290          * at the level of Notification.
8291          */
8292         public TvExtender setDeleteIntent(PendingIntent intent) {
8293             mDeleteIntent = intent;
8294             return this;
8295         }
8296
8297         /**
8298          * Returns the TV-specific delete intent.  If this method returns null, the
8299          * main delete intent on the notification should be used.
8300          *
8301          * @see {@link Notification#deleteIntent}
8302          */
8303         public PendingIntent getDeleteIntent() {
8304             return mDeleteIntent;
8305         }
8306     }
8307
8308     /**
8309      * Get an array of Notification objects from a parcelable array bundle field.
8310      * Update the bundle to have a typed array so fetches in the future don't need
8311      * to do an array copy.
8312      */
8313     private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
8314         Parcelable[] array = bundle.getParcelableArray(key);
8315         if (array instanceof Notification[] || array == null) {
8316             return (Notification[]) array;
8317         }
8318         Notification[] typedArray = Arrays.copyOf(array, array.length,
8319                 Notification[].class);
8320         bundle.putParcelableArray(key, typedArray);
8321         return typedArray;
8322     }
8323
8324     private static class BuilderRemoteViews extends RemoteViews {
8325         public BuilderRemoteViews(Parcel parcel) {
8326             super(parcel);
8327         }
8328
8329         public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
8330             super(appInfo, layoutId);
8331         }
8332
8333         @Override
8334         public BuilderRemoteViews clone() {
8335             Parcel p = Parcel.obtain();
8336             writeToParcel(p, 0);
8337             p.setDataPosition(0);
8338             BuilderRemoteViews brv = new BuilderRemoteViews(p);
8339             p.recycle();
8340             return brv;
8341         }
8342     }
8343
8344     private static class StandardTemplateParams {
8345         boolean hasProgress = true;
8346         boolean ambient = false;
8347         CharSequence title;
8348         CharSequence text;
8349
8350         final StandardTemplateParams reset() {
8351             hasProgress = true;
8352             ambient = false;
8353             title = null;
8354             text = null;
8355             return this;
8356         }
8357
8358         final StandardTemplateParams hasProgress(boolean hasProgress) {
8359             this.hasProgress = hasProgress;
8360             return this;
8361         }
8362
8363         final StandardTemplateParams title(CharSequence title) {
8364             this.title = title;
8365             return this;
8366         }
8367
8368         final StandardTemplateParams text(CharSequence text) {
8369             this.text = text;
8370             return this;
8371         }
8372
8373         final StandardTemplateParams ambient(boolean ambient) {
8374             Preconditions.checkState(title == null && text == null, "must set ambient before text");
8375             this.ambient = ambient;
8376             return this;
8377         }
8378
8379         final StandardTemplateParams fillTextsFrom(Builder b) {
8380             Bundle extras = b.mN.extras;
8381             title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
8382             text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
8383             return this;
8384         }
8385     }
8386 }