/*

* Copyright (C) 2007 The Android Open Source Project

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package android . app ;

import android . annotation . ColorInt ;

import android . annotation . DrawableRes ;

import android . annotation . IntDef ;

import android . annotation . SdkConstant ;

import android . annotation . SdkConstant . SdkConstantType ;

import android . annotation . SystemApi ;

import android . content . Context ;

import android . content . Intent ;

import android . content . pm . ApplicationInfo ;

import android . content . pm . PackageManager ;

import android . content . pm . PackageManager . NameNotFoundException ;

import android . content . res . ColorStateList ;

import android . graphics . Bitmap ;

import android . graphics . Canvas ;

import android . graphics . PorterDuff ;

import android . graphics . drawable . Drawable ;

import android . graphics . drawable . Icon ;

import android . media . AudioAttributes ;

import android . media . AudioManager ;

import android . media . session . MediaSession ;

import android . net . Uri ;

import android . os . BadParcelableException ;

import android . os . Build ;

import android . os . Bundle ;

import android . os . Parcel ;

import android . os . Parcelable ;

import android . os . SystemClock ;

import android . os . UserHandle ;

import android . text . TextUtils ;

import android . util . Log ;

import android . util . SparseArray ;

import android . util . TypedValue ;

import android . view . Gravity ;

import android . view . NotificationHeaderView ;

import android . view . View ;

import android . widget . ProgressBar ;

import android . widget . RemoteViews ;

import com . android . internal . R ;

import com . android . internal . util . NotificationColorUtil ;

import java . lang . annotation . Retention ;

import java . lang . annotation . RetentionPolicy ;

import java . lang . reflect . Constructor ;

import java . util . ArrayList ;

import java . util . Arrays ;

import java . util . Collections ;

import java . util . List ;

import java . util . Objects ;

import java . util . Set ;

/**

* A class that represents how a persistent notification is to be presented to

* the user using the {@link android.app.NotificationManager}.

*

* <p>The {@link Notification.Builder Notification.Builder} has been added to make it

* easier to construct Notifications.</p>

*

* <div class="special reference">

* <h3>Developer Guides</h3>

* <p>For a guide to creating notifications, read the

* <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>

* developer guide.</p>

* </div>

*/

public class Notification implements Parcelable

{

private static final String TAG = "Notification" ;

/**

* An activity that provides a user interface for adjusting notification preferences for its

* containing application. Optional but recommended for apps that post

* {@link android.app.Notification Notifications}.

*/

@SdkConstant ( SdkConstantType . INTENT_CATEGORY )

public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES

= "android.intent.category.NOTIFICATION_PREFERENCES" ;

/**

* Use all default values (where applicable).

*/

public static final int DEFAULT_ALL = ~ 0 ;

/**

* Use the default notification sound. This will ignore any given

* {@link #sound}.

*

* <p>

* A notification that is noisy is more likely to be presented as a heads-up notification.

* </p>

*

* @see #defaults

*/

public static final int DEFAULT_SOUND = 1 ;

/**

* Use the default notification vibrate. This will ignore any given

* {@link #vibrate}. Using phone vibration requires the

* {@link android.Manifest.permission#VIBRATE VIBRATE} permission.

*

* <p>

* A notification that vibrates is more likely to be presented as a heads-up notification.

* </p>

*

* @see #defaults

*/

public static final int DEFAULT_VIBRATE = 2 ;

/**

* Use the default notification lights. This will ignore the

* {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or

* {@link #ledOnMS}.

*

* @see #defaults

*/

public static final int DEFAULT_LIGHTS = 4 ;

/**

* Maximum length of CharSequences accepted by Builder and friends.

*

* <p>

* Avoids spamming the system with overly large strings such as full e-mails.

*/

private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024 ;

/**

* A timestamp related to this notification, in milliseconds since the epoch.

*

* Default value: {@link System#currentTimeMillis() Now}.

*

* Choose a timestamp that will be most relevant to the user. For most finite events, this

* corresponds to the time the event happened (or will happen, in the case of events that have

* yet to occur but about which the user is being informed). Indefinite events should be

* timestamped according to when the activity began.

*

* Some examples:

*

* <ul>

* <li>Notification of a new chat message should be stamped when the message was received.</li>

* <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>

* <li>Notification of a completed file download should be stamped when the download finished.</li>

* <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>

* <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.

* <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.

* </ul>

*

*/

public long when ;

/**

* The resource id of a drawable to use as the icon in the status bar.

*

* @deprecated Use {@link Builder#setSmallIcon(Icon)} instead.

*/

@Deprecated

@DrawableRes

public int icon ;

/**

* If the icon in the status bar is to have more than one level, you can set this. Otherwise,

* leave it at its default value of 0.

*

* @see android.widget.ImageView#setImageLevel

* @see android.graphics.drawable.Drawable#setLevel

*/

public int iconLevel ;

/**

* The number of events that this notification represents. For example, in a new mail

* notification, this could be the number of unread messages.

*

* The system may or may not use this field to modify the appearance of the notification. For

* example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was

* superimposed over the icon in the status bar. Starting with

* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by

* {@link Notification.Builder} has displayed the number in the expanded notification view.

*

* If the number is 0 or negative, it is never shown.

*/

public int number ;

/**

* The intent to execute when the expanded status entry is clicked. If

* this is an activity, it must include the

* {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires

* that you take care of task management as described in the

* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back

* Stack</a> document. In particular, make sure to read the notification section

* <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling

* Notifications</a> for the correct ways to launch an application from a

* notification.

*/

public PendingIntent contentIntent ;

/**

* The intent to execute when the notification is explicitly dismissed by the user, either with

* the "Clear All" button or by swiping it away individually.

*

* This probably shouldn't be launching an activity since several of those will be sent

* at the same time.

*/

public PendingIntent deleteIntent ;

/**

* An intent to launch instead of posting the notification to the status bar.

*

* <p>

* The system UI may choose to display a heads-up notification, instead of

* launching this intent, while the user is using the device.

* </p>

*

* @see Notification.Builder#setFullScreenIntent

*/

public PendingIntent fullScreenIntent ;

/**

* Text that summarizes this notification for accessibility services.

*

* As of the L release, this text is no longer shown on screen, but it is still useful to

* accessibility services (where it serves as an audible announcement of the notification's

* appearance).

*

* @see #tickerView

*/

public CharSequence tickerText ;

/**

* Formerly, a view showing the {@link #tickerText}.

*

* No longer displayed in the status bar as of API 21.

*/

@Deprecated

public RemoteViews tickerView ;

/**

* The view that will represent this notification in the notification list (which is pulled

* down from the status bar).

*

* As of N, this field is not used. The notification view is determined by the inputs to

* {@link Notification.Builder}; a custom RemoteViews can optionally be

* supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.

*/

@Deprecated

public RemoteViews contentView ;

/**

* A large-format version of {@link #contentView}, giving the Notification an

* opportunity to show more detail. The system UI may choose to show this

* instead of the normal content view at its discretion.

*

* As of N, this field is not used. The expanded notification view is determined by the

* inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be

* supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.

*/

@Deprecated

public RemoteViews bigContentView ;

/**

* A medium-format version of {@link #contentView}, providing the Notification an

* opportunity to add action buttons to contentView. At its discretion, the system UI may

* choose to show this as a heads-up notification, which will pop up so the user can see

* it without leaving their current activity.

*

* As of N, this field is not used. The heads-up notification view is determined by the

* inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be

* supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.

*/

@Deprecated

public RemoteViews headsUpContentView ;

/**

* A large bitmap to be shown in the notification content area.

*

* @deprecated Use {@link Builder#setLargeIcon(Icon)} instead.

*/

@Deprecated

public Bitmap largeIcon ;

/**

* The sound to play.

*

* <p>

* A notification that is noisy is more likely to be presented as a heads-up notification.

* </p>

*

* <p>

* To play the default notification sound, see {@link #defaults}.

* </p>

*/

public Uri sound ;

/**

* Use this constant as the value for audioStreamType to request that

* the default stream type for notifications be used. Currently the

* default stream type is {@link AudioManager#STREAM_NOTIFICATION}.

*

* @deprecated Use {@link #audioAttributes} instead.

*/

@Deprecated

public static final int STREAM_DEFAULT = - 1 ;

/**

* The audio stream type to use when playing the sound.

* Should be one of the STREAM_ constants from

* {@link android.media.AudioManager}.

*

* @deprecated Use {@link #audioAttributes} instead.

*/

@Deprecated

public int audioStreamType = STREAM_DEFAULT ;

/**

* The default value of {@link #audioAttributes}.

*/

public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes . Builder ()

. setContentType ( AudioAttributes . CONTENT_TYPE_SONIFICATION )

. setUsage ( AudioAttributes . USAGE_NOTIFICATION )

. build ();

/**

* The {@link AudioAttributes audio attributes} to use when playing the sound.

*/

public AudioAttributes audioAttributes = AUDIO_ATTRIBUTES_DEFAULT ;

/**

* The pattern with which to vibrate.

*

* <p>

* To vibrate the default pattern, see {@link #defaults}.

* </p>

*

* <p>

* A notification that vibrates is more likely to be presented as a heads-up notification.

* </p>

*

* @see android.os.Vibrator#vibrate(long[],int)

*/

public long [] vibrate ;

/**

* The color of the led. The hardware will do its best approximation.

*

* @see #FLAG_SHOW_LIGHTS

* @see #flags

*/

@ColorInt

public int ledARGB ;

/**

* The number of milliseconds for the LED to be on while it's flashing.

* The hardware will do its best approximation.

*

* @see #FLAG_SHOW_LIGHTS

* @see #flags

*/

public int ledOnMS ;

/**

* The number of milliseconds for the LED to be off while it's flashing.

* The hardware will do its best approximation.

*

* @see #FLAG_SHOW_LIGHTS

* @see #flags

*/

public int ledOffMS ;

/**

* Specifies which values should be taken from the defaults.

* <p>

* To set, OR the desired from {@link #DEFAULT_SOUND},

* {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default

* values, use {@link #DEFAULT_ALL}.

* </p>

*/

public int defaults ;

/**

* Bit to be bitwise-ored into the {@link #flags} field that should be

* set if you want the LED on for this notification.

* <ul>

* <li>To turn the LED off, pass 0 in the alpha channel for colorARGB

* or 0 for both ledOnMS and ledOffMS.</li>

* <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>

* <li>To flash the LED, pass the number of milliseconds that it should

* be on and off to ledOnMS and ledOffMS.</li>

* </ul>

* <p>

* Since hardware varies, you are not guaranteed that any of the values

* you pass are honored exactly. Use the system defaults (TODO) if possible

* because they will be set to values that work on any given hardware.

* <p>

* The alpha channel must be set for forward compatibility.

*

*/

public static final int FLAG_SHOW_LIGHTS = 0x00000001 ;

/**

* Bit to be bitwise-ored into the {@link #flags} field that should be

* set if this notification is in reference to something that is ongoing,

* like a phone call. It should not be set if this notification is in

* reference to something that happened at a particular point in time,

* like a missed phone call.

*/

public static final int FLAG_ONGOING_EVENT = 0x00000002 ;

/**

* Bit to be bitwise-ored into the {@link #flags} field that if set,

* the audio will be repeated until the notification is

* cancelled or the notification window is opened.

*/

public static final int FLAG_INSISTENT = 0x00000004 ;

/**

* Bit to be bitwise-ored into the {@link #flags} field that should be

* set if you would only like the sound, vibrate and ticker to be played

* if the notification was not already showing.

*/

public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008 ;

/**

* Bit to be bitwise-ored into the {@link #flags} field that should be

* set if the notification should be canceled when it is clicked by the

* user.

*/

public static final int FLAG_AUTO_CANCEL = 0x00000010 ;

/**

* Bit to be bitwise-ored into the {@link #flags} field that should be

* set if the notification should not be canceled when the user clicks

* the Clear all button.

*/

public static final int FLAG_NO_CLEAR = 0x00000020 ;

/**

* Bit to be bitwise-ored into the {@link #flags} field that should be

* set if this notification represents a currently running service. This

* will normally be set for you by {@link Service#startForeground}.

*/

public static final int FLAG_FOREGROUND_SERVICE = 0x00000040 ;

/**

* Obsolete flag indicating high-priority notifications; use the priority field instead.

*

* @deprecated Use {@link #priority} with a positive value.

*/

public static final int FLAG_HIGH_PRIORITY = 0x00000080 ;

/**

* Bit to be bitswise-ored into the {@link #flags} field that should be

* set if this notification is relevant to the current device only

* and it is not recommended that it bridge to other devices.

*/

public static final int FLAG_LOCAL_ONLY = 0x00000100 ;

/**

* Bit to be bitswise-ored into the {@link #flags} field that should be

* set if this notification is the group summary for a group of notifications.

* Grouped notifications may display in a cluster or stack on devices which

* support such rendering. Requires a group key also be set using {@link Builder#setGroup}.

*/

public static final int FLAG_GROUP_SUMMARY = 0x00000200 ;

public int flags ;

/** @hide */

@IntDef ({ PRIORITY_DEFAULT , PRIORITY_LOW , PRIORITY_MIN , PRIORITY_HIGH , PRIORITY_MAX })

@Retention ( RetentionPolicy . SOURCE )

public @interface Priority {}

/**

* Default notification {@link #priority}. If your application does not prioritize its own

* notifications, use this value for all notifications.

*/

public static final int PRIORITY_DEFAULT = 0 ;

/**

* Lower {@link #priority}, for items that are less important. The UI may choose to show these

* items smaller, or at a different position in the list, compared with your app's

* {@link #PRIORITY_DEFAULT} items.

*/

public static final int PRIORITY_LOW = - 1 ;

/**

* Lowest {@link #priority}; these items might not be shown to the user except under special

* circumstances, such as detailed notification logs.

*/

public static final int PRIORITY_MIN = - 2 ;

/**

* Higher {@link #priority}, for more important notifications or alerts. The UI may choose to

* show these items larger, or at a different position in notification lists, compared with

* your app's {@link #PRIORITY_DEFAULT} items.

*/

public static final int PRIORITY_HIGH = 1 ;

/**

* Highest {@link #priority}, for your application's most important items that require the

* user's prompt attention or input.

*/

public static final int PRIORITY_MAX = 2 ;

/**

* Relative priority for this notification.

*

* Priority is an indication of how much of the user's valuable attention should be consumed by

* this notification. Low-priority notifications may be hidden from the user in certain

* situations, while the user might be interrupted for a higher-priority notification. The

* system will make a determination about how to interpret this priority when presenting

* the notification.

*

* <p>

* A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented

* as a heads-up notification.

* </p>

*

*/

@Priority

public int priority ;

/**

* Accent color (an ARGB integer like the constants in {@link android.graphics.Color})

* to be applied by the standard Style templates when presenting this notification.

*

* The current template design constructs a colorful header image by overlaying the

* {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are

* ignored.

*/

@ColorInt

public int color = COLOR_DEFAULT ;

/**

* Special value of {@link #color} telling the system not to decorate this notification with

* any special color but instead use default colors when presenting this notification.

*/

@ColorInt

public static final int COLOR_DEFAULT = 0 ; // AKA Color.TRANSPARENT

/**

* Sphere of visibility of this notification, which affects how and when the SystemUI reveals

* the notification's presence and contents in untrusted situations (namely, on the secure

* lockscreen).

*

* The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always

* done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are

* shown in all situations, but the contents are only available if the device is unlocked for

* the appropriate user.

*

* A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification

* can be read even in an "insecure" context (that is, above a secure lockscreen).

* To modify the public version of this notification—for example, to redact some portions—see

* {@link Builder#setPublicVersion(Notification)}.

*

* Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon

* and ticker until the user has bypassed the lockscreen.

*/

public int visibility ;

/**

* Notification visibility: Show this notification in its entirety on all lockscreens.

*

* {@see #visibility}

*/

public static final int VISIBILITY_PUBLIC = 1 ;

/**

* Notification visibility: Show this notification on all lockscreens, but conceal sensitive or

* private information on secure lockscreens.

*

* {@see #visibility}

*/

public static final int VISIBILITY_PRIVATE = 0 ;

/**

* Notification visibility: Do not reveal any part of this notification on a secure lockscreen.

*

* {@see #visibility}

*/

public static final int VISIBILITY_SECRET = - 1 ;

/**

* Notification category: incoming call (voice or video) or similar synchronous communication request.

*/

public static final String CATEGORY_CALL = "call" ;

/**

* Notification category: incoming direct message (SMS, instant message, etc.).

*/

public static final String CATEGORY_MESSAGE = "msg" ;

/**

* Notification category: asynchronous bulk message (email).

*/

public static final String CATEGORY_EMAIL = "email" ;

/**

* Notification category: calendar event.

*/

public static final String CATEGORY_EVENT = "event" ;

/**

* Notification category: promotion or advertisement.

*/

public static final String CATEGORY_PROMO = "promo" ;

/**

* Notification category: alarm or timer.

*/

public static final String CATEGORY_ALARM = "alarm" ;

/**

* Notification category: progress of a long-running background operation.

*/

public static final String CATEGORY_PROGRESS = "progress" ;

/**

* Notification category: social network or sharing update.

*/

public static final String CATEGORY_SOCIAL = "social" ;

/**

* Notification category: error in background operation or authentication status.

*/

public static final String CATEGORY_ERROR = "err" ;

/**

* Notification category: media transport control for playback.

*/

public static final String CATEGORY_TRANSPORT = "transport" ;

/**

* Notification category: system or device status update. Reserved for system use.

*/

public static final String CATEGORY_SYSTEM = "sys" ;

/**

* Notification category: indication of running background service.

*/

public static final String CATEGORY_SERVICE = "service" ;

/**

* Notification category: a specific, timely recommendation for a single thing.

* For example, a news app might want to recommend a news story it believes the user will

* want to read next.

*/

public static final String CATEGORY_RECOMMENDATION = "recommendation" ;

/**

* Notification category: ongoing information about device or contextual status.

*/

public static final String CATEGORY_STATUS = "status" ;

/**

* Notification category: user-scheduled reminder.

*/

public static final String CATEGORY_REMINDER = "reminder" ;

/**

* One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)

* that best describes this Notification. May be used by the system for ranking and filtering.

*/

public String category ;

private String mGroupKey ;

/**

* Get the key used to group this notification into a cluster or stack

* with other notifications on devices which support such rendering.

*/

public String getGroup () {

return mGroupKey ;

}

private String mSortKey ;

/**

* Get a sort key that orders this notification among other notifications from the

* same package. This can be useful if an external sort was already applied and an app

* would like to preserve this. Notifications will be sorted lexicographically using this

* value, although providing different priorities in addition to providing sort key may

* cause this value to be ignored.

*

* <p>This sort key can also be used to order members of a notification group. See

* {@link Builder#setGroup}.

*

* @see String#compareTo(String)

*/

public String getSortKey () {

return mSortKey ;

}

/**

* Additional semantic data to be carried around with this Notification.

* <p>

* The extras keys defined here are intended to capture the original inputs to {@link Builder}

* APIs, and are intended to be used by

* {@link android.service.notification.NotificationListenerService} implementations to extract

* detailed information from notification objects.

*/

public Bundle extras = new Bundle ();

/**

* {@link #extras} key: this is the title of the notification,

* as supplied to {@link Builder#setContentTitle(CharSequence)}.

*/

public static final String EXTRA_TITLE = "android.title" ;

/**

* {@link #extras} key: this is the title of the notification when shown in expanded form,

* e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.

*/

public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big" ;

/**

* {@link #extras} key: this is the main text payload, as supplied to

* {@link Builder#setContentText(CharSequence)}.

*/

public static final String EXTRA_TEXT = "android.text" ;

/**

* {@link #extras} key: this is a third line of text, as supplied to

* {@link Builder#setSubText(CharSequence)}.

*/

public static final String EXTRA_SUB_TEXT = "android.subText" ;

/**

* {@link #extras} key: this is a small piece of additional text as supplied to

* {@link Builder#setContentInfo(CharSequence)}.

*/

public static final String EXTRA_INFO_TEXT = "android.infoText" ;

/**

* {@link #extras} key: this is a line of summary information intended to be shown

* alongside expanded notifications, as supplied to (e.g.)

* {@link BigTextStyle#setSummaryText(CharSequence)}.

*/

public static final String EXTRA_SUMMARY_TEXT = "android.summaryText" ;

/**

* {@link #extras} key: this is the longer text shown in the big form of a

* {@link BigTextStyle} notification, as supplied to

* {@link BigTextStyle#bigText(CharSequence)}.

*/

public static final String EXTRA_BIG_TEXT = "android.bigText" ;

/**

* {@link #extras} key: this is the resource ID of the notification's main small icon, as

* supplied to {@link Builder#setSmallIcon(int)}.

*/

public static final String EXTRA_SMALL_ICON = "android.icon" ;

/**

* {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the

* notification payload, as

* supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.

*/

public static final String EXTRA_LARGE_ICON = "android.largeIcon" ;

/**

* {@link #extras} key: this is a bitmap to be used instead of the one from

* {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is

* shown in its expanded form, as supplied to

* {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.

*/

public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big" ;

/**

* {@link #extras} key: this is the progress value supplied to

* {@link Builder#setProgress(int, int, boolean)}.

*/

public static final String EXTRA_PROGRESS = "android.progress" ;

/**

* {@link #extras} key: this is the maximum value supplied to

* {@link Builder#setProgress(int, int, boolean)}.

*/

public static final String EXTRA_PROGRESS_MAX = "android.progressMax" ;

/**

* {@link #extras} key: whether the progress bar is indeterminate, supplied to

* {@link Builder#setProgress(int, int, boolean)}.

*/

public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate" ;

/**

* {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically

* a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to

* {@link Builder#setUsesChronometer(boolean)}.

*/

public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer" ;

/**

* {@link #extras} key: whether {@link #when} should be shown,

* as supplied to {@link Builder#setShowWhen(boolean)}.

*/

public static final String EXTRA_SHOW_WHEN = "android.showWhen" ;

/**

* {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded

* notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.

*/

public static final String EXTRA_PICTURE = "android.picture" ;

/**

* {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded

* notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.

*/

public static final String EXTRA_TEXT_LINES = "android.textLines" ;

/**

* {@link #extras} key: A string representing the name of the specific

* {@link android.app.Notification.Style} used to create this notification.

*/

public static final String EXTRA_TEMPLATE = "android.template" ;

/**

* {@link #extras} key: A String array containing the people that this notification relates to,

* each of which was supplied to {@link Builder#addPerson(String)}.

*/

public static final String EXTRA_PEOPLE = "android.people" ;

/**

* Allow certain system-generated notifications to appear before the device is provisioned.

* Only available to notifications coming from the android package.

* @hide

*/

public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup" ;

/**

* {@link #extras} key: A

* {@link android.content.ContentUris content URI} pointing to an image that can be displayed

* in the background when the notification is selected. The URI must point to an image stream

* suitable for passing into

* {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)

* BitmapFactory.decodeStream}; all other content types will be ignored. The content provider

* URI used for this purpose must require no permissions to read the image data.

*/

public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri" ;

/**

* {@link #extras} key: A

* {@link android.media.session.MediaSession.Token} associated with a

* {@link android.app.Notification.MediaStyle} notification.

*/

public static final String EXTRA_MEDIA_SESSION = "android.mediaSession" ;

/**

* {@link #extras} key: the indices of actions to be shown in the compact view,

* as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.

*/

public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions" ;

/**

* {@link #extras} key: the user that built the notification.

*

* @hide

*/

public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId" ;

/**

* @hide

*/

public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo" ;

private Icon mSmallIcon ;

private Icon mLargeIcon ;

/**

* Structure to encapsulate a named action that can be shown as part of this notification.

* It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is

* selected by the user.

* <p>

* Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}

* or {@link Notification.Builder#addAction(Notification.Action)}

* to attach actions.

*/

public static class Action implements Parcelable {

private final Bundle mExtras ;

private Icon mIcon ;

private final RemoteInput [] mRemoteInputs ;

/**

* Small icon representing the action.

*

* @deprecated Use {@link Action#getIcon()} instead.

*/

@Deprecated

public int icon ;

/**

* Title of the action.

*/

public CharSequence title ;

/**

* Intent to send when the user invokes this action. May be null, in which case the action

* may be rendered in a disabled presentation by the system UI.

*/

public PendingIntent actionIntent ;

private Action ( Parcel in ) {

if ( in . readInt () != 0 ) {

mIcon = Icon . CREATOR . createFromParcel ( in );

if ( mIcon . getType () == Icon . TYPE_RESOURCE ) {

icon = mIcon . getResId ();

}

}

title = TextUtils . CHAR_SEQUENCE_CREATOR . createFromParcel ( in );

if ( in . readInt () == 1 ) {

actionIntent = PendingIntent . CREATOR . createFromParcel ( in );

}

mExtras = in . readBundle ();

mRemoteInputs = in . createTypedArray ( RemoteInput . CREATOR );

}

/**

* @deprecated Use {@link android.app.Notification.Action.Builder}.

*/

@Deprecated

public Action ( int icon , CharSequence title , PendingIntent intent ) {

this ( Icon . createWithResource ( "" , icon ), title , intent , new Bundle (), null );

}

private Action ( Icon icon , CharSequence title , PendingIntent intent , Bundle extras ,

RemoteInput [] remoteInputs ) {

this . mIcon = icon ;

if ( icon != null && icon . getType () == Icon . TYPE_RESOURCE ) {

this . icon = icon . getResId ();

}

this . title = title ;

this . actionIntent = intent ;

this . mExtras = extras != null ? extras : new Bundle ();

this . mRemoteInputs = remoteInputs ;

}

/**

* Return an icon representing the action.

*/

public Icon getIcon () {

if ( mIcon == null && icon != 0 ) {

// you snuck an icon in here without using the builder; let's try to keep it

mIcon = Icon . createWithResource ( "" , icon );

}

return mIcon ;

}

/**

* Get additional metadata carried around with this Action.

*/

public Bundle getExtras () {

return mExtras ;

}

/**

* Get the list of inputs to be collected from the user when this action is sent.

* May return null if no remote inputs were added.

*/

public RemoteInput [] getRemoteInputs () {

return mRemoteInputs ;

}

/**

* Builder class for {@link Action} objects.

*/

public static final class Builder {

private final Icon mIcon ;

private final CharSequence mTitle ;

private final PendingIntent mIntent ;

private final Bundle mExtras ;

private ArrayList < RemoteInput > mRemoteInputs ;

/**

* Construct a new builder for {@link Action} object.

* @param icon icon to show for this action

* @param title the title of the action

* @param intent the {@link PendingIntent} to fire when users trigger this action

*/

@Deprecated

public Builder ( int icon , CharSequence title , PendingIntent intent ) {

this ( Icon . createWithResource ( "" , icon ), title , intent , new Bundle (), null );

}

/**

* Construct a new builder for {@link Action} object.

* @param icon icon to show for this action

* @param title the title of the action

* @param intent the {@link PendingIntent} to fire when users trigger this action

*/

public Builder ( Icon icon , CharSequence title , PendingIntent intent ) {

this ( icon , title , intent , new Bundle (), null );

}

/**

* Construct a new builder for {@link Action} object using the fields from an

* {@link Action}.

* @param action the action to read fields from.

*/

public Builder ( Action action ) {

this ( action . getIcon (), action . title , action . actionIntent , new Bundle ( action . mExtras ),

action . getRemoteInputs ());

}

private Builder ( Icon icon , CharSequence title , PendingIntent intent , Bundle extras ,

RemoteInput [] remoteInputs ) {

mIcon = icon ;

mTitle = title ;

mIntent = intent ;

mExtras = extras ;

if ( remoteInputs != null ) {

mRemoteInputs = new ArrayList < RemoteInput >( remoteInputs . length );

Collections . addAll ( mRemoteInputs , remoteInputs );

}

}

/**

* Merge additional metadata into this builder.

*

* <p>Values within the Bundle will replace existing extras values in this Builder.

*

* @see Notification.Action#extras

*/

public Builder addExtras ( Bundle extras ) {

if ( extras != null ) {

mExtras . putAll ( extras );

}

return this ;

}

/**

* Get the metadata Bundle used by this Builder.

*

* <p>The returned Bundle is shared with this Builder.

*/

public Bundle getExtras () {

return mExtras ;

}

/**

* Add an input to be collected from the user when this action is sent.

* Response values can be retrieved from the fired intent by using the

* {@link RemoteInput#getResultsFromIntent} function.

* @param remoteInput a {@link RemoteInput} to add to the action

* @return this object for method chaining

*/

public Builder addRemoteInput ( RemoteInput remoteInput ) {

if ( mRemoteInputs == null ) {

mRemoteInputs = new ArrayList < RemoteInput >();

}

mRemoteInputs . add ( remoteInput );

return this ;

}

/**

* Apply an extender to this action builder. Extenders may be used to add

* metadata or change options on this builder.

*/

public Builder extend ( Extender extender ) {

extender . extend ( this );

return this ;

}

/**

* Combine all of the options that have been set and return a new {@link Action}

* object.

* @return the built action

*/

public Action build () {

RemoteInput [] remoteInputs = mRemoteInputs != null

? mRemoteInputs . toArray ( new RemoteInput [ mRemoteInputs . size ()]) : null ;

return new Action ( mIcon , mTitle , mIntent , mExtras , remoteInputs );

}

}

@Override

public Action clone () {

return new Action (

getIcon (),

title ,

actionIntent , // safe to alias

new Bundle ( mExtras ),

getRemoteInputs ());

}

@Override

public int describeContents () {

return 0 ;

}

@Override

public void writeToParcel ( Parcel out , int flags ) {

final Icon ic = getIcon ();

if ( ic != null ) {

out . writeInt ( 1 );

ic . writeToParcel ( out , 0 );

} else {

out . writeInt ( 0 );

}

TextUtils . writeToParcel ( title , out , flags );

if ( actionIntent != null ) {

out . writeInt ( 1 );

actionIntent . writeToParcel ( out , flags );

} else {

out . writeInt ( 0 );

}

out . writeBundle ( mExtras );

out . writeTypedArray ( mRemoteInputs , flags );

}

public static final Parcelable . Creator < Action > CREATOR =

new Parcelable . Creator < Action >() {

public Action createFromParcel ( Parcel in ) {

return new Action ( in );

}

public Action [] newArray ( int size ) {

return new Action [ size ];

}

};

/**

* Extender interface for use with {@link Builder#extend}. Extenders may be used to add

* metadata or change options on an action builder.

*/

public interface Extender {

/**

* Apply this extender to a notification action builder.

* @param builder the builder to be modified.

* @return the build object for chaining.

*/

public Builder extend ( Builder builder );

}

/**

* Wearable extender for notification actions. To add extensions to an action,

* create a new {@link android.app.Notification.Action.WearableExtender} object using

* the {@code WearableExtender()} constructor and apply it to a

* {@link android.app.Notification.Action.Builder} using

* {@link android.app.Notification.Action.Builder#extend}.

*

* <pre class="prettyprint">

* Notification.Action action = new Notification.Action.Builder(

* R.drawable.archive_all, "Archive all", actionIntent)

* .extend(new Notification.Action.WearableExtender()

* .setAvailableOffline(false))

* .build();</pre>

*/

public static final class WearableExtender implements Extender {

/** Notification action extra which contains wearable extensions */

private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS" ;

// Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.

private static final String KEY_FLAGS = "flags" ;

private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel" ;

private static final String KEY_CONFIRM_LABEL = "confirmLabel" ;

private static final String KEY_CANCEL_LABEL = "cancelLabel" ;

// Flags bitwise-ored to mFlags

private static final int FLAG_AVAILABLE_OFFLINE = 0x1 ;

// Default value for flags integer

private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE ;

private int mFlags = DEFAULT_FLAGS ;

private CharSequence mInProgressLabel ;

private CharSequence mConfirmLabel ;

private CharSequence mCancelLabel ;

/**

* Create a {@link android.app.Notification.Action.WearableExtender} with default

* options.

*/

public WearableExtender () {

}

/**

* Create a {@link android.app.Notification.Action.WearableExtender} by reading

* wearable options present in an existing notification action.

* @param action the notification action to inspect.

*/

public WearableExtender ( Action action ) {

Bundle wearableBundle = action . getExtras (). getBundle ( EXTRA_WEARABLE_EXTENSIONS );

if ( wearableBundle != null ) {

mFlags = wearableBundle . getInt ( KEY_FLAGS , DEFAULT_FLAGS );

mInProgressLabel = wearableBundle . getCharSequence ( KEY_IN_PROGRESS_LABEL );

mConfirmLabel = wearableBundle . getCharSequence ( KEY_CONFIRM_LABEL );

mCancelLabel = wearableBundle . getCharSequence ( KEY_CANCEL_LABEL );

}

}

/**

* Apply wearable extensions to a notification action that is being built. This is

* typically called by the {@link android.app.Notification.Action.Builder#extend}

* method of {@link android.app.Notification.Action.Builder}.

*/

@Override

public Action . Builder extend ( Action . Builder builder ) {

Bundle wearableBundle = new Bundle ();

if ( mFlags != DEFAULT_FLAGS ) {

wearableBundle . putInt ( KEY_FLAGS , mFlags );

}

if ( mInProgressLabel != null ) {

wearableBundle . putCharSequence ( KEY_IN_PROGRESS_LABEL , mInProgressLabel );

}

if ( mConfirmLabel != null ) {

wearableBundle . putCharSequence ( KEY_CONFIRM_LABEL , mConfirmLabel );

}

if ( mCancelLabel != null ) {

wearableBundle . putCharSequence ( KEY_CANCEL_LABEL , mCancelLabel );

}

builder . getExtras (). putBundle ( EXTRA_WEARABLE_EXTENSIONS , wearableBundle );

return builder ;

}

@Override

public WearableExtender clone () {

WearableExtender that = new WearableExtender ();

that . mFlags = this . mFlags ;

that . mInProgressLabel = this . mInProgressLabel ;

that . mConfirmLabel = this . mConfirmLabel ;

that . mCancelLabel = this . mCancelLabel ;

return that ;

}

/**

* Set whether this action is available when the wearable device is not connected to

* a companion device. The user can still trigger this action when the wearable device is

* offline, but a visual hint will indicate that the action may not be available.

* Defaults to true.

*/

public WearableExtender setAvailableOffline ( boolean availableOffline ) {

setFlag ( FLAG_AVAILABLE_OFFLINE , availableOffline );

return this ;

}

/**

* Get whether this action is available when the wearable device is not connected to

* a companion device. The user can still trigger this action when the wearable device is

* offline, but a visual hint will indicate that the action may not be available.

* Defaults to true.

*/

public boolean isAvailableOffline () {

return ( mFlags & FLAG_AVAILABLE_OFFLINE ) != 0 ;

}

private void setFlag ( int mask , boolean value ) {

if ( value ) {

mFlags |= mask ;

} else {

mFlags &= ~ mask ;

}

}

/**

* Set a label to display while the wearable is preparing to automatically execute the

* action. This is usually a 'ing' verb ending in ellipsis like "Sending..."

*

* @param label the label to display while the action is being prepared to execute

* @return this object for method chaining

*/

public WearableExtender setInProgressLabel ( CharSequence label ) {

mInProgressLabel = label ;

return this ;

}

/**

* Get the label to display while the wearable is preparing to automatically execute

* the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."

*

* @return the label to display while the action is being prepared to execute

*/

public CharSequence getInProgressLabel () {

return mInProgressLabel ;

}

/**

* Set a label to display to confirm that the action should be executed.

* This is usually an imperative verb like "Send".

*

* @param label the label to confirm the action should be executed

* @return this object for method chaining

*/

public WearableExtender setConfirmLabel ( CharSequence label ) {

mConfirmLabel = label ;

return this ;

}

/**

* Get the label to display to confirm that the action should be executed.

* This is usually an imperative verb like "Send".

*

* @return the label to confirm the action should be executed

*/

public CharSequence getConfirmLabel () {

return mConfirmLabel ;

}

/**

* Set a label to display to cancel the action.

* This is usually an imperative verb, like "Cancel".

*

* @param label the label to display to cancel the action

* @return this object for method chaining

*/

public WearableExtender setCancelLabel ( CharSequence label ) {

mCancelLabel = label ;

return this ;

}

/**

* Get the label to display to cancel the action.

* This is usually an imperative verb like "Cancel".

*

* @return the label to display to cancel the action

*/

public CharSequence getCancelLabel () {

return mCancelLabel ;

}

}

}

/**

* Array of all {@link Action} structures attached to this notification by

* {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of

* {@link android.service.notification.NotificationListenerService} that provide an alternative

* interface for invoking actions.

*/

public Action [] actions ;

/**

* Replacement version of this notification whose content will be shown

* in an insecure context such as atop a secure keyguard. See {@link #visibility}

* and {@link #VISIBILITY_PUBLIC}.

*/

public Notification publicVersion ;

/**

* Structure to encapsulate a topic that is shown in Notification settings.

* It must include an id and label.

*/

public static class Topic implements Parcelable {

private final String id ;

private final CharSequence label ;

public Topic ( String id , CharSequence label ) {

this . id = id ;

this . label = safeCharSequence ( label );

}

private Topic ( Parcel in ) {

if ( in . readInt () != 0 ) {

id = in . readString ();

} else {

id = null ;

}

label = TextUtils . CHAR_SEQUENCE_CREATOR . createFromParcel ( in );

}

public String getId () {

return id ;

}

public CharSequence getLabel () {

return label ;

}

@Override

public String toString () {

return new StringBuilder ( Topic . class . getSimpleName ()). append ( '[' )

. append ( "id=" ). append ( id )

. append ( ",label=" ). append ( label )

. append ( ']' ). toString ();

}

@Override

public boolean equals ( Object o ) {

if (!( o instanceof Topic )) return false ;

if ( o == this ) return true ;

final Topic other = ( Topic ) o ;

return Objects . equals ( other . id , id )

&& Objects . equals ( other . label , label );

}

@Override

public int hashCode () {

return Objects . hash ( id , label );

}

@Override

public Topic clone () {

return new Topic ( id , label );

}

@Override

public int describeContents () {

return 0 ;

}

@Override

public void writeToParcel ( Parcel out , int flags ) {

if ( id != null ) {

out . writeInt ( 1 );

out . writeString ( id );

} else {

out . writeInt ( 0 );

}

TextUtils . writeToParcel ( label , out , flags );

}

public static final Parcelable . Creator < Topic > CREATOR =

new Parcelable . Creator < Topic >() {

public Topic createFromParcel ( Parcel in ) {

return new Topic ( in );

}

public Topic [] newArray ( int size ) {

return new Topic [ size ];

}

};

}

@SystemApi

public static final String TOPIC_DEFAULT = "system_default_topic" ;

private Topic topic ;

public Topic getTopic () {

return topic ;

}

/**

* Constructs a Notification object with default values.

* You might want to consider using {@link Builder} instead.

*/

public Notification ()

{

this . when = System . currentTimeMillis ();

this . priority = PRIORITY_DEFAULT ;

}

/**

* @hide

*/

public Notification ( Context context , int icon , CharSequence tickerText , long when ,

CharSequence contentTitle , CharSequence contentText , Intent contentIntent )

{

new Builder ( context )

. setWhen ( when )

. setSmallIcon ( icon )

. setTicker ( tickerText )

. setContentTitle ( contentTitle )

. setContentText ( contentText )

. setContentIntent ( PendingIntent . getActivity ( context , 0 , contentIntent , 0 ))

. buildInto ( this );

}

/**

* Constructs a Notification object with the information needed to

* have a status bar icon without the standard expanded view.

*

* @param icon The resource id of the icon to put in the status bar.

* @param tickerText The text that flows by in the status bar when the notification first

* activates.

* @param when The time to show in the time field. In the System.currentTimeMillis

* timebase.

*

* @deprecated Use {@link Builder} instead.

*/

@Deprecated

public Notification ( int icon , CharSequence tickerText , long when )

{

this . icon = icon ;

this . tickerText = tickerText ;

this . when = when ;

}

/**

* Unflatten the notification from a parcel.

*/

public Notification ( Parcel parcel )

{

int version = parcel . readInt ();

when = parcel . readLong ();

if ( parcel . readInt () != 0 ) {

mSmallIcon = Icon . CREATOR . createFromParcel ( parcel );

if ( mSmallIcon . getType () == Icon . TYPE_RESOURCE ) {

icon = mSmallIcon . getResId ();

}

}

number = parcel . readInt ();

if ( parcel . readInt () != 0 ) {

contentIntent = PendingIntent . CREATOR . createFromParcel ( parcel );

}

if ( parcel . readInt () != 0 ) {

deleteIntent = PendingIntent . CREATOR . createFromParcel ( parcel );

}

if ( parcel . readInt () != 0 ) {

tickerText = TextUtils . CHAR_SEQUENCE_CREATOR . createFromParcel ( parcel );

}

if ( parcel . readInt () != 0 ) {

tickerView = RemoteViews . CREATOR . createFromParcel ( parcel );

}

if ( parcel . readInt () != 0 ) {

contentView = RemoteViews . CREATOR . createFromParcel ( parcel );

}

if ( parcel . readInt () != 0 ) {

mLargeIcon = Icon . CREATOR . createFromParcel ( parcel );

}

defaults = parcel . readInt ();

flags = parcel . readInt ();

if ( parcel . readInt () != 0 ) {

sound = Uri . CREATOR . createFromParcel ( parcel );

}

audioStreamType = parcel . readInt ();

if ( parcel . readInt () != 0 ) {

audioAttributes = AudioAttributes . CREATOR . createFromParcel ( parcel );

}

vibrate = parcel . createLongArray ();

ledARGB = parcel . readInt ();

ledOnMS = parcel . readInt ();

ledOffMS = parcel . readInt ();

iconLevel = parcel . readInt ();

if ( parcel . readInt () != 0 ) {

fullScreenIntent = PendingIntent . CREATOR . createFromParcel ( parcel );

}

priority = parcel . readInt ();

category = parcel . readString ();

mGroupKey = parcel . readString ();

mSortKey = parcel . readString ();

extras = parcel . readBundle (); // may be null

actions = parcel . createTypedArray ( Action . CREATOR ); // may be null

if ( parcel . readInt () != 0 ) {

bigContentView = RemoteViews . CREATOR . createFromParcel ( parcel );

}

if ( parcel . readInt () != 0 ) {

headsUpContentView = RemoteViews . CREATOR . createFromParcel ( parcel );

}

visibility = parcel . readInt ();

if ( parcel . readInt () != 0 ) {

publicVersion = Notification . CREATOR . createFromParcel ( parcel );

}

color = parcel . readInt ();

if ( parcel . readInt () != 0 ) {

topic = Topic . CREATOR . createFromParcel ( parcel );

}

}

@Override

public Notification clone () {

Notification that = new Notification ();

cloneInto ( that , true );

return that ;

}

/**

* Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members

* of this into that.

* @hide

*/

public void cloneInto ( Notification that , boolean heavy ) {

that . when = this . when ;

that . mSmallIcon = this . mSmallIcon ;

that . number = this . number ;

// PendingIntents are global, so there's no reason (or way) to clone them.

that . contentIntent = this . contentIntent ;

that . deleteIntent = this . deleteIntent ;

that . fullScreenIntent = this . fullScreenIntent ;

if ( this . tickerText != null ) {

that . tickerText = this . tickerText . toString ();

}

if ( heavy && this . tickerView != null ) {

that . tickerView = this . tickerView . clone ();

}

if ( heavy && this . contentView != null ) {

that . contentView = this . contentView . clone ();

}

if ( heavy && this . mLargeIcon != null ) {

that . mLargeIcon = this . mLargeIcon ;

}

that . iconLevel = this . iconLevel ;

that . sound = this . sound ; // android.net.Uri is immutable

that . audioStreamType = this . audioStreamType ;

if ( this . audioAttributes != null ) {

that . audioAttributes = new AudioAttributes . Builder ( this . audioAttributes ). build ();

}

final long [] vibrate = this . vibrate ;

if ( vibrate != null ) {

final int N = vibrate . length ;

final long [] vib = that . vibrate = new long [ N ];

System . arraycopy ( vibrate , 0 , vib , 0 , N );

}

that . ledARGB = this . ledARGB ;

that . ledOnMS = this . ledOnMS ;

that . ledOffMS = this . ledOffMS ;

that . defaults = this . defaults ;

that . flags = this . flags ;

that . priority = this . priority ;

that . category = this . category ;

that . mGroupKey = this . mGroupKey ;

that . mSortKey = this . mSortKey ;

if ( this . extras != null ) {

try {

that . extras = new Bundle ( this . extras );

// will unparcel

that . extras . size ();

} catch ( BadParcelableException e ) {

Log . e ( TAG , "could not unparcel extras from notification: " + this , e );

that . extras = null ;

}

}

if ( this . actions != null ) {

that . actions = new Action [ this . actions . length ];

for ( int i = 0 ; i < this . actions . length ; i ++) {

that . actions [ i ] = this . actions [ i ]. clone ();

}

}

if ( heavy && this . bigContentView != null ) {

that . bigContentView = this . bigContentView . clone ();

}

if ( heavy && this . headsUpContentView != null ) {

that . headsUpContentView = this . headsUpContentView . clone ();

}

that . visibility = this . visibility ;

if ( this . publicVersion != null ) {

that . publicVersion = new Notification ();

this . publicVersion . cloneInto ( that . publicVersion , heavy );

}

that . color = this . color ;

if ( this . topic != null ) {

that . topic = this . topic . clone ();

}

if (! heavy ) {

that . lightenPayload (); // will clean out extras

}

}

/**

* Removes heavyweight parts of the Notification object for archival or for sending to

* listeners when the full contents are not necessary.

* @hide

*/

public final void lightenPayload () {

tickerView = null ;

contentView = null ;

bigContentView = null ;

headsUpContentView = null ;

mLargeIcon = null ;

if ( extras != null && ! extras . isEmpty ()) {

final Set < String > keyset = extras . keySet ();

final int N = keyset . size ();

final String [] keys = keyset . toArray ( new String [ N ]);

for ( int i = 0 ; i < N ; i ++) {

final String key = keys [ i ];

final Object obj = extras . get ( key );

if ( obj != null &&

( obj instanceof Parcelable

|| obj instanceof Parcelable []

|| obj instanceof SparseArray

|| obj instanceof ArrayList )) {

extras . remove ( key );

}

}

}

}

/**

* Make sure this CharSequence is safe to put into a bundle, which basically

* means it had better not be some custom Parcelable implementation.

* @hide

*/

public static CharSequence safeCharSequence ( CharSequence cs ) {

if ( cs == null ) return cs ;

if ( cs . length () > MAX_CHARSEQUENCE_LENGTH ) {

cs = cs . subSequence ( 0 , MAX_CHARSEQUENCE_LENGTH );

}

if ( cs instanceof Parcelable ) {

Log . e ( TAG , "warning: " + cs . getClass (). getCanonicalName ()

+ " instance is a custom Parcelable and not allowed in Notification" );

return cs . toString ();

}

return cs ;

}

public int describeContents () {

return 0 ;

}

/**

* Flatten this notification into a parcel.

*/

public void writeToParcel ( Parcel parcel , int flags )

{

parcel . writeInt ( 1 );

parcel . writeLong ( when );

if ( mSmallIcon == null && icon != 0 ) {

// you snuck an icon in here without using the builder; let's try to keep it

mSmallIcon = Icon . createWithResource ( "" , icon );

}

if ( mSmallIcon != null ) {

parcel . writeInt ( 1 );

mSmallIcon . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

parcel . writeInt ( number );

if ( contentIntent != null ) {

parcel . writeInt ( 1 );

contentIntent . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

if ( deleteIntent != null ) {

parcel . writeInt ( 1 );

deleteIntent . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

if ( tickerText != null ) {

parcel . writeInt ( 1 );

TextUtils . writeToParcel ( tickerText , parcel , flags );

} else {

parcel . writeInt ( 0 );

}

if ( tickerView != null ) {

parcel . writeInt ( 1 );

tickerView . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

if ( contentView != null ) {

parcel . writeInt ( 1 );

contentView . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

if ( mLargeIcon != null ) {

parcel . writeInt ( 1 );

mLargeIcon . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

parcel . writeInt ( defaults );

parcel . writeInt ( this . flags );

if ( sound != null ) {

parcel . writeInt ( 1 );

sound . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

parcel . writeInt ( audioStreamType );

if ( audioAttributes != null ) {

parcel . writeInt ( 1 );

audioAttributes . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

parcel . writeLongArray ( vibrate );

parcel . writeInt ( ledARGB );

parcel . writeInt ( ledOnMS );

parcel . writeInt ( ledOffMS );

parcel . writeInt ( iconLevel );

if ( fullScreenIntent != null ) {

parcel . writeInt ( 1 );

fullScreenIntent . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

parcel . writeInt ( priority );

parcel . writeString ( category );

parcel . writeString ( mGroupKey );

parcel . writeString ( mSortKey );

parcel . writeBundle ( extras ); // null ok

parcel . writeTypedArray ( actions , 0 ); // null ok

if ( bigContentView != null ) {

parcel . writeInt ( 1 );

bigContentView . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

if ( headsUpContentView != null ) {

parcel . writeInt ( 1 );

headsUpContentView . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

parcel . writeInt ( visibility );

if ( publicVersion != null ) {

parcel . writeInt ( 1 );

publicVersion . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

parcel . writeInt ( color );

if ( topic != null ) {

parcel . writeInt ( 1 );

topic . writeToParcel ( parcel , 0 );

} else {

parcel . writeInt ( 0 );

}

}

/**

* Parcelable.Creator that instantiates Notification objects

*/

public static final Parcelable . Creator < Notification > CREATOR

= new Parcelable . Creator < Notification >()

{

public Notification createFromParcel ( Parcel parcel )

{

return new Notification ( parcel );

}

public Notification [] newArray ( int size )

{

return new Notification [ size ];

}

};

/**

* Sets the {@link #contentView} field to be a view with the standard "Latest Event"

* layout.

*

* <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields

* in the view.</p>

* @param context The context for your application / activity.

* @param contentTitle The title that goes in the expanded entry.

* @param contentText The text that goes in the expanded entry.

* @param contentIntent The intent to launch when the user clicks the expanded notification.

* If this is an activity, it must include the

* {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires

* that you take care of task management as described in the

* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back

* Stack</a> document.

*

* @deprecated Use {@link Builder} instead.

* @removed

*/

@Deprecated

public void setLatestEventInfo ( Context context ,

CharSequence contentTitle , CharSequence contentText , PendingIntent contentIntent ) {

if ( context . getApplicationInfo (). targetSdkVersion > Build . VERSION_CODES . LOLLIPOP_MR1 ){

Log . e ( TAG , "setLatestEventInfo() is deprecated and you should feel deprecated." ,

new Throwable ());

}

// ensure that any information already set directly is preserved

final Notification . Builder builder = new Notification . Builder ( context , this );

// now apply the latestEventInfo fields

if ( contentTitle != null ) {

builder . setContentTitle ( contentTitle );

}

if ( contentText != null ) {

builder . setContentText ( contentText );

}

builder . setContentIntent ( contentIntent );

builder . build (); // callers expect this notification to be ready to use

}

/**

* @hide

*/

public static void addFieldsFromContext ( Context context , Notification notification ) {

if ( notification . extras . getParcelable ( EXTRA_BUILDER_APPLICATION_INFO ) == null ) {

notification . extras . putParcelable ( EXTRA_BUILDER_APPLICATION_INFO ,

context . getApplicationInfo ());

}

if (! notification . extras . containsKey ( EXTRA_ORIGINATING_USERID )) {

notification . extras . putInt ( EXTRA_ORIGINATING_USERID , context . getUserId ());

}

}

@Override

public String toString () {

StringBuilder sb = new StringBuilder ();

sb . append ( "Notification(pri=" );

sb . append ( priority );

sb . append ( " contentView=" );

if ( contentView != null ) {

sb . append ( contentView . getPackage ());

sb . append ( "/0x" );

sb . append ( Integer . toHexString ( contentView . getLayoutId ()));

} else {

sb . append ( "null" );

}

sb . append ( " vibrate=" );

if (( this . defaults & DEFAULT_VIBRATE ) != 0 ) {

sb . append ( "default" );

} else if ( this . vibrate != null ) {

int N = this . vibrate . length - 1 ;

sb . append ( "[" );

for ( int i = 0 ; i < N ; i ++) {

sb . append ( this . vibrate [ i ]);

sb . append ( ',' );

}

if ( N != - 1 ) {

sb . append ( this . vibrate [ N ]);

}

sb . append ( "]" );

} else {

sb . append ( "null" );

}

sb . append ( " sound=" );

if (( this . defaults & DEFAULT_SOUND ) != 0 ) {

sb . append ( "default" );

} else if ( this . sound != null ) {

sb . append ( this . sound . toString ());

} else {

sb . append ( "null" );

}

if ( this . tickerText != null ) {

sb . append ( " tick" );

}

sb . append ( " defaults=0x" );

sb . append ( Integer . toHexString ( this . defaults ));

sb . append ( " flags=0x" );

sb . append ( Integer . toHexString ( this . flags ));

sb . append ( String . format ( " color=0x%08x" , this . color ));

if ( this . category != null ) {

sb . append ( " category=" );

sb . append ( this . category );

}

if ( this . mGroupKey != null ) {

sb . append ( " groupKey=" );

sb . append ( this . mGroupKey );

}

if ( this . mSortKey != null ) {

sb . append ( " sortKey=" );

sb . append ( this . mSortKey );

}

if ( actions != null ) {

sb . append ( " actions=" );

sb . append ( actions . length );

}

sb . append ( " vis=" );

sb . append ( visibilityToString ( this . visibility ));

if ( this . publicVersion != null ) {

sb . append ( " publicVersion=" );

sb . append ( publicVersion . toString ());

}

if ( topic != null ) {

sb . append ( "topic=" );

sb . append ( topic . toString ());

}

sb . append ( ")" );

return sb . toString ();

}

/**

* {@hide}

*/

public static String visibilityToString ( int vis ) {

switch ( vis ) {

case VISIBILITY_PRIVATE :

return "PRIVATE" ;

case VISIBILITY_PUBLIC :

return "PUBLIC" ;

case VISIBILITY_SECRET :

return "SECRET" ;

default :

return "UNKNOWN(" + String . valueOf ( vis ) + ")" ;

}

}

/**

* {@hide}

*/

public static String priorityToString ( @Priority int pri ) {

switch ( pri ) {

case PRIORITY_MIN :

return "MIN" ;

case PRIORITY_LOW :

return "LOW" ;

case PRIORITY_DEFAULT :

return "DEFAULT" ;

case PRIORITY_HIGH :

return "HIGH" ;

case PRIORITY_MAX :

return "MAX" ;

default :

return "UNKNOWN(" + String . valueOf ( pri ) + ")" ;

}

}

/**

* The small icon representing this notification in the status bar and content view.

*

* @return the small icon representing this notification.

*

* @see Builder#getSmallIcon()

* @see Builder#setSmallIcon(Icon)

*/

public Icon getSmallIcon () {

return mSmallIcon ;

}

/**

* Used when notifying to clean up legacy small icons.

* @hide

*/

public void setSmallIcon ( Icon icon ) {

mSmallIcon = icon ;

}

/**

* The large icon shown in this notification's content view.

* @see Builder#getLargeIcon()

* @see Builder#setLargeIcon(Icon)

*/

public Icon getLargeIcon () {

return mLargeIcon ;

}

/**

* @hide

*/

public boolean isGroupSummary () {

return mGroupKey != null && ( flags & FLAG_GROUP_SUMMARY ) != 0 ;

}

/**

* @hide

*/

public boolean isGroupChild () {

return mGroupKey != null && ( flags & FLAG_GROUP_SUMMARY ) == 0 ;

}

/**

* Builder class for {@link Notification} objects.

*

* Provides a convenient way to set the various fields of a {@link Notification} and generate

* content views using the platform's notification layout template. If your app supports

* versions of Android as old as API level 4, you can instead use

* {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},

* available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support

* library</a>.

*

* <p>Example:

*

* <pre class="prettyprint">

* Notification noti = new Notification.Builder(mContext)

* .setContentTitle("New mail from " + sender.toString())

* .setContentText(subject)

* .setSmallIcon(R.drawable.new_mail)

* .setLargeIcon(aBitmap)

* .build();

* </pre>

*/

public static class Builder {

private static final int MAX_ACTION_BUTTONS = 3 ;

private static final float LARGE_TEXT_SCALE = 1.3f ;

private Context mContext ;

private Notification mN ;

private Bundle mUserExtras = new Bundle ();

private Style mStyle ;

private ArrayList < Action > mActions = new ArrayList < Action >( MAX_ACTION_BUTTONS );

private ArrayList < String > mPersonList = new ArrayList < String >();

private NotificationColorUtil mColorUtil ;

private boolean mColorUtilInited = false ;

/**

* Constructs a new Builder with the defaults:

*

* <table>

* <tr><th align=right>priority</th>

* <td>{@link #PRIORITY_DEFAULT}</td></tr>

* <tr><th align=right>when</th>

* <td>now ({@link System#currentTimeMillis()})</td></tr>

* <tr><th align=right>audio stream</th>

* <td>{@link #STREAM_DEFAULT}</td></tr>

* </table>

*

* @param context

* A {@link Context} that will be used by the Builder to construct the

* RemoteViews. The Context will not be held past the lifetime of this Builder

* object.

*/

public Builder ( Context context ) {

this ( context , null );

}

/**

* @hide

*/

public Builder ( Context context , Notification toAdopt ) {

mContext = context ;

if ( toAdopt == null ) {

mN = new Notification ();

mN . extras . putBoolean ( EXTRA_SHOW_WHEN , true );

mN . priority = PRIORITY_DEFAULT ;

mN . visibility = VISIBILITY_PRIVATE ;

} else {

mN = toAdopt ;

if ( mN . actions != null ) {

Collections . addAll ( mActions , mN . actions );

}

if ( mN . extras . containsKey ( EXTRA_PEOPLE )) {

Collections . addAll ( mPersonList , mN . extras . getStringArray ( EXTRA_PEOPLE ));

}

String templateClass = mN . extras . getString ( EXTRA_TEMPLATE );

if (! TextUtils . isEmpty ( templateClass )) {

final Class <? extends Style > styleClass

= getNotificationStyleClass ( templateClass );

if ( styleClass == null ) {

Log . d ( TAG , "Unknown style class: " + templateClass );

} else {

try {

final Constructor <? extends Style > ctor = styleClass . getConstructor ();

ctor . setAccessible ( true );

final Style style = ctor . newInstance ();

style . restoreFromExtras ( mN . extras );

if ( style != null ) {

setStyle ( style );

}

} catch ( Throwable t ) {

Log . e ( TAG , "Could not create Style" , t );

}

}

}

}

}

private NotificationColorUtil getColorUtil () {

if (! mColorUtilInited ) {

mColorUtilInited = true ;

if ( mContext . getApplicationInfo (). targetSdkVersion < Build . VERSION_CODES . LOLLIPOP ) {

mColorUtil = NotificationColorUtil . getInstance ( mContext );

}

}

return mColorUtil ;

}

/**

* Add a timestamp pertaining to the notification (usually the time the event occurred).

* It will be shown in the notification content view by default; use

* {@link #setShowWhen(boolean) setShowWhen} to control this.

*

* @see Notification#when

*/

public Builder setWhen ( long when ) {

mN . when = when ;

return this ;

}

/**

* Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown

* in the content view.

*/

public Builder setShowWhen ( boolean show ) {

mN . extras . putBoolean ( EXTRA_SHOW_WHEN , show );

return this ;

}

/**

* Show the {@link Notification#when} field as a stopwatch.

*

* Instead of presenting <code>when</code> as a timestamp, the notification will show an

* automatically updating display of the minutes and seconds since <code>when</code>.

*

* Useful when showing an elapsed time (like an ongoing phone call).

*

* @see android.widget.Chronometer

* @see Notification#when

*/

public Builder setUsesChronometer ( boolean b ) {

mN . extras . putBoolean ( EXTRA_SHOW_CHRONOMETER , b );

return this ;

}

/**

* Set the small icon resource, which will be used to represent the notification in the

* status bar.

*

* The platform template for the expanded view will draw this icon in the left, unless a

* {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small

* icon will be moved to the right-hand side.

*

* @param icon

* A resource ID in the application's package of the drawable to use.

* @see Notification#icon

*/

public Builder setSmallIcon ( @DrawableRes int icon ) {

return setSmallIcon ( icon != 0

? Icon . createWithResource ( mContext , icon )

: null );

}

/**

* A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional

* level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable

* LevelListDrawable}.

*

* @param icon A resource ID in the application's package of the drawable to use.

* @param level The level to use for the icon.

*

* @see Notification#icon

* @see Notification#iconLevel

*/

public Builder setSmallIcon ( @DrawableRes int icon , int level ) {

mN . iconLevel = level ;

return setSmallIcon ( icon );

}

/**

* Set the small icon, which will be used to represent the notification in the

* status bar and content view (unless overriden there by a

* {@link #setLargeIcon(Bitmap) large icon}).

*

* @param icon An Icon object to use.

* @see Notification#icon

*/

public Builder setSmallIcon ( Icon icon ) {

mN . setSmallIcon ( icon );

if ( icon != null && icon . getType () == Icon . TYPE_RESOURCE ) {

mN . icon = icon . getResId ();

}

return this ;

}

/**

* Set the first line of text in the platform notification template.

*/

public Builder setContentTitle ( CharSequence title ) {

mN . extras . putCharSequence ( EXTRA_TITLE , safeCharSequence ( title ));

return this ;

}

/**

* Set the second line of text in the platform notification template.

*/

public Builder setContentText ( CharSequence text ) {

mN . extras . putCharSequence ( EXTRA_TEXT , safeCharSequence ( text ));

return this ;

}

/**

* Set the third line of text in the platform notification template.

* Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the

* same location in the standard template.

*/

public Builder setSubText ( CharSequence text ) {

mN . extras . putCharSequence ( EXTRA_SUB_TEXT , safeCharSequence ( text ));

return this ;

}

/**

* Set the large number at the right-hand side of the notification. This is

* equivalent to setContentInfo, although it might show the number in a different

* font size for readability.

*/

public Builder setNumber ( int number ) {

mN . number = number ;

return this ;

}

/**

* A small piece of additional information pertaining to this notification.

*

* The platform template will draw this on the last line of the notification, at the far

* right (to the right of a smallIcon if it has been placed there).

*/

public Builder setContentInfo ( CharSequence info ) {

mN . extras . putCharSequence ( EXTRA_INFO_TEXT , safeCharSequence ( info ));

return this ;

}

/**

* Set the progress this notification represents.

*

* The platform template will represent this using a {@link ProgressBar}.

*/

public Builder setProgress ( int max , int progress , boolean indeterminate ) {

mN . extras . putInt ( EXTRA_PROGRESS , progress );

mN . extras . putInt ( EXTRA_PROGRESS_MAX , max );

mN . extras . putBoolean ( EXTRA_PROGRESS_INDETERMINATE , indeterminate );

return this ;

}

/**

* Supply a custom RemoteViews to use instead of the platform template.

*

* Use {@link #setCustomContentView(RemoteViews)} instead.

*/

@Deprecated

public Builder setContent ( RemoteViews views ) {

return setCustomContentView ( views );

}

/**

* Supply custom RemoteViews to use instead of the platform template.

*

* This will override the layout that would otherwise be constructed by this Builder

* object.

*/

public Builder setCustomContentView ( RemoteViews contentView ) {

mN . contentView = contentView ;

return this ;

}

/**

* Supply custom RemoteViews to use instead of the platform template in the expanded form.

*

* This will override the expanded layout that would otherwise be constructed by this

* Builder object.

*/

public Builder setCustomBigContentView ( RemoteViews contentView ) {

mN . bigContentView = contentView ;

return this ;

}

/**

* Supply custom RemoteViews to use instead of the platform template in the heads up dialog.

*

* This will override the heads-up layout that would otherwise be constructed by this

* Builder object.

*/

public Builder setCustomHeadsUpContentView ( RemoteViews contentView ) {

mN . headsUpContentView = contentView ;

return this ;

}

/**

* Supply a {@link PendingIntent} to be sent when the notification is clicked.

*

* As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you

* have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use

* {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}

* to assign PendingIntents to individual views in that custom layout (i.e., to create

* clickable buttons inside the notification view).

*

* @see Notification#contentIntent Notification.contentIntent

*/

public Builder setContentIntent ( PendingIntent intent ) {

mN . contentIntent = intent ;

return this ;

}

/**

* Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.

*

* @see Notification#deleteIntent

*/

public Builder setDeleteIntent ( PendingIntent intent ) {

mN . deleteIntent = intent ;

return this ;

}

/**

* An intent to launch instead of posting the notification to the status bar.

* Only for use with extremely high-priority notifications demanding the user's

* <strong>immediate</strong> attention, such as an incoming phone call or

* alarm clock that the user has explicitly set to a particular time.

* If this facility is used for something else, please give the user an option

* to turn it off and use a normal notification, as this can be extremely

* disruptive.

*

* <p>

* The system UI may choose to display a heads-up notification, instead of

* launching this intent, while the user is using the device.

* </p>

*

* @param intent The pending intent to launch.

* @param highPriority Passing true will cause this notification to be sent

* even if other notifications are suppressed.

*

* @see Notification#fullScreenIntent

*/

public Builder setFullScreenIntent ( PendingIntent intent , boolean highPriority ) {

mN . fullScreenIntent = intent ;

setFlag ( FLAG_HIGH_PRIORITY , highPriority );

return this ;

}

/**

* Set the "ticker" text which is sent to accessibility services.

*

* @see Notification#tickerText

*/

public Builder setTicker ( CharSequence tickerText ) {

mN . tickerText = safeCharSequence ( tickerText );

return this ;

}

/**

* Obsolete version of {@link #setTicker(CharSequence)}.

*

*/

@Deprecated

public Builder setTicker ( CharSequence tickerText , RemoteViews views ) {

setTicker ( tickerText );

// views is ignored

return this ;

}

/**

* Add a large icon to the notification content view.

*

* In the platform template, this image will be shown on the left of the notification view

* in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small

* badge atop the large icon).

*/

public Builder setLargeIcon ( Bitmap b ) {

return setLargeIcon ( b != null ? Icon . createWithBitmap ( b ) : null );

}

/**

* Add a large icon to the notification content view.

*

* In the platform template, this image will be shown on the left of the notification view

* in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small

* badge atop the large icon).

*/

public Builder setLargeIcon ( Icon icon ) {

mN . mLargeIcon = icon ;

mN . extras . putParcelable ( EXTRA_LARGE_ICON , icon );

return this ;

}

/**

* Set the sound to play.

*

* It will be played using the {@link #AUDIO_ATTRIBUTES_DEFAULT default audio attributes}

* for notifications.

*

* <p>

* A notification that is noisy is more likely to be presented as a heads-up notification.

* </p>

*

* @see Notification#sound

*/

public Builder setSound ( Uri sound ) {

mN . sound = sound ;

mN . audioAttributes = AUDIO_ATTRIBUTES_DEFAULT ;

return this ;

}

/**

* Set the sound to play, along with a specific stream on which to play it.

*

* See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.

*

* <p>

* A notification that is noisy is more likely to be presented as a heads-up notification.

* </p>

* @deprecated use {@link #setSound(Uri, AudioAttributes)} instead.

* @see Notification#sound

*/

@Deprecated

public Builder setSound ( Uri sound , int streamType ) {

mN . sound = sound ;

mN . audioStreamType = streamType ;

return this ;

}

/**

* Set the sound to play, along with specific {@link AudioAttributes audio attributes} to

* use during playback.

*

* <p>

* A notification that is noisy is more likely to be presented as a heads-up notification.

* </p>

*

* @see Notification#sound

*/

public Builder setSound ( Uri sound , AudioAttributes audioAttributes ) {

mN . sound = sound ;

mN . audioAttributes = audioAttributes ;

return this ;

}

/**

* Set the vibration pattern to use.

*

* See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the

* <code>pattern</code> parameter.

*

* <p>

* A notification that vibrates is more likely to be presented as a heads-up notification.

* </p>

*

* @see Notification#vibrate

*/

public Builder setVibrate ( long [] pattern ) {

mN . vibrate = pattern ;

return this ;

}

/**

* Set the desired color for the indicator LED on the device, as well as the

* blink duty cycle (specified in milliseconds).

*

* Not all devices will honor all (or even any) of these values.

*

* @see Notification#ledARGB

* @see Notification#ledOnMS

* @see Notification#ledOffMS

*/

public Builder setLights ( @ColorInt int argb , int onMs , int offMs ) {

mN . ledARGB = argb ;

mN . ledOnMS = onMs ;

mN . ledOffMS = offMs ;

if ( onMs != 0 || offMs != 0 ) {

mN . flags |= FLAG_SHOW_LIGHTS ;

}

if (( mN . defaults & DEFAULT_LIGHTS ) != 0 ) {

mN . flags |= FLAG_SHOW_LIGHTS ;

}

return this ;

}

/**

* Set whether this is an "ongoing" notification.

*

* Ongoing notifications cannot be dismissed by the user, so your application or service

* must take care of canceling them.

*

* They are typically used to indicate a background task that the user is actively engaged

* with (e.g., playing music) or is pending in some way and therefore occupying the device

* (e.g., a file download, sync operation, active network connection).

*

* @see Notification#FLAG_ONGOING_EVENT

* @see Service#setForeground(boolean)

*/

public Builder setOngoing ( boolean ongoing ) {

setFlag ( FLAG_ONGOING_EVENT , ongoing );

return this ;

}

/**

* Set this flag if you would only like the sound, vibrate

* and ticker to be played if the notification is not already showing.

*

* @see Notification#FLAG_ONLY_ALERT_ONCE

*/

public Builder setOnlyAlertOnce ( boolean onlyAlertOnce ) {

setFlag ( FLAG_ONLY_ALERT_ONCE , onlyAlertOnce );

return this ;

}

/**

* Make this notification automatically dismissed when the user touches it. The

* PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.

*

* @see Notification#FLAG_AUTO_CANCEL

*/

public Builder setAutoCancel ( boolean autoCancel ) {

setFlag ( FLAG_AUTO_CANCEL , autoCancel );

return this ;

}

/**

* Set whether or not this notification should not bridge to other devices.

*

* <p>Some notifications can be bridged to other devices for remote display.

* This hint can be set to recommend this notification not be bridged.

*/

public Builder setLocalOnly ( boolean localOnly ) {

setFlag ( FLAG_LOCAL_ONLY , localOnly );

return this ;

}

/**

* Set which notification properties will be inherited from system defaults.

* <p>

* The value should be one or more of the following fields combined with

* bitwise-or:

* {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.

* <p>

* For all default values, use {@link #DEFAULT_ALL}.

*/

public Builder setDefaults ( int defaults ) {

mN . defaults = defaults ;

return this ;

}

/**

* Set the priority of this notification.

*

* @see Notification#priority

*/

public Builder setPriority ( @Priority int pri ) {

mN . priority = pri ;

return this ;

}

/**

* Set the notification category.

*

* @see Notification#category

*/

public Builder setCategory ( String category ) {

mN . category = category ;

return this ;

}

/**

* Add a person that is relevant to this notification.

*

* <P>

* Depending on user preferences, this annotation may allow the notification to pass

* through interruption filters, and to appear more prominently in the user interface.

* </P>

*

* <P>

* The person should be specified by the {@code String} representation of a

* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.

* </P>

*

* <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema

* URIs. The path part of these URIs must exist in the contacts database, in the

* appropriate column, or the reference will be discarded as invalid. Telephone schema

* URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.

* </P>

*

* @param uri A URI for the person.

* @see Notification#EXTRA_PEOPLE

*/

public Builder addPerson ( String uri ) {

mPersonList . add ( uri );

return this ;

}

/**

* Set this notification to be part of a group of notifications sharing the same key.

* Grouped notifications may display in a cluster or stack on devices which

* support such rendering.

*

* <p>To make this notification the summary for its group, also call

* {@link #setGroupSummary}. A sort order can be specified for group members by using

* {@link #setSortKey}.

* @param groupKey The group key of the group.

* @return this object for method chaining

*/

public Builder setGroup ( String groupKey ) {

mN . mGroupKey = groupKey ;

return this ;

}

/**

* Set this notification to be the group summary for a group of notifications.

* Grouped notifications may display in a cluster or stack on devices which

* support such rendering. Requires a group key also be set using {@link #setGroup}.

* @param isGroupSummary Whether this notification should be a group summary.

* @return this object for method chaining

*/

public Builder setGroupSummary ( boolean isGroupSummary ) {

setFlag ( FLAG_GROUP_SUMMARY , isGroupSummary );

return this ;

}

/**

* Set a sort key that orders this notification among other notifications from the

* same package. This can be useful if an external sort was already applied and an app

* would like to preserve this. Notifications will be sorted lexicographically using this

* value, although providing different priorities in addition to providing sort key may

* cause this value to be ignored.

*

* <p>This sort key can also be used to order members of a notification group. See

* {@link #setGroup}.

*

* @see String#compareTo(String)

*/

public Builder setSortKey ( String sortKey ) {

mN . mSortKey = sortKey ;

return this ;

}

/**

* Merge additional metadata into this notification.

*

* <p>Values within the Bundle will replace existing extras values in this Builder.

*

* @see Notification#extras

*/

public Builder addExtras ( Bundle extras ) {

if ( extras != null ) {

mUserExtras . putAll ( extras );

}

return this ;

}

/**

* Set metadata for this notification.

*

* <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's

* current contents are copied into the Notification each time {@link #build()} is

* called.

*

* <p>Replaces any existing extras values with those from the provided Bundle.

* Use {@link #addExtras} to merge in metadata instead.

*

* @see Notification#extras

*/

public Builder setExtras ( Bundle extras ) {

if ( extras != null ) {

mUserExtras = extras ;

}

return this ;

}

/**

* Get the current metadata Bundle used by this notification Builder.

*

* <p>The returned Bundle is shared with this Builder.

*

* <p>The current contents of this Bundle are copied into the Notification each time

* {@link #build()} is called.

*

* @see Notification#extras

*/

public Bundle getExtras () {

return mUserExtras ;

}

private Bundle getAllExtras () {

final Bundle saveExtras = ( Bundle ) mUserExtras . clone ();

saveExtras . putAll ( mN . extras );

return saveExtras ;

}

/**

* Add an action to this notification. Actions are typically displayed by

* the system as a button adjacent to the notification content.

* <p>

* Every action must have an icon (32dp square and matching the

* <a href="{@docRoot}design/style/iconography.html#action-bar">Holo

* Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.

* <p>

* A notification in its expanded form can display up to 3 actions, from left to right in

* the order they were added. Actions will not be displayed when the notification is

* collapsed, however, so be sure that any essential functions may be accessed by the user

* in some other way (for example, in the Activity pointed to by {@link #contentIntent}).

*

* @param icon Resource ID of a drawable that represents the action.

* @param title Text describing the action.

* @param intent PendingIntent to be fired when the action is invoked.

*

* @deprecated Use {@link #addAction(Action)} instead.

*/

@Deprecated

public Builder addAction ( int icon , CharSequence title , PendingIntent intent ) {

mActions . add ( new Action ( icon , safeCharSequence ( title ), intent ));

return this ;

}

/**

* Add an action to this notification. Actions are typically displayed by

* the system as a button adjacent to the notification content.

* <p>

* Every action must have an icon (32dp square and matching the

* <a href="{@docRoot}design/style/iconography.html#action-bar">Holo

* Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}.

* <p>

* A notification in its expanded form can display up to 3 actions, from left to right in

* the order they were added. Actions will not be displayed when the notification is

* collapsed, however, so be sure that any essential functions may be accessed by the user

* in some other way (for example, in the Activity pointed to by {@link #contentIntent}).

*

* @param action The action to add.

*/

public Builder addAction ( Action action ) {

mActions . add ( action );

return this ;

}

/**

* Alter the complete list of actions attached to this notification.

* @see #addAction(Action).

*

* @param actions

* @return

*/

public Builder setActions ( Action ... actions ) {

mActions . clear ();

for ( int i = 0 ; i < actions . length ; i ++) {

mActions . add ( actions [ i ]);

}

return this ;

}

/**

* Add a rich notification style to be applied at build time.

*

* @param style Object responsible for modifying the notification style.

*/

public Builder setStyle ( Style style ) {

if ( mStyle != style ) {

mStyle = style ;

if ( mStyle != null ) {

mStyle . setBuilder ( this );

mN . extras . putString ( EXTRA_TEMPLATE , style . getClass (). getName ());

} else {

mN . extras . remove ( EXTRA_TEMPLATE );

}

}

return this ;

}

/**

* Specify the value of {@link #visibility}.

*

* @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),

* {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.

*

* @return The same Builder.

*/

public Builder setVisibility ( int visibility ) {

mN . visibility = visibility ;

return this ;

}

/**

* Supply a replacement Notification whose contents should be shown in insecure contexts

* (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}.

* @param n A replacement notification, presumably with some or all info redacted.

* @return The same Builder.

*/

public Builder setPublicVersion ( Notification n ) {

if ( n != null ) {

mN . publicVersion = new Notification ();

n . cloneInto ( mN . publicVersion , /*heavy=*/ true );

} else {

mN . publicVersion = null ;

}

return this ;

}

/**

* Apply an extender to this notification builder. Extenders may be used to add

* metadata or change options on this builder.

*/

public Builder extend ( Extender extender ) {

extender . extend ( this );

return this ;

}

/**

* @hide

*/

public void setFlag ( int mask , boolean value ) {

if ( value ) {

mN . flags |= mask ;

} else {

mN . flags &= ~ mask ;

}

}

/**

* Sets {@link Notification#color}.

*

* @param argb The accent color to use

*

* @return The same Builder.

*/

public Builder setColor ( @ColorInt int argb ) {

mN . color = argb ;

sanitizeColor ();

return this ;

}

/**

* Sets the topic of this notification. Topics are typically displayed in Notification

* settings.

* <p>

* Every topic must have an id and a textual label.

*

* @param topic The topic to add.

*/

public Builder setTopic ( Topic topic ) {

mN . topic = topic ;

return this ;

}

private Drawable getProfileBadgeDrawable () {

// Note: This assumes that the current user can read the profile badge of the

// originating user.

return mContext . getPackageManager (). getUserBadgeForDensity (

new UserHandle ( mContext . getUserId ()), 0 );

}

private Bitmap getProfileBadge () {

Drawable badge = getProfileBadgeDrawable ();

if ( badge == null ) {

return null ;

}

final int size = mContext . getResources (). getDimensionPixelSize (

R . dimen . notification_badge_size );

Bitmap bitmap = Bitmap . createBitmap ( size , size , Bitmap . Config . ARGB_8888 );

Canvas canvas = new Canvas ( bitmap );

badge . setBounds ( 0 , 0 , size , size );

badge . draw ( canvas );

return bitmap ;

}

private boolean addProfileBadge ( RemoteViews contentView , int resId ) {

Bitmap profileBadge = getProfileBadge ();

contentView . setViewVisibility ( R . id . profile_badge_large_template , View . GONE );

contentView . setViewVisibility ( R . id . profile_badge_line3 , View . GONE );

if ( profileBadge != null ) {

contentView . setImageViewBitmap ( resId , profileBadge );

contentView . setViewVisibility ( resId , View . VISIBLE );

// Make sure Line 3 is visible. As badge will be here if there

// is no text to display.

if ( resId == R . id . profile_badge_line3 ) {

contentView . setViewVisibility ( R . id . line3 , View . VISIBLE );

}

return true ;

}

return false ;

}

private void resetStandardTemplate ( RemoteViews contentView ) {

resetNotificationHeader ( contentView );

resetContentMargins ( contentView );

contentView . setViewVisibility ( R . id . right_icon , View . GONE );

contentView . setTextViewText ( R . id . title , null );

contentView . setTextViewText ( R . id . text , null );

contentView . setViewVisibility ( R . id . line3 , View . GONE );

contentView . setViewVisibility ( R . id . text_line_1 , View . GONE );

contentView . setViewVisibility ( R . id . progress , View . GONE );

}

/**

* Resets the notification header to its original state

*/

private void resetNotificationHeader ( RemoteViews contentView ) {

contentView . setImageViewResource ( R . id . icon , 0 );

contentView . setBoolean ( R . id . notification_header , "setExpanded" , false );

contentView . setTextViewText ( R . id . app_name_text , null );

contentView . setViewVisibility ( R . id . chronometer , View . GONE );

contentView . setViewVisibility ( R . id . header_sub_text , View . GONE );

contentView . setViewVisibility ( R . id . header_content_info , View . GONE );

contentView . setViewVisibility ( R . id . number_of_children , View . GONE );

contentView . setViewVisibility ( R . id . sub_text_divider , View . GONE );

contentView . setViewVisibility ( R . id . content_info_divider , View . GONE );

contentView . setViewVisibility ( R . id . time_divider , View . GONE );

}

private void resetContentMargins ( RemoteViews contentView ) {

contentView . setViewLayoutMarginEnd ( R . id . line1 , 0 );

contentView . setViewLayoutMarginEnd ( R . id . line3 , 0 );

}

private RemoteViews applyStandardTemplate ( int resId ) {

return applyStandardTemplate ( resId , true /* hasProgress */ );

}

/**

* @param hasProgress whether the progress bar should be shown and set

*/

private RemoteViews applyStandardTemplate ( int resId , boolean hasProgress ) {

RemoteViews contentView = new BuilderRemoteViews ( mContext . getApplicationInfo (), resId );

resetStandardTemplate ( contentView );

boolean showLine3 = false ;

final Bundle ex = mN . extras ;

bindNotificationHeader ( contentView );

bindLargeIcon ( contentView );

if ( ex . getCharSequence ( EXTRA_TITLE ) != null ) {

contentView . setTextViewText ( R . id . title ,

processLegacyText ( ex . getCharSequence ( EXTRA_TITLE )));

}

boolean showProgress = handleProgressBar ( hasProgress , contentView , ex );

if ( ex . getCharSequence ( EXTRA_TEXT ) != null ) {

contentView . setTextViewText ( showProgress ? R . id . text_line_1 : R . id . text ,

processLegacyText ( ex . getCharSequence ( EXTRA_TEXT )));

if ( showProgress ) {

contentView . setViewVisibility ( R . id . text_line_1 , View . VISIBLE );

}

showLine3 = ! showProgress ;

}

// We want to add badge to first line of text.

if ( addProfileBadge ( contentView , R . id . profile_badge_line3 )) {

showLine3 = true ;

}

// Note getStandardView may hide line 3 again.

contentView . setViewVisibility ( R . id . line3 , showLine3 ? View . VISIBLE : View . GONE );

return contentView ;

}

private boolean handleProgressBar ( boolean hasProgress , RemoteViews contentView , Bundle ex ) {

final int max = ex . getInt ( EXTRA_PROGRESS_MAX , 0 );

final int progress = ex . getInt ( EXTRA_PROGRESS , 0 );

final boolean ind = ex . getBoolean ( EXTRA_PROGRESS_INDETERMINATE );

if ( hasProgress && ( max != 0 || ind )) {

contentView . setViewVisibility ( com . android . internal . R . id . progress , View . VISIBLE );

contentView . setProgressBar (

R . id . progress , max , progress , ind );

contentView . setProgressBackgroundTintList (

R . id . progress , ColorStateList . valueOf ( mContext . getColor (

R . color . notification_progress_background_color )));

if ( mN . color != COLOR_DEFAULT ) {

ColorStateList colorStateList = ColorStateList . valueOf ( mN . color );

contentView . setProgressTintList ( R . id . progress , colorStateList );

contentView . setProgressIndeterminateTintList ( R . id . progress , colorStateList );

}

return true ;

} else {

contentView . setViewVisibility ( R . id . progress , View . GONE );

return false ;

}

}

private void bindLargeIcon ( RemoteViews contentView ) {

if ( mN . mLargeIcon != null ) {

contentView . setViewVisibility ( R . id . right_icon , View . VISIBLE );

contentView . setImageViewIcon ( R . id . right_icon , mN . mLargeIcon );

processLargeLegacyIcon ( mN . mLargeIcon , contentView );

int endMargin = mContext . getResources (). getDimensionPixelSize (

R . dimen . notification_content_picture_margin );

contentView . setViewLayoutMarginEnd ( R . id . line1 , endMargin );

contentView . setViewLayoutMarginEnd ( R . id . line3 , endMargin );

contentView . setViewLayoutMarginEnd ( R . id . progress , endMargin );

}

}

private void bindNotificationHeader ( RemoteViews contentView ) {

bindSmallIcon ( contentView );

bindChildCountColor ( contentView );

bindHeaderAppName ( contentView );

bindHeaderSubText ( contentView );

bindContentInfo ( contentView );

bindHeaderChronometerAndTime ( contentView );

bindExpandButton ( contentView );

}

private void bindChildCountColor ( RemoteViews contentView ) {

contentView . setTextColor ( R . id . number_of_children , resolveColor ());

}

private void bindContentInfo ( RemoteViews contentView ) {

boolean visible = false ;

if ( mN . extras . getCharSequence ( EXTRA_INFO_TEXT ) != null ) {

contentView . setTextViewText ( R . id . header_content_info ,

processLegacyText ( mN . extras . getCharSequence ( EXTRA_INFO_TEXT )));

contentView . setViewVisibility ( R . id . header_content_info , View . VISIBLE );

visible = true ;

} else if ( mN . number > 0 ) {

final int tooBig = mContext . getResources (). getInteger (

R . integer . status_bar_notification_info_maxnum );

if ( mN . number > tooBig ) {

contentView . setTextViewText ( R . id . header_content_info , processLegacyText (

mContext . getResources (). getString (

R . string . status_bar_notification_info_overflow )));

} else {

contentView . setTextViewText ( R . id . header_content_info ,

processLegacyText ( String . valueOf ( mN . number )));

}

contentView . setViewVisibility ( R . id . header_content_info , View . VISIBLE );

visible = true ;

}

if ( visible ) {

contentView . setViewVisibility ( R . id . content_info_divider , View . VISIBLE );

}

}

private void bindExpandButton ( RemoteViews contentView ) {

contentView . setDrawableParameters ( R . id . expand_button , false , - 1 , resolveColor (),

PorterDuff . Mode . SRC_ATOP , - 1 );

contentView . setInt ( R . id . notification_header , "setOriginalNotificationColor" ,

resolveColor ());

}

private void bindHeaderChronometerAndTime ( RemoteViews contentView ) {

if ( showsTimeOrChronometer ()) {

contentView . setViewVisibility ( R . id . time_divider , View . VISIBLE );

if ( mN . extras . getBoolean ( EXTRA_SHOW_CHRONOMETER )) {

contentView . setViewVisibility ( R . id . chronometer , View . VISIBLE );

contentView . setLong ( R . id . chronometer , "setBase" ,

mN . when + ( SystemClock . elapsedRealtime () - System . currentTimeMillis ()));

contentView . setBoolean ( R . id . chronometer , "setStarted" , true );

} else {

contentView . setViewVisibility ( R . id . time , View . VISIBLE );

contentView . setLong ( R . id . time , "setTime" , mN . when );

}

}

}

private void bindHeaderSubText ( RemoteViews contentView ) {

CharSequence subText = mN . extras . getCharSequence ( EXTRA_SUB_TEXT );

if ( subText == null && mStyle != null && mStyle . mSummaryTextSet

&& mStyle . hasSummaryInHeader ()) {

subText = mStyle . mSummaryText ;

}

if ( subText != null ) {

// TODO: Remove the span entirely to only have the string with propper formating.

contentView . setTextViewText ( R . id . header_sub_text , processLegacyText ( subText ));

contentView . setViewVisibility ( R . id . header_sub_text , View . VISIBLE );

contentView . setViewVisibility ( R . id . sub_text_divider , View . VISIBLE );

}

}

private void bindHeaderAppName ( RemoteViews contentView ) {

PackageManager packageManager = mContext . getPackageManager ();

ApplicationInfo info = null ;

try {

info = packageManager . getApplicationInfo ( mContext . getApplicationInfo (). packageName ,

0 );

} catch ( final NameNotFoundException e ) {

return ;

}

CharSequence appName = info != null ? packageManager . getApplicationLabel ( info )

: null ;

if ( TextUtils . isEmpty ( appName )) {

return ;

}

contentView . setTextViewText ( R . id . app_name_text , appName );

}

private void bindSmallIcon ( RemoteViews contentView ) {

contentView . setImageViewIcon ( R . id . icon , mN . mSmallIcon );

processSmallIconColor ( mN . mSmallIcon , contentView );

}

/**

* @return true if the built notification will show the time or the chronometer; false

* otherwise

*/

private boolean