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