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