Notification.java revision 61a9e8635f04e5bae5f4ee81579beb9d5baf56d0
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.app; 18 19import android.annotation.IntDef; 20import android.content.Context; 21import android.content.Intent; 22import android.content.res.Resources; 23import android.graphics.Bitmap; 24import android.graphics.PorterDuff; 25import android.media.AudioManager; 26import android.media.session.MediaSessionToken; 27import android.net.Uri; 28import android.os.BadParcelableException; 29import android.os.Build; 30import android.os.Bundle; 31import android.os.Parcel; 32import android.os.Parcelable; 33import android.os.SystemClock; 34import android.os.UserHandle; 35import android.text.TextUtils; 36import android.util.Log; 37import android.util.TypedValue; 38import android.view.Gravity; 39import android.view.View; 40import android.widget.ProgressBar; 41import android.widget.RemoteViews; 42 43import com.android.internal.R; 44import com.android.internal.util.NotificationColorUtil; 45 46import java.lang.annotation.Retention; 47import java.lang.annotation.RetentionPolicy; 48import java.text.NumberFormat; 49import java.util.ArrayList; 50import java.util.Arrays; 51import java.util.Collections; 52import java.util.List; 53 54/** 55 * A class that represents how a persistent notification is to be presented to 56 * the user using the {@link android.app.NotificationManager}. 57 * 58 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it 59 * easier to construct Notifications.</p> 60 * 61 * <div class="special reference"> 62 * <h3>Developer Guides</h3> 63 * <p>For a guide to creating notifications, read the 64 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a> 65 * developer guide.</p> 66 * </div> 67 */ 68public class Notification implements Parcelable 69{ 70 private static final String TAG = "Notification"; 71 72 /** 73 * Use all default values (where applicable). 74 */ 75 public static final int DEFAULT_ALL = ~0; 76 77 /** 78 * Use the default notification sound. This will ignore any given 79 * {@link #sound}. 80 * 81 82 * @see #defaults 83 */ 84 85 public static final int DEFAULT_SOUND = 1; 86 87 /** 88 * Use the default notification vibrate. This will ignore any given 89 * {@link #vibrate}. Using phone vibration requires the 90 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission. 91 * 92 * @see #defaults 93 */ 94 95 public static final int DEFAULT_VIBRATE = 2; 96 97 /** 98 * Use the default notification lights. This will ignore the 99 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or 100 * {@link #ledOnMS}. 101 * 102 * @see #defaults 103 */ 104 105 public static final int DEFAULT_LIGHTS = 4; 106 107 /** 108 * A timestamp related to this notification, in milliseconds since the epoch. 109 * 110 * Default value: {@link System#currentTimeMillis() Now}. 111 * 112 * Choose a timestamp that will be most relevant to the user. For most finite events, this 113 * corresponds to the time the event happened (or will happen, in the case of events that have 114 * yet to occur but about which the user is being informed). Indefinite events should be 115 * timestamped according to when the activity began. 116 * 117 * Some examples: 118 * 119 * <ul> 120 * <li>Notification of a new chat message should be stamped when the message was received.</li> 121 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li> 122 * <li>Notification of a completed file download should be stamped when the download finished.</li> 123 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li> 124 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time. 125 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time. 126 * </ul> 127 * 128 */ 129 public long when; 130 131 /** 132 * The resource id of a drawable to use as the icon in the status bar. 133 * This is required; notifications with an invalid icon resource will not be shown. 134 */ 135 public int icon; 136 137 /** 138 * If the icon in the status bar is to have more than one level, you can set this. Otherwise, 139 * leave it at its default value of 0. 140 * 141 * @see android.widget.ImageView#setImageLevel 142 * @see android.graphics.drawable.Drawable#setLevel 143 */ 144 public int iconLevel; 145 146 /** 147 * The number of events that this notification represents. For example, in a new mail 148 * notification, this could be the number of unread messages. 149 * 150 * The system may or may not use this field to modify the appearance of the notification. For 151 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was 152 * superimposed over the icon in the status bar. Starting with 153 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by 154 * {@link Notification.Builder} has displayed the number in the expanded notification view. 155 * 156 * If the number is 0 or negative, it is never shown. 157 */ 158 public int number; 159 160 /** 161 * The intent to execute when the expanded status entry is clicked. If 162 * this is an activity, it must include the 163 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires 164 * that you take care of task management as described in the 165 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back 166 * Stack</a> document. In particular, make sure to read the notification section 167 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling 168 * Notifications</a> for the correct ways to launch an application from a 169 * notification. 170 */ 171 public PendingIntent contentIntent; 172 173 /** 174 * The intent to execute when the notification is explicitly dismissed by the user, either with 175 * the "Clear All" button or by swiping it away individually. 176 * 177 * This probably shouldn't be launching an activity since several of those will be sent 178 * at the same time. 179 */ 180 public PendingIntent deleteIntent; 181 182 /** 183 * An intent to launch instead of posting the notification to the status bar. 184 * 185 * @see Notification.Builder#setFullScreenIntent 186 */ 187 public PendingIntent fullScreenIntent; 188 189 /** 190 * Text to scroll across the screen when this item is added to 191 * the status bar on large and smaller devices. 192 * 193 * @see #tickerView 194 */ 195 public CharSequence tickerText; 196 197 /** 198 * The view to show as the ticker in the status bar when the notification 199 * is posted. 200 */ 201 public RemoteViews tickerView; 202 203 /** 204 * The view that will represent this notification in the expanded status bar. 205 */ 206 public RemoteViews contentView; 207 208 /** 209 * A large-format version of {@link #contentView}, giving the Notification an 210 * opportunity to show more detail. The system UI may choose to show this 211 * instead of the normal content view at its discretion. 212 */ 213 public RemoteViews bigContentView; 214 215 216 /** 217 * @hide 218 * A medium-format version of {@link #contentView}, giving the Notification an 219 * opportunity to add action buttons to contentView. The system UI may 220 * choose to show this as a popup notification at its discretion. 221 */ 222 public RemoteViews headsUpContentView; 223 224 /** 225 * The bitmap that may escape the bounds of the panel and bar. 226 */ 227 public Bitmap largeIcon; 228 229 /** 230 * The sound to play. 231 * 232 * <p> 233 * To play the default notification sound, see {@link #defaults}. 234 * </p> 235 */ 236 public Uri sound; 237 238 /** 239 * Use this constant as the value for audioStreamType to request that 240 * the default stream type for notifications be used. Currently the 241 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}. 242 */ 243 public static final int STREAM_DEFAULT = -1; 244 245 /** 246 * The audio stream type to use when playing the sound. 247 * Should be one of the STREAM_ constants from 248 * {@link android.media.AudioManager}. 249 */ 250 public int audioStreamType = STREAM_DEFAULT; 251 252 /** 253 * The pattern with which to vibrate. 254 * 255 * <p> 256 * To vibrate the default pattern, see {@link #defaults}. 257 * </p> 258 * 259 * @see android.os.Vibrator#vibrate(long[],int) 260 */ 261 public long[] vibrate; 262 263 /** 264 * The color of the led. The hardware will do its best approximation. 265 * 266 * @see #FLAG_SHOW_LIGHTS 267 * @see #flags 268 */ 269 public int ledARGB; 270 271 /** 272 * The number of milliseconds for the LED to be on while it's flashing. 273 * The hardware will do its best approximation. 274 * 275 * @see #FLAG_SHOW_LIGHTS 276 * @see #flags 277 */ 278 public int ledOnMS; 279 280 /** 281 * The number of milliseconds for the LED to be off while it's flashing. 282 * The hardware will do its best approximation. 283 * 284 * @see #FLAG_SHOW_LIGHTS 285 * @see #flags 286 */ 287 public int ledOffMS; 288 289 /** 290 * Specifies which values should be taken from the defaults. 291 * <p> 292 * To set, OR the desired from {@link #DEFAULT_SOUND}, 293 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default 294 * values, use {@link #DEFAULT_ALL}. 295 * </p> 296 */ 297 public int defaults; 298 299 /** 300 * Bit to be bitwise-ored into the {@link #flags} field that should be 301 * set if you want the LED on for this notification. 302 * <ul> 303 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB 304 * or 0 for both ledOnMS and ledOffMS.</li> 305 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li> 306 * <li>To flash the LED, pass the number of milliseconds that it should 307 * be on and off to ledOnMS and ledOffMS.</li> 308 * </ul> 309 * <p> 310 * Since hardware varies, you are not guaranteed that any of the values 311 * you pass are honored exactly. Use the system defaults (TODO) if possible 312 * because they will be set to values that work on any given hardware. 313 * <p> 314 * The alpha channel must be set for forward compatibility. 315 * 316 */ 317 public static final int FLAG_SHOW_LIGHTS = 0x00000001; 318 319 /** 320 * Bit to be bitwise-ored into the {@link #flags} field that should be 321 * set if this notification is in reference to something that is ongoing, 322 * like a phone call. It should not be set if this notification is in 323 * reference to something that happened at a particular point in time, 324 * like a missed phone call. 325 */ 326 public static final int FLAG_ONGOING_EVENT = 0x00000002; 327 328 /** 329 * Bit to be bitwise-ored into the {@link #flags} field that if set, 330 * the audio will be repeated until the notification is 331 * cancelled or the notification window is opened. 332 */ 333 public static final int FLAG_INSISTENT = 0x00000004; 334 335 /** 336 * Bit to be bitwise-ored into the {@link #flags} field that should be 337 * set if you would only like the sound, vibrate and ticker to be played 338 * if the notification was not already showing. 339 */ 340 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008; 341 342 /** 343 * Bit to be bitwise-ored into the {@link #flags} field that should be 344 * set if the notification should be canceled when it is clicked by the 345 * user. 346 347 */ 348 public static final int FLAG_AUTO_CANCEL = 0x00000010; 349 350 /** 351 * Bit to be bitwise-ored into the {@link #flags} field that should be 352 * set if the notification should not be canceled when the user clicks 353 * the Clear all button. 354 */ 355 public static final int FLAG_NO_CLEAR = 0x00000020; 356 357 /** 358 * Bit to be bitwise-ored into the {@link #flags} field that should be 359 * set if this notification represents a currently running service. This 360 * will normally be set for you by {@link Service#startForeground}. 361 */ 362 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040; 363 364 /** 365 * Obsolete flag indicating high-priority notifications; use the priority field instead. 366 * 367 * @deprecated Use {@link #priority} with a positive value. 368 */ 369 public static final int FLAG_HIGH_PRIORITY = 0x00000080; 370 371 /** 372 * Bit to be bitswise-ored into the {@link #flags} field that should be 373 * set if this notification is relevant to the current device only 374 * and it is not recommended that it bridge to other devices. 375 */ 376 public static final int FLAG_LOCAL_ONLY = 0x00000100; 377 378 /** 379 * Bit to be bitswise-ored into the {@link #flags} field that should be 380 * set if this notification is the group summary for a group of notifications. 381 * Grouped notifications may display in a cluster or stack on devices which 382 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}. 383 */ 384 public static final int FLAG_GROUP_SUMMARY = 0x00000200; 385 386 public int flags; 387 388 /** @hide */ 389 @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX}) 390 @Retention(RetentionPolicy.SOURCE) 391 public @interface Priority {} 392 393 /** 394 * Default notification {@link #priority}. If your application does not prioritize its own 395 * notifications, use this value for all notifications. 396 */ 397 public static final int PRIORITY_DEFAULT = 0; 398 399 /** 400 * Lower {@link #priority}, for items that are less important. The UI may choose to show these 401 * items smaller, or at a different position in the list, compared with your app's 402 * {@link #PRIORITY_DEFAULT} items. 403 */ 404 public static final int PRIORITY_LOW = -1; 405 406 /** 407 * Lowest {@link #priority}; these items might not be shown to the user except under special 408 * circumstances, such as detailed notification logs. 409 */ 410 public static final int PRIORITY_MIN = -2; 411 412 /** 413 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to 414 * show these items larger, or at a different position in notification lists, compared with 415 * your app's {@link #PRIORITY_DEFAULT} items. 416 */ 417 public static final int PRIORITY_HIGH = 1; 418 419 /** 420 * Highest {@link #priority}, for your application's most important items that require the 421 * user's prompt attention or input. 422 */ 423 public static final int PRIORITY_MAX = 2; 424 425 /** 426 * Relative priority for this notification. 427 * 428 * Priority is an indication of how much of the user's valuable attention should be consumed by 429 * this notification. Low-priority notifications may be hidden from the user in certain 430 * situations, while the user might be interrupted for a higher-priority notification. The 431 * system will make a determination about how to interpret this priority when presenting 432 * the notification. 433 */ 434 @Priority 435 public int priority; 436 437 /** 438 * Accent color (an ARGB integer like the constants in {@link android.graphics.Color}) 439 * to be applied by the standard Style templates when presenting this notification. 440 * 441 * The current template design constructs a colorful header image by overlaying the 442 * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are 443 * ignored. 444 */ 445 public int color = COLOR_DEFAULT; 446 447 /** 448 * Special value of {@link #color} telling the system not to decorate this notification with 449 * any special color but instead use default colors when presenting this notification. 450 */ 451 public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT 452 453 /** 454 * Sphere of visibility of this notification, which affects how and when the SystemUI reveals 455 * the notification's presence and contents in untrusted situations (namely, on the secure 456 * lockscreen). 457 * 458 * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always 459 * done on Android: The notification's {@link #icon} and {@link #tickerText} (if available) are 460 * shown in all situations, but the contents are only available if the device is unlocked for 461 * the appropriate user. 462 * 463 * A more permissive policy can be expressed by {@link #VISIBILITY_PUBLIC}; such a notification 464 * can be read even in an "insecure" context (that is, above a secure lockscreen). 465 * To modify the public version of this notification—for example, to redact some portions—see 466 * {@link Builder#setPublicVersion(Notification)}. 467 * 468 * Finally, a notification can be made {@link #VISIBILITY_SECRET}, which will suppress its icon 469 * and ticker until the user has bypassed the lockscreen. 470 */ 471 public int visibility; 472 473 public static final int VISIBILITY_PUBLIC = 1; 474 public static final int VISIBILITY_PRIVATE = 0; 475 public static final int VISIBILITY_SECRET = -1; 476 477 /** 478 * Notification category: incoming call (voice or video) or similar synchronous communication request. 479 */ 480 public static final String CATEGORY_CALL = "call"; 481 482 /** 483 * Notification category: incoming direct message (SMS, instant message, etc.). 484 */ 485 public static final String CATEGORY_MESSAGE = "msg"; 486 487 /** 488 * Notification category: asynchronous bulk message (email). 489 */ 490 public static final String CATEGORY_EMAIL = "email"; 491 492 /** 493 * Notification category: calendar event. 494 */ 495 public static final String CATEGORY_EVENT = "event"; 496 497 /** 498 * Notification category: promotion or advertisement. 499 */ 500 public static final String CATEGORY_PROMO = "promo"; 501 502 /** 503 * Notification category: alarm or timer. 504 */ 505 public static final String CATEGORY_ALARM = "alarm"; 506 507 /** 508 * Notification category: progress of a long-running background operation. 509 */ 510 public static final String CATEGORY_PROGRESS = "progress"; 511 512 /** 513 * Notification category: social network or sharing update. 514 */ 515 public static final String CATEGORY_SOCIAL = "social"; 516 517 /** 518 * Notification category: error in background operation or authentication status. 519 */ 520 public static final String CATEGORY_ERROR = "err"; 521 522 /** 523 * Notification category: media transport control for playback. 524 */ 525 public static final String CATEGORY_TRANSPORT = "transport"; 526 527 /** 528 * Notification category: system or device status update. Reserved for system use. 529 */ 530 public static final String CATEGORY_SYSTEM = "sys"; 531 532 /** 533 * Notification category: indication of running background service. 534 */ 535 public static final String CATEGORY_SERVICE = "service"; 536 537 /** 538 * Notification category: a specific, timely recommendation for a single thing. 539 * For example, a news app might want to recommend a news story it believes the user will 540 * want to read next. 541 */ 542 public static final String CATEGORY_RECOMMENDATION = "recommendation"; 543 544 /** 545 * Notification category: ongoing information about device or contextual status. 546 */ 547 public static final String CATEGORY_STATUS = "status"; 548 549 /** 550 * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants) 551 * that best describes this Notification. May be used by the system for ranking and filtering. 552 */ 553 public String category; 554 555 private String mGroupKey; 556 557 /** 558 * Get the key used to group this notification into a cluster or stack 559 * with other notifications on devices which support such rendering. 560 */ 561 public String getGroup() { 562 return mGroupKey; 563 } 564 565 private String mSortKey; 566 567 /** 568 * Get a sort key that orders this notification among other notifications from the 569 * same package. This can be useful if an external sort was already applied and an app 570 * would like to preserve this. Notifications will be sorted lexicographically using this 571 * value, although providing different priorities in addition to providing sort key may 572 * cause this value to be ignored. 573 * 574 * <p>This sort key can also be used to order members of a notification group. See 575 * {@link Builder#setGroup}. 576 * 577 * @see String#compareTo(String) 578 */ 579 public String getSortKey() { 580 return mSortKey; 581 } 582 583 /** 584 * Additional semantic data to be carried around with this Notification. 585 * <p> 586 * The extras keys defined here are intended to capture the original inputs to {@link Builder} 587 * APIs, and are intended to be used by 588 * {@link android.service.notification.NotificationListenerService} implementations to extract 589 * detailed information from notification objects. 590 */ 591 public Bundle extras = new Bundle(); 592 593 /** 594 * {@link #extras} key: this is the title of the notification, 595 * as supplied to {@link Builder#setContentTitle(CharSequence)}. 596 */ 597 public static final String EXTRA_TITLE = "android.title"; 598 599 /** 600 * {@link #extras} key: this is the title of the notification when shown in expanded form, 601 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}. 602 */ 603 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big"; 604 605 /** 606 * {@link #extras} key: this is the main text payload, as supplied to 607 * {@link Builder#setContentText(CharSequence)}. 608 */ 609 public static final String EXTRA_TEXT = "android.text"; 610 611 /** 612 * {@link #extras} key: this is a third line of text, as supplied to 613 * {@link Builder#setSubText(CharSequence)}. 614 */ 615 public static final String EXTRA_SUB_TEXT = "android.subText"; 616 617 /** 618 * {@link #extras} key: this is a small piece of additional text as supplied to 619 * {@link Builder#setContentInfo(CharSequence)}. 620 */ 621 public static final String EXTRA_INFO_TEXT = "android.infoText"; 622 623 /** 624 * {@link #extras} key: this is a line of summary information intended to be shown 625 * alongside expanded notifications, as supplied to (e.g.) 626 * {@link BigTextStyle#setSummaryText(CharSequence)}. 627 */ 628 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText"; 629 630 /** 631 * {@link #extras} key: this is the resource ID of the notification's main small icon, as 632 * supplied to {@link Builder#setSmallIcon(int)}. 633 */ 634 public static final String EXTRA_SMALL_ICON = "android.icon"; 635 636 /** 637 * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the 638 * notification payload, as 639 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}. 640 */ 641 public static final String EXTRA_LARGE_ICON = "android.largeIcon"; 642 643 /** 644 * {@link #extras} key: this is a bitmap to be used instead of the one from 645 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is 646 * shown in its expanded form, as supplied to 647 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}. 648 */ 649 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big"; 650 651 /** 652 * {@link #extras} key: this is the progress value supplied to 653 * {@link Builder#setProgress(int, int, boolean)}. 654 */ 655 public static final String EXTRA_PROGRESS = "android.progress"; 656 657 /** 658 * {@link #extras} key: this is the maximum value supplied to 659 * {@link Builder#setProgress(int, int, boolean)}. 660 */ 661 public static final String EXTRA_PROGRESS_MAX = "android.progressMax"; 662 663 /** 664 * {@link #extras} key: whether the progress bar is indeterminate, supplied to 665 * {@link Builder#setProgress(int, int, boolean)}. 666 */ 667 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate"; 668 669 /** 670 * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically 671 * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to 672 * {@link Builder#setUsesChronometer(boolean)}. 673 */ 674 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer"; 675 676 /** 677 * {@link #extras} key: whether {@link #when} should be shown, 678 * as supplied to {@link Builder#setShowWhen(boolean)}. 679 */ 680 public static final String EXTRA_SHOW_WHEN = "android.showWhen"; 681 682 /** 683 * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded 684 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}. 685 */ 686 public static final String EXTRA_PICTURE = "android.picture"; 687 688 /** 689 * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded 690 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}. 691 */ 692 public static final String EXTRA_TEXT_LINES = "android.textLines"; 693 694 /** 695 * {@link #extras} key: A string representing the name of the specific 696 * {@link android.app.Notification.Style} used to create this notification. 697 */ 698 public static final String EXTRA_TEMPLATE = "android.template"; 699 700 /** 701 * {@link #extras} key: An array of people that this notification relates to, specified 702 * by contacts provider contact URI. 703 */ 704 public static final String EXTRA_PEOPLE = "android.people"; 705 706 /** 707 * @hide 708 * Extra added by NotificationManagerService to indicate whether 709 * the Notifications's score has been modified. 710 */ 711 public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified"; 712 713 /** 714 * Not used. 715 * @hide 716 */ 717 public static final String EXTRA_AS_HEADS_UP = "headsup"; 718 719 /** 720 * Allow certain system-generated notifications to appear before the device is provisioned. 721 * Only available to notifications coming from the android package. 722 * @hide 723 */ 724 public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup"; 725 726 /** 727 * {@link #extras} key: A 728 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed 729 * in the background when the notification is selected. The URI must point to an image stream 730 * suitable for passing into 731 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream) 732 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider 733 * URI used for this purpose must require no permissions to read the image data. 734 */ 735 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; 736 737 /** 738 * {@link #extras} key: A 739 * {@link android.media.session.MediaSessionToken} associated with a 740 * {@link android.app.Notification.MediaStyle} notification. 741 */ 742 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession"; 743 744 /** 745 * Value for {@link #EXTRA_AS_HEADS_UP}. 746 * @hide 747 */ 748 public static final int HEADS_UP_NEVER = 0; 749 750 /** 751 * Default value for {@link #EXTRA_AS_HEADS_UP}. 752 * @hide 753 */ 754 public static final int HEADS_UP_ALLOWED = 1; 755 756 /** 757 * Value for {@link #EXTRA_AS_HEADS_UP}. 758 * @hide 759 */ 760 public static final int HEADS_UP_REQUESTED = 2; 761 762 /** 763 * Structure to encapsulate a named action that can be shown as part of this notification. 764 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is 765 * selected by the user. 766 * <p> 767 * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)} 768 * or {@link Notification.Builder#addAction(Notification.Action)} 769 * to attach actions. 770 */ 771 public static class Action implements Parcelable { 772 private final Bundle mExtras; 773 private final RemoteInput[] mRemoteInputs; 774 775 /** 776 * Small icon representing the action. 777 */ 778 public int icon; 779 780 /** 781 * Title of the action. 782 */ 783 public CharSequence title; 784 785 /** 786 * Intent to send when the user invokes this action. May be null, in which case the action 787 * may be rendered in a disabled presentation by the system UI. 788 */ 789 public PendingIntent actionIntent; 790 791 private Action(Parcel in) { 792 icon = in.readInt(); 793 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 794 if (in.readInt() == 1) { 795 actionIntent = PendingIntent.CREATOR.createFromParcel(in); 796 } 797 mExtras = in.readBundle(); 798 mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR); 799 } 800 801 /** 802 * Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}. 803 */ 804 public Action(int icon, CharSequence title, PendingIntent intent) { 805 this(icon, title, intent, new Bundle(), null); 806 } 807 808 private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras, 809 RemoteInput[] remoteInputs) { 810 this.icon = icon; 811 this.title = title; 812 this.actionIntent = intent; 813 this.mExtras = extras != null ? extras : new Bundle(); 814 this.mRemoteInputs = remoteInputs; 815 } 816 817 /** 818 * Get additional metadata carried around with this Action. 819 */ 820 public Bundle getExtras() { 821 return mExtras; 822 } 823 824 /** 825 * Get the list of inputs to be collected from the user when this action is sent. 826 * May return null if no remote inputs were added. 827 */ 828 public RemoteInput[] getRemoteInputs() { 829 return mRemoteInputs; 830 } 831 832 /** 833 * Builder class for {@link Action} objects. 834 */ 835 public static final class Builder { 836 private final int mIcon; 837 private final CharSequence mTitle; 838 private final PendingIntent mIntent; 839 private final Bundle mExtras; 840 private ArrayList<RemoteInput> mRemoteInputs; 841 842 /** 843 * Construct a new builder for {@link Action} object. 844 * @param icon icon to show for this action 845 * @param title the title of the action 846 * @param intent the {@link PendingIntent} to fire when users trigger this action 847 */ 848 public Builder(int icon, CharSequence title, PendingIntent intent) { 849 this(icon, title, intent, new Bundle(), null); 850 } 851 852 /** 853 * Construct a new builder for {@link Action} object using the fields from an 854 * {@link Action}. 855 * @param action the action to read fields from. 856 */ 857 public Builder(Action action) { 858 this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras), 859 action.getRemoteInputs()); 860 } 861 862 private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras, 863 RemoteInput[] remoteInputs) { 864 mIcon = icon; 865 mTitle = title; 866 mIntent = intent; 867 mExtras = extras; 868 if (remoteInputs != null) { 869 mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length); 870 Collections.addAll(mRemoteInputs, remoteInputs); 871 } 872 } 873 874 /** 875 * Merge additional metadata into this builder. 876 * 877 * <p>Values within the Bundle will replace existing extras values in this Builder. 878 * 879 * @see Notification.Action#extras 880 */ 881 public Builder addExtras(Bundle extras) { 882 if (extras != null) { 883 mExtras.putAll(extras); 884 } 885 return this; 886 } 887 888 /** 889 * Get the metadata Bundle used by this Builder. 890 * 891 * <p>The returned Bundle is shared with this Builder. 892 */ 893 public Bundle getExtras() { 894 return mExtras; 895 } 896 897 /** 898 * Add an input to be collected from the user when this action is sent. 899 * Response values can be retrieved from the fired intent by using the 900 * {@link RemoteInput#getResultsFromIntent} function. 901 * @param remoteInput a {@link RemoteInput} to add to the action 902 * @return this object for method chaining 903 */ 904 public Builder addRemoteInput(RemoteInput remoteInput) { 905 if (mRemoteInputs == null) { 906 mRemoteInputs = new ArrayList<RemoteInput>(); 907 } 908 mRemoteInputs.add(remoteInput); 909 return this; 910 } 911 912 /** 913 * Apply an extender to this action builder. Extenders may be used to add 914 * metadata or change options on this builder. 915 */ 916 public Builder extend(Extender extender) { 917 extender.extend(this); 918 return this; 919 } 920 921 /** 922 * Combine all of the options that have been set and return a new {@link Action} 923 * object. 924 * @return the built action 925 */ 926 public Action build() { 927 RemoteInput[] remoteInputs = mRemoteInputs != null 928 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null; 929 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs); 930 } 931 } 932 933 @Override 934 public Action clone() { 935 return new Action( 936 icon, 937 title, 938 actionIntent, // safe to alias 939 new Bundle(mExtras), 940 getRemoteInputs()); 941 } 942 @Override 943 public int describeContents() { 944 return 0; 945 } 946 @Override 947 public void writeToParcel(Parcel out, int flags) { 948 out.writeInt(icon); 949 TextUtils.writeToParcel(title, out, flags); 950 if (actionIntent != null) { 951 out.writeInt(1); 952 actionIntent.writeToParcel(out, flags); 953 } else { 954 out.writeInt(0); 955 } 956 out.writeBundle(mExtras); 957 out.writeTypedArray(mRemoteInputs, flags); 958 } 959 public static final Parcelable.Creator<Action> CREATOR = 960 new Parcelable.Creator<Action>() { 961 public Action createFromParcel(Parcel in) { 962 return new Action(in); 963 } 964 public Action[] newArray(int size) { 965 return new Action[size]; 966 } 967 }; 968 969 /** 970 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add 971 * metadata or change options on an action builder. 972 */ 973 public interface Extender { 974 /** 975 * Apply this extender to a notification action builder. 976 * @param builder the builder to be modified. 977 * @return the build object for chaining. 978 */ 979 public Builder extend(Builder builder); 980 } 981 982 /** 983 * Wearable extender for notification actions. To add extensions to an action, 984 * create a new {@link android.app.Notification.Action.WearableExtender} object using 985 * the {@code WearableExtender()} constructor and apply it to a 986 * {@link android.app.Notification.Action.Builder} using 987 * {@link android.app.Notification.Action.Builder#extend}. 988 * 989 * <pre class="prettyprint"> 990 * Notification.Action action = new Notification.Action.Builder( 991 * R.drawable.archive_all, "Archive all", actionIntent) 992 * .apply(new Notification.Action.WearableExtender() 993 * .setAvailableOffline(false)) 994 * .build(); 995 * </pre> 996 */ 997 public static final class WearableExtender implements Extender { 998 /** Notification action extra which contains wearable extensions */ 999 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; 1000 1001 private static final String KEY_FLAGS = "flags"; 1002 1003 // Flags bitwise-ored to mFlags 1004 private static final int FLAG_AVAILABLE_OFFLINE = 0x1; 1005 1006 // Default value for flags integer 1007 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE; 1008 1009 private int mFlags = DEFAULT_FLAGS; 1010 1011 /** 1012 * Create a {@link android.app.Notification.Action.WearableExtender} with default 1013 * options. 1014 */ 1015 public WearableExtender() { 1016 } 1017 1018 /** 1019 * Create a {@link android.app.Notification.Action.WearableExtender} by reading 1020 * wearable options present in an existing notification action. 1021 * @param action the notification action to inspect. 1022 */ 1023 public WearableExtender(Action action) { 1024 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS); 1025 if (wearableBundle != null) { 1026 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS); 1027 } 1028 } 1029 1030 /** 1031 * Apply wearable extensions to a notification action that is being built. This is 1032 * typically called by the {@link android.app.Notification.Action.Builder#extend} 1033 * method of {@link android.app.Notification.Action.Builder}. 1034 */ 1035 @Override 1036 public Action.Builder extend(Action.Builder builder) { 1037 Bundle wearableBundle = new Bundle(); 1038 1039 if (mFlags != DEFAULT_FLAGS) { 1040 wearableBundle.putInt(KEY_FLAGS, mFlags); 1041 } 1042 1043 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); 1044 return builder; 1045 } 1046 1047 @Override 1048 public WearableExtender clone() { 1049 WearableExtender that = new WearableExtender(); 1050 that.mFlags = this.mFlags; 1051 return that; 1052 } 1053 1054 /** 1055 * Set whether this action is available when the wearable device is not connected to 1056 * a companion device. The user can still trigger this action when the wearable device is 1057 * offline, but a visual hint will indicate that the action may not be available. 1058 * Defaults to true. 1059 */ 1060 public WearableExtender setAvailableOffline(boolean availableOffline) { 1061 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline); 1062 return this; 1063 } 1064 1065 /** 1066 * Get whether this action is available when the wearable device is not connected to 1067 * a companion device. The user can still trigger this action when the wearable device is 1068 * offline, but a visual hint will indicate that the action may not be available. 1069 * Defaults to true. 1070 */ 1071 public boolean isAvailableOffline() { 1072 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0; 1073 } 1074 1075 private void setFlag(int mask, boolean value) { 1076 if (value) { 1077 mFlags |= mask; 1078 } else { 1079 mFlags &= ~mask; 1080 } 1081 } 1082 } 1083 } 1084 1085 /** 1086 * Array of all {@link Action} structures attached to this notification by 1087 * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of 1088 * {@link android.service.notification.NotificationListenerService} that provide an alternative 1089 * interface for invoking actions. 1090 */ 1091 public Action[] actions; 1092 1093 /** 1094 * Replacement version of this notification whose content will be shown 1095 * in an insecure context such as atop a secure keyguard. See {@link #visibility} 1096 * and {@link #VISIBILITY_PUBLIC}. 1097 */ 1098 public Notification publicVersion; 1099 1100 /** 1101 * Constructs a Notification object with default values. 1102 * You might want to consider using {@link Builder} instead. 1103 */ 1104 public Notification() 1105 { 1106 this.when = System.currentTimeMillis(); 1107 this.priority = PRIORITY_DEFAULT; 1108 } 1109 1110 /** 1111 * @hide 1112 */ 1113 public Notification(Context context, int icon, CharSequence tickerText, long when, 1114 CharSequence contentTitle, CharSequence contentText, Intent contentIntent) 1115 { 1116 this.when = when; 1117 this.icon = icon; 1118 this.tickerText = tickerText; 1119 setLatestEventInfo(context, contentTitle, contentText, 1120 PendingIntent.getActivity(context, 0, contentIntent, 0)); 1121 } 1122 1123 /** 1124 * Constructs a Notification object with the information needed to 1125 * have a status bar icon without the standard expanded view. 1126 * 1127 * @param icon The resource id of the icon to put in the status bar. 1128 * @param tickerText The text that flows by in the status bar when the notification first 1129 * activates. 1130 * @param when The time to show in the time field. In the System.currentTimeMillis 1131 * timebase. 1132 * 1133 * @deprecated Use {@link Builder} instead. 1134 */ 1135 @Deprecated 1136 public Notification(int icon, CharSequence tickerText, long when) 1137 { 1138 this.icon = icon; 1139 this.tickerText = tickerText; 1140 this.when = when; 1141 } 1142 1143 /** 1144 * Unflatten the notification from a parcel. 1145 */ 1146 public Notification(Parcel parcel) 1147 { 1148 int version = parcel.readInt(); 1149 1150 when = parcel.readLong(); 1151 icon = parcel.readInt(); 1152 number = parcel.readInt(); 1153 if (parcel.readInt() != 0) { 1154 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel); 1155 } 1156 if (parcel.readInt() != 0) { 1157 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel); 1158 } 1159 if (parcel.readInt() != 0) { 1160 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 1161 } 1162 if (parcel.readInt() != 0) { 1163 tickerView = RemoteViews.CREATOR.createFromParcel(parcel); 1164 } 1165 if (parcel.readInt() != 0) { 1166 contentView = RemoteViews.CREATOR.createFromParcel(parcel); 1167 } 1168 if (parcel.readInt() != 0) { 1169 largeIcon = Bitmap.CREATOR.createFromParcel(parcel); 1170 } 1171 defaults = parcel.readInt(); 1172 flags = parcel.readInt(); 1173 if (parcel.readInt() != 0) { 1174 sound = Uri.CREATOR.createFromParcel(parcel); 1175 } 1176 1177 audioStreamType = parcel.readInt(); 1178 vibrate = parcel.createLongArray(); 1179 ledARGB = parcel.readInt(); 1180 ledOnMS = parcel.readInt(); 1181 ledOffMS = parcel.readInt(); 1182 iconLevel = parcel.readInt(); 1183 1184 if (parcel.readInt() != 0) { 1185 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel); 1186 } 1187 1188 priority = parcel.readInt(); 1189 1190 category = parcel.readString(); 1191 1192 mGroupKey = parcel.readString(); 1193 1194 mSortKey = parcel.readString(); 1195 1196 extras = parcel.readBundle(); // may be null 1197 1198 actions = parcel.createTypedArray(Action.CREATOR); // may be null 1199 1200 if (parcel.readInt() != 0) { 1201 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel); 1202 } 1203 1204 if (parcel.readInt() != 0) { 1205 headsUpContentView = RemoteViews.CREATOR.createFromParcel(parcel); 1206 } 1207 1208 visibility = parcel.readInt(); 1209 1210 if (parcel.readInt() != 0) { 1211 publicVersion = Notification.CREATOR.createFromParcel(parcel); 1212 } 1213 1214 color = parcel.readInt(); 1215 } 1216 1217 @Override 1218 public Notification clone() { 1219 Notification that = new Notification(); 1220 cloneInto(that, true); 1221 return that; 1222 } 1223 1224 /** 1225 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members 1226 * of this into that. 1227 * @hide 1228 */ 1229 public void cloneInto(Notification that, boolean heavy) { 1230 that.when = this.when; 1231 that.icon = this.icon; 1232 that.number = this.number; 1233 1234 // PendingIntents are global, so there's no reason (or way) to clone them. 1235 that.contentIntent = this.contentIntent; 1236 that.deleteIntent = this.deleteIntent; 1237 that.fullScreenIntent = this.fullScreenIntent; 1238 1239 if (this.tickerText != null) { 1240 that.tickerText = this.tickerText.toString(); 1241 } 1242 if (heavy && this.tickerView != null) { 1243 that.tickerView = this.tickerView.clone(); 1244 } 1245 if (heavy && this.contentView != null) { 1246 that.contentView = this.contentView.clone(); 1247 } 1248 if (heavy && this.largeIcon != null) { 1249 that.largeIcon = Bitmap.createBitmap(this.largeIcon); 1250 } 1251 that.iconLevel = this.iconLevel; 1252 that.sound = this.sound; // android.net.Uri is immutable 1253 that.audioStreamType = this.audioStreamType; 1254 1255 final long[] vibrate = this.vibrate; 1256 if (vibrate != null) { 1257 final int N = vibrate.length; 1258 final long[] vib = that.vibrate = new long[N]; 1259 System.arraycopy(vibrate, 0, vib, 0, N); 1260 } 1261 1262 that.ledARGB = this.ledARGB; 1263 that.ledOnMS = this.ledOnMS; 1264 that.ledOffMS = this.ledOffMS; 1265 that.defaults = this.defaults; 1266 1267 that.flags = this.flags; 1268 1269 that.priority = this.priority; 1270 1271 that.category = this.category; 1272 1273 that.mGroupKey = this.mGroupKey; 1274 1275 that.mSortKey = this.mSortKey; 1276 1277 if (this.extras != null) { 1278 try { 1279 that.extras = new Bundle(this.extras); 1280 // will unparcel 1281 that.extras.size(); 1282 } catch (BadParcelableException e) { 1283 Log.e(TAG, "could not unparcel extras from notification: " + this, e); 1284 that.extras = null; 1285 } 1286 } 1287 1288 if (this.actions != null) { 1289 that.actions = new Action[this.actions.length]; 1290 for(int i=0; i<this.actions.length; i++) { 1291 that.actions[i] = this.actions[i].clone(); 1292 } 1293 } 1294 1295 if (heavy && this.bigContentView != null) { 1296 that.bigContentView = this.bigContentView.clone(); 1297 } 1298 1299 if (heavy && this.headsUpContentView != null) { 1300 that.headsUpContentView = this.headsUpContentView.clone(); 1301 } 1302 1303 that.visibility = this.visibility; 1304 1305 if (this.publicVersion != null) { 1306 that.publicVersion = new Notification(); 1307 this.publicVersion.cloneInto(that.publicVersion, heavy); 1308 } 1309 1310 that.color = this.color; 1311 1312 if (!heavy) { 1313 that.lightenPayload(); // will clean out extras 1314 } 1315 } 1316 1317 /** 1318 * Removes heavyweight parts of the Notification object for archival or for sending to 1319 * listeners when the full contents are not necessary. 1320 * @hide 1321 */ 1322 public final void lightenPayload() { 1323 tickerView = null; 1324 contentView = null; 1325 bigContentView = null; 1326 headsUpContentView = null; 1327 largeIcon = null; 1328 if (extras != null) { 1329 extras.remove(Notification.EXTRA_LARGE_ICON); 1330 extras.remove(Notification.EXTRA_LARGE_ICON_BIG); 1331 extras.remove(Notification.EXTRA_PICTURE); 1332 } 1333 } 1334 1335 /** 1336 * Make sure this CharSequence is safe to put into a bundle, which basically 1337 * means it had better not be some custom Parcelable implementation. 1338 * @hide 1339 */ 1340 public static CharSequence safeCharSequence(CharSequence cs) { 1341 if (cs instanceof Parcelable) { 1342 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName() 1343 + " instance is a custom Parcelable and not allowed in Notification"); 1344 return cs.toString(); 1345 } 1346 1347 return cs; 1348 } 1349 1350 public int describeContents() { 1351 return 0; 1352 } 1353 1354 /** 1355 * Flatten this notification from a parcel. 1356 */ 1357 public void writeToParcel(Parcel parcel, int flags) 1358 { 1359 parcel.writeInt(1); 1360 1361 parcel.writeLong(when); 1362 parcel.writeInt(icon); 1363 parcel.writeInt(number); 1364 if (contentIntent != null) { 1365 parcel.writeInt(1); 1366 contentIntent.writeToParcel(parcel, 0); 1367 } else { 1368 parcel.writeInt(0); 1369 } 1370 if (deleteIntent != null) { 1371 parcel.writeInt(1); 1372 deleteIntent.writeToParcel(parcel, 0); 1373 } else { 1374 parcel.writeInt(0); 1375 } 1376 if (tickerText != null) { 1377 parcel.writeInt(1); 1378 TextUtils.writeToParcel(tickerText, parcel, flags); 1379 } else { 1380 parcel.writeInt(0); 1381 } 1382 if (tickerView != null) { 1383 parcel.writeInt(1); 1384 tickerView.writeToParcel(parcel, 0); 1385 } else { 1386 parcel.writeInt(0); 1387 } 1388 if (contentView != null) { 1389 parcel.writeInt(1); 1390 contentView.writeToParcel(parcel, 0); 1391 } else { 1392 parcel.writeInt(0); 1393 } 1394 if (largeIcon != null) { 1395 parcel.writeInt(1); 1396 largeIcon.writeToParcel(parcel, 0); 1397 } else { 1398 parcel.writeInt(0); 1399 } 1400 1401 parcel.writeInt(defaults); 1402 parcel.writeInt(this.flags); 1403 1404 if (sound != null) { 1405 parcel.writeInt(1); 1406 sound.writeToParcel(parcel, 0); 1407 } else { 1408 parcel.writeInt(0); 1409 } 1410 parcel.writeInt(audioStreamType); 1411 parcel.writeLongArray(vibrate); 1412 parcel.writeInt(ledARGB); 1413 parcel.writeInt(ledOnMS); 1414 parcel.writeInt(ledOffMS); 1415 parcel.writeInt(iconLevel); 1416 1417 if (fullScreenIntent != null) { 1418 parcel.writeInt(1); 1419 fullScreenIntent.writeToParcel(parcel, 0); 1420 } else { 1421 parcel.writeInt(0); 1422 } 1423 1424 parcel.writeInt(priority); 1425 1426 parcel.writeString(category); 1427 1428 parcel.writeString(mGroupKey); 1429 1430 parcel.writeString(mSortKey); 1431 1432 parcel.writeBundle(extras); // null ok 1433 1434 parcel.writeTypedArray(actions, 0); // null ok 1435 1436 if (bigContentView != null) { 1437 parcel.writeInt(1); 1438 bigContentView.writeToParcel(parcel, 0); 1439 } else { 1440 parcel.writeInt(0); 1441 } 1442 1443 if (headsUpContentView != null) { 1444 parcel.writeInt(1); 1445 headsUpContentView.writeToParcel(parcel, 0); 1446 } else { 1447 parcel.writeInt(0); 1448 } 1449 1450 parcel.writeInt(visibility); 1451 1452 if (publicVersion != null) { 1453 parcel.writeInt(1); 1454 publicVersion.writeToParcel(parcel, 0); 1455 } else { 1456 parcel.writeInt(0); 1457 } 1458 1459 parcel.writeInt(color); 1460 } 1461 1462 /** 1463 * Parcelable.Creator that instantiates Notification objects 1464 */ 1465 public static final Parcelable.Creator<Notification> CREATOR 1466 = new Parcelable.Creator<Notification>() 1467 { 1468 public Notification createFromParcel(Parcel parcel) 1469 { 1470 return new Notification(parcel); 1471 } 1472 1473 public Notification[] newArray(int size) 1474 { 1475 return new Notification[size]; 1476 } 1477 }; 1478 1479 /** 1480 * Sets the {@link #contentView} field to be a view with the standard "Latest Event" 1481 * layout. 1482 * 1483 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields 1484 * in the view.</p> 1485 * @param context The context for your application / activity. 1486 * @param contentTitle The title that goes in the expanded entry. 1487 * @param contentText The text that goes in the expanded entry. 1488 * @param contentIntent The intent to launch when the user clicks the expanded notification. 1489 * If this is an activity, it must include the 1490 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires 1491 * that you take care of task management as described in the 1492 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back 1493 * Stack</a> document. 1494 * 1495 * @deprecated Use {@link Builder} instead. 1496 */ 1497 @Deprecated 1498 public void setLatestEventInfo(Context context, 1499 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { 1500 Notification.Builder builder = new Notification.Builder(context); 1501 1502 // First, ensure that key pieces of information that may have been set directly 1503 // are preserved 1504 builder.setWhen(this.when); 1505 builder.setSmallIcon(this.icon); 1506 builder.setPriority(this.priority); 1507 builder.setTicker(this.tickerText); 1508 builder.setNumber(this.number); 1509 builder.mFlags = this.flags; 1510 builder.setSound(this.sound, this.audioStreamType); 1511 builder.setDefaults(this.defaults); 1512 builder.setVibrate(this.vibrate); 1513 1514 // now apply the latestEventInfo fields 1515 if (contentTitle != null) { 1516 builder.setContentTitle(contentTitle); 1517 } 1518 if (contentText != null) { 1519 builder.setContentText(contentText); 1520 } 1521 builder.setContentIntent(contentIntent); 1522 builder.buildInto(this); 1523 } 1524 1525 @Override 1526 public String toString() { 1527 StringBuilder sb = new StringBuilder(); 1528 sb.append("Notification(pri="); 1529 sb.append(priority); 1530 sb.append(" contentView="); 1531 if (contentView != null) { 1532 sb.append(contentView.getPackage()); 1533 sb.append("/0x"); 1534 sb.append(Integer.toHexString(contentView.getLayoutId())); 1535 } else { 1536 sb.append("null"); 1537 } 1538 // TODO(dsandler): defaults take precedence over local values, so reorder the branches below 1539 sb.append(" vibrate="); 1540 if ((this.defaults & DEFAULT_VIBRATE) != 0) { 1541 sb.append("default"); 1542 } else if (this.vibrate != null) { 1543 int N = this.vibrate.length-1; 1544 sb.append("["); 1545 for (int i=0; i<N; i++) { 1546 sb.append(this.vibrate[i]); 1547 sb.append(','); 1548 } 1549 if (N != -1) { 1550 sb.append(this.vibrate[N]); 1551 } 1552 sb.append("]"); 1553 } else { 1554 sb.append("null"); 1555 } 1556 sb.append(" sound="); 1557 if ((this.defaults & DEFAULT_SOUND) != 0) { 1558 sb.append("default"); 1559 } else if (this.sound != null) { 1560 sb.append(this.sound.toString()); 1561 } else { 1562 sb.append("null"); 1563 } 1564 sb.append(" defaults=0x"); 1565 sb.append(Integer.toHexString(this.defaults)); 1566 sb.append(" flags=0x"); 1567 sb.append(Integer.toHexString(this.flags)); 1568 sb.append(String.format(" color=0x%08x", this.color)); 1569 if (this.category != null) { 1570 sb.append(" category="); 1571 sb.append(this.category); 1572 } 1573 if (this.mGroupKey != null) { 1574 sb.append(" groupKey="); 1575 sb.append(this.mGroupKey); 1576 } 1577 if (this.mSortKey != null) { 1578 sb.append(" sortKey="); 1579 sb.append(this.mSortKey); 1580 } 1581 if (actions != null) { 1582 sb.append(" "); 1583 sb.append(actions.length); 1584 sb.append(" action"); 1585 if (actions.length > 1) sb.append("s"); 1586 } 1587 sb.append(")"); 1588 return sb.toString(); 1589 } 1590 1591 /** {@hide} */ 1592 public void setUser(UserHandle user) { 1593 if (user.getIdentifier() == UserHandle.USER_ALL) { 1594 user = UserHandle.OWNER; 1595 } 1596 if (tickerView != null) { 1597 tickerView.setUser(user); 1598 } 1599 if (contentView != null) { 1600 contentView.setUser(user); 1601 } 1602 if (bigContentView != null) { 1603 bigContentView.setUser(user); 1604 } 1605 if (headsUpContentView != null) { 1606 headsUpContentView.setUser(user); 1607 } 1608 } 1609 1610 /** 1611 * Builder class for {@link Notification} objects. 1612 * 1613 * Provides a convenient way to set the various fields of a {@link Notification} and generate 1614 * content views using the platform's notification layout template. If your app supports 1615 * versions of Android as old as API level 4, you can instead use 1616 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}, 1617 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support 1618 * library</a>. 1619 * 1620 * <p>Example: 1621 * 1622 * <pre class="prettyprint"> 1623 * Notification noti = new Notification.Builder(mContext) 1624 * .setContentTitle("New mail from " + sender.toString()) 1625 * .setContentText(subject) 1626 * .setSmallIcon(R.drawable.new_mail) 1627 * .setLargeIcon(aBitmap) 1628 * .build(); 1629 * </pre> 1630 */ 1631 public static class Builder { 1632 private static final int MAX_ACTION_BUTTONS = 3; 1633 1634 private Context mContext; 1635 1636 private long mWhen; 1637 private int mSmallIcon; 1638 private int mSmallIconLevel; 1639 private int mNumber; 1640 private CharSequence mContentTitle; 1641 private CharSequence mContentText; 1642 private CharSequence mContentInfo; 1643 private CharSequence mSubText; 1644 private PendingIntent mContentIntent; 1645 private RemoteViews mContentView; 1646 private PendingIntent mDeleteIntent; 1647 private PendingIntent mFullScreenIntent; 1648 private CharSequence mTickerText; 1649 private RemoteViews mTickerView; 1650 private Bitmap mLargeIcon; 1651 private Uri mSound; 1652 private int mAudioStreamType; 1653 private long[] mVibrate; 1654 private int mLedArgb; 1655 private int mLedOnMs; 1656 private int mLedOffMs; 1657 private int mDefaults; 1658 private int mFlags; 1659 private int mProgressMax; 1660 private int mProgress; 1661 private boolean mProgressIndeterminate; 1662 private String mCategory; 1663 private String mGroupKey; 1664 private String mSortKey; 1665 private Bundle mExtras; 1666 private int mPriority; 1667 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS); 1668 private boolean mUseChronometer; 1669 private Style mStyle; 1670 private boolean mShowWhen = true; 1671 private int mVisibility = VISIBILITY_PRIVATE; 1672 private Notification mPublicVersion = null; 1673 private final NotificationColorUtil mColorUtil; 1674 private ArrayList<String> mPeople; 1675 private boolean mPreQuantum; 1676 private int mColor = COLOR_DEFAULT; 1677 1678 /** 1679 * Constructs a new Builder with the defaults: 1680 * 1681 1682 * <table> 1683 * <tr><th align=right>priority</th> 1684 * <td>{@link #PRIORITY_DEFAULT}</td></tr> 1685 * <tr><th align=right>when</th> 1686 * <td>now ({@link System#currentTimeMillis()})</td></tr> 1687 * <tr><th align=right>audio stream</th> 1688 * <td>{@link #STREAM_DEFAULT}</td></tr> 1689 * </table> 1690 * 1691 1692 * @param context 1693 * A {@link Context} that will be used by the Builder to construct the 1694 * RemoteViews. The Context will not be held past the lifetime of this Builder 1695 * object. 1696 */ 1697 public Builder(Context context) { 1698 mContext = context; 1699 1700 // Set defaults to match the defaults of a Notification 1701 mWhen = System.currentTimeMillis(); 1702 mAudioStreamType = STREAM_DEFAULT; 1703 mPriority = PRIORITY_DEFAULT; 1704 mPeople = new ArrayList<String>(); 1705 1706 mPreQuantum = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L; 1707 mColorUtil = NotificationColorUtil.getInstance(); 1708 } 1709 1710 /** 1711 * Add a timestamp pertaining to the notification (usually the time the event occurred). 1712 * It will be shown in the notification content view by default; use 1713 * {@link #setShowWhen(boolean) setShowWhen} to control this. 1714 * 1715 * @see Notification#when 1716 */ 1717 public Builder setWhen(long when) { 1718 mWhen = when; 1719 return this; 1720 } 1721 1722 /** 1723 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown 1724 * in the content view. 1725 */ 1726 public Builder setShowWhen(boolean show) { 1727 mShowWhen = show; 1728 return this; 1729 } 1730 1731 /** 1732 * Show the {@link Notification#when} field as a stopwatch. 1733 * 1734 * Instead of presenting <code>when</code> as a timestamp, the notification will show an 1735 * automatically updating display of the minutes and seconds since <code>when</code>. 1736 * 1737 * Useful when showing an elapsed time (like an ongoing phone call). 1738 * 1739 * @see android.widget.Chronometer 1740 * @see Notification#when 1741 */ 1742 public Builder setUsesChronometer(boolean b) { 1743 mUseChronometer = b; 1744 return this; 1745 } 1746 1747 /** 1748 * Set the small icon resource, which will be used to represent the notification in the 1749 * status bar. 1750 * 1751 1752 * The platform template for the expanded view will draw this icon in the left, unless a 1753 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small 1754 * icon will be moved to the right-hand side. 1755 * 1756 1757 * @param icon 1758 * A resource ID in the application's package of the drawable to use. 1759 * @see Notification#icon 1760 */ 1761 public Builder setSmallIcon(int icon) { 1762 mSmallIcon = icon; 1763 return this; 1764 } 1765 1766 /** 1767 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional 1768 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable 1769 * LevelListDrawable}. 1770 * 1771 * @param icon A resource ID in the application's package of the drawable to use. 1772 * @param level The level to use for the icon. 1773 * 1774 * @see Notification#icon 1775 * @see Notification#iconLevel 1776 */ 1777 public Builder setSmallIcon(int icon, int level) { 1778 mSmallIcon = icon; 1779 mSmallIconLevel = level; 1780 return this; 1781 } 1782 1783 /** 1784 * Set the first line of text in the platform notification template. 1785 */ 1786 public Builder setContentTitle(CharSequence title) { 1787 mContentTitle = safeCharSequence(title); 1788 return this; 1789 } 1790 1791 /** 1792 * Set the second line of text in the platform notification template. 1793 */ 1794 public Builder setContentText(CharSequence text) { 1795 mContentText = safeCharSequence(text); 1796 return this; 1797 } 1798 1799 /** 1800 * Set the third line of text in the platform notification template. 1801 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the 1802 * same location in the standard template. 1803 */ 1804 public Builder setSubText(CharSequence text) { 1805 mSubText = safeCharSequence(text); 1806 return this; 1807 } 1808 1809 /** 1810 * Set the large number at the right-hand side of the notification. This is 1811 * equivalent to setContentInfo, although it might show the number in a different 1812 * font size for readability. 1813 */ 1814 public Builder setNumber(int number) { 1815 mNumber = number; 1816 return this; 1817 } 1818 1819 /** 1820 * A small piece of additional information pertaining to this notification. 1821 * 1822 * The platform template will draw this on the last line of the notification, at the far 1823 * right (to the right of a smallIcon if it has been placed there). 1824 */ 1825 public Builder setContentInfo(CharSequence info) { 1826 mContentInfo = safeCharSequence(info); 1827 return this; 1828 } 1829 1830 /** 1831 * Set the progress this notification represents. 1832 * 1833 * The platform template will represent this using a {@link ProgressBar}. 1834 */ 1835 public Builder setProgress(int max, int progress, boolean indeterminate) { 1836 mProgressMax = max; 1837 mProgress = progress; 1838 mProgressIndeterminate = indeterminate; 1839 return this; 1840 } 1841 1842 /** 1843 * Supply a custom RemoteViews to use instead of the platform template. 1844 * 1845 * @see Notification#contentView 1846 */ 1847 public Builder setContent(RemoteViews views) { 1848 mContentView = views; 1849 return this; 1850 } 1851 1852 /** 1853 * Supply a {@link PendingIntent} to be sent when the notification is clicked. 1854 * 1855 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you 1856 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use 1857 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)} 1858 * to assign PendingIntents to individual views in that custom layout (i.e., to create 1859 * clickable buttons inside the notification view). 1860 * 1861 * @see Notification#contentIntent Notification.contentIntent 1862 */ 1863 public Builder setContentIntent(PendingIntent intent) { 1864 mContentIntent = intent; 1865 return this; 1866 } 1867 1868 /** 1869 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user. 1870 * 1871 * @see Notification#deleteIntent 1872 */ 1873 public Builder setDeleteIntent(PendingIntent intent) { 1874 mDeleteIntent = intent; 1875 return this; 1876 } 1877 1878 /** 1879 * An intent to launch instead of posting the notification to the status bar. 1880 * Only for use with extremely high-priority notifications demanding the user's 1881 * <strong>immediate</strong> attention, such as an incoming phone call or 1882 * alarm clock that the user has explicitly set to a particular time. 1883 * If this facility is used for something else, please give the user an option 1884 * to turn it off and use a normal notification, as this can be extremely 1885 * disruptive. 1886 * 1887 * @param intent The pending intent to launch. 1888 * @param highPriority Passing true will cause this notification to be sent 1889 * even if other notifications are suppressed. 1890 * 1891 * @see Notification#fullScreenIntent 1892 */ 1893 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) { 1894 mFullScreenIntent = intent; 1895 setFlag(FLAG_HIGH_PRIORITY, highPriority); 1896 return this; 1897 } 1898 1899 /** 1900 * Set the "ticker" text which is displayed in the status bar when the notification first 1901 * arrives. 1902 * 1903 * @see Notification#tickerText 1904 */ 1905 public Builder setTicker(CharSequence tickerText) { 1906 mTickerText = safeCharSequence(tickerText); 1907 return this; 1908 } 1909 1910 /** 1911 * Set the text that is displayed in the status bar when the notification first 1912 * arrives, and also a RemoteViews object that may be displayed instead on some 1913 * devices. 1914 * 1915 * @see Notification#tickerText 1916 * @see Notification#tickerView 1917 */ 1918 public Builder setTicker(CharSequence tickerText, RemoteViews views) { 1919 mTickerText = safeCharSequence(tickerText); 1920 mTickerView = views; 1921 return this; 1922 } 1923 1924 /** 1925 * Add a large icon to the notification (and the ticker on some devices). 1926 * 1927 * In the platform template, this image will be shown on the left of the notification view 1928 * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side). 1929 * 1930 * @see Notification#largeIcon 1931 */ 1932 public Builder setLargeIcon(Bitmap icon) { 1933 mLargeIcon = icon; 1934 return this; 1935 } 1936 1937 /** 1938 * Set the sound to play. 1939 * 1940 * It will be played on the {@link #STREAM_DEFAULT default stream} for notifications. 1941 * 1942 * @see Notification#sound 1943 */ 1944 public Builder setSound(Uri sound) { 1945 mSound = sound; 1946 mAudioStreamType = STREAM_DEFAULT; 1947 return this; 1948 } 1949 1950 /** 1951 * Set the sound to play, along with a specific stream on which to play it. 1952 * 1953 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants. 1954 * 1955 * @see Notification#sound 1956 */ 1957 public Builder setSound(Uri sound, int streamType) { 1958 mSound = sound; 1959 mAudioStreamType = streamType; 1960 return this; 1961 } 1962 1963 /** 1964 * Set the vibration pattern to use. 1965 * 1966 1967 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the 1968 * <code>pattern</code> parameter. 1969 * 1970 1971 * @see Notification#vibrate 1972 */ 1973 public Builder setVibrate(long[] pattern) { 1974 mVibrate = pattern; 1975 return this; 1976 } 1977 1978 /** 1979 * Set the desired color for the indicator LED on the device, as well as the 1980 * blink duty cycle (specified in milliseconds). 1981 * 1982 1983 * Not all devices will honor all (or even any) of these values. 1984 * 1985 1986 * @see Notification#ledARGB 1987 * @see Notification#ledOnMS 1988 * @see Notification#ledOffMS 1989 */ 1990 public Builder setLights(int argb, int onMs, int offMs) { 1991 mLedArgb = argb; 1992 mLedOnMs = onMs; 1993 mLedOffMs = offMs; 1994 return this; 1995 } 1996 1997 /** 1998 * Set whether this is an "ongoing" notification. 1999 * 2000 2001 * Ongoing notifications cannot be dismissed by the user, so your application or service 2002 * must take care of canceling them. 2003 * 2004 2005 * They are typically used to indicate a background task that the user is actively engaged 2006 * with (e.g., playing music) or is pending in some way and therefore occupying the device 2007 * (e.g., a file download, sync operation, active network connection). 2008 * 2009 2010 * @see Notification#FLAG_ONGOING_EVENT 2011 * @see Service#setForeground(boolean) 2012 */ 2013 public Builder setOngoing(boolean ongoing) { 2014 setFlag(FLAG_ONGOING_EVENT, ongoing); 2015 return this; 2016 } 2017 2018 /** 2019 * Set this flag if you would only like the sound, vibrate 2020 * and ticker to be played if the notification is not already showing. 2021 * 2022 * @see Notification#FLAG_ONLY_ALERT_ONCE 2023 */ 2024 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) { 2025 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce); 2026 return this; 2027 } 2028 2029 /** 2030 * Make this notification automatically dismissed when the user touches it. The 2031 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens. 2032 * 2033 * @see Notification#FLAG_AUTO_CANCEL 2034 */ 2035 public Builder setAutoCancel(boolean autoCancel) { 2036 setFlag(FLAG_AUTO_CANCEL, autoCancel); 2037 return this; 2038 } 2039 2040 /** 2041 * Set whether or not this notification should not bridge to other devices. 2042 * 2043 * <p>Some notifications can be bridged to other devices for remote display. 2044 * This hint can be set to recommend this notification not be bridged. 2045 */ 2046 public Builder setLocalOnly(boolean localOnly) { 2047 setFlag(FLAG_LOCAL_ONLY, localOnly); 2048 return this; 2049 } 2050 2051 /** 2052 * Set which notification properties will be inherited from system defaults. 2053 * <p> 2054 * The value should be one or more of the following fields combined with 2055 * bitwise-or: 2056 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. 2057 * <p> 2058 * For all default values, use {@link #DEFAULT_ALL}. 2059 */ 2060 public Builder setDefaults(int defaults) { 2061 mDefaults = defaults; 2062 return this; 2063 } 2064 2065 /** 2066 * Set the priority of this notification. 2067 * 2068 * @see Notification#priority 2069 */ 2070 public Builder setPriority(@Priority int pri) { 2071 mPriority = pri; 2072 return this; 2073 } 2074 2075 /** 2076 * Set the notification category. 2077 * 2078 * @see Notification#category 2079 */ 2080 public Builder setCategory(String category) { 2081 mCategory = category; 2082 return this; 2083 } 2084 2085 /** 2086 * Add a person that is relevant to this notification. 2087 * 2088 * @see Notification#EXTRA_PEOPLE 2089 */ 2090 public Builder addPerson(String handle) { 2091 mPeople.add(handle); 2092 return this; 2093 } 2094 2095 /** 2096 * Set this notification to be part of a group of notifications sharing the same key. 2097 * Grouped notifications may display in a cluster or stack on devices which 2098 * support such rendering. 2099 * 2100 * <p>To make this notification the summary for its group, also call 2101 * {@link #setGroupSummary}. A sort order can be specified for group members by using 2102 * {@link #setSortKey}. 2103 * @param groupKey The group key of the group. 2104 * @return this object for method chaining 2105 */ 2106 public Builder setGroup(String groupKey) { 2107 mGroupKey = groupKey; 2108 return this; 2109 } 2110 2111 /** 2112 * Set this notification to be the group summary for a group of notifications. 2113 * Grouped notifications may display in a cluster or stack on devices which 2114 * support such rendering. Requires a group key also be set using {@link #setGroup}. 2115 * @param isGroupSummary Whether this notification should be a group summary. 2116 * @return this object for method chaining 2117 */ 2118 public Builder setGroupSummary(boolean isGroupSummary) { 2119 setFlag(FLAG_GROUP_SUMMARY, isGroupSummary); 2120 return this; 2121 } 2122 2123 /** 2124 * Set a sort key that orders this notification among other notifications from the 2125 * same package. This can be useful if an external sort was already applied and an app 2126 * would like to preserve this. Notifications will be sorted lexicographically using this 2127 * value, although providing different priorities in addition to providing sort key may 2128 * cause this value to be ignored. 2129 * 2130 * <p>This sort key can also be used to order members of a notification group. See 2131 * {@link #setGroup}. 2132 * 2133 * @see String#compareTo(String) 2134 */ 2135 public Builder setSortKey(String sortKey) { 2136 mSortKey = sortKey; 2137 return this; 2138 } 2139 2140 /** 2141 * Merge additional metadata into this notification. 2142 * 2143 * <p>Values within the Bundle will replace existing extras values in this Builder. 2144 * 2145 * @see Notification#extras 2146 */ 2147 public Builder addExtras(Bundle extras) { 2148 if (extras != null) { 2149 if (mExtras == null) { 2150 mExtras = new Bundle(extras); 2151 } else { 2152 mExtras.putAll(extras); 2153 } 2154 } 2155 return this; 2156 } 2157 2158 /** 2159 * Set metadata for this notification. 2160 * 2161 * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's 2162 * current contents are copied into the Notification each time {@link #build()} is 2163 * called. 2164 * 2165 * <p>Replaces any existing extras values with those from the provided Bundle. 2166 * Use {@link #addExtras} to merge in metadata instead. 2167 * 2168 * @see Notification#extras 2169 */ 2170 public Builder setExtras(Bundle extras) { 2171 mExtras = extras; 2172 return this; 2173 } 2174 2175 /** 2176 * Get the current metadata Bundle used by this notification Builder. 2177 * 2178 * <p>The returned Bundle is shared with this Builder. 2179 * 2180 * <p>The current contents of this Bundle are copied into the Notification each time 2181 * {@link #build()} is called. 2182 * 2183 * @see Notification#extras 2184 */ 2185 public Bundle getExtras() { 2186 if (mExtras == null) { 2187 mExtras = new Bundle(); 2188 } 2189 return mExtras; 2190 } 2191 2192 /** 2193 * Add an action to this notification. Actions are typically displayed by 2194 * the system as a button adjacent to the notification content. 2195 * <p> 2196 * Every action must have an icon (32dp square and matching the 2197 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo 2198 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}. 2199 * <p> 2200 * A notification in its expanded form can display up to 3 actions, from left to right in 2201 * the order they were added. Actions will not be displayed when the notification is 2202 * collapsed, however, so be sure that any essential functions may be accessed by the user 2203 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}). 2204 * 2205 * @param icon Resource ID of a drawable that represents the action. 2206 * @param title Text describing the action. 2207 * @param intent PendingIntent to be fired when the action is invoked. 2208 */ 2209 public Builder addAction(int icon, CharSequence title, PendingIntent intent) { 2210 mActions.add(new Action(icon, safeCharSequence(title), intent)); 2211 return this; 2212 } 2213 2214 /** 2215 * Add an action to this notification. Actions are typically displayed by 2216 * the system as a button adjacent to the notification content. 2217 * <p> 2218 * Every action must have an icon (32dp square and matching the 2219 * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo 2220 * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}. 2221 * <p> 2222 * A notification in its expanded form can display up to 3 actions, from left to right in 2223 * the order they were added. Actions will not be displayed when the notification is 2224 * collapsed, however, so be sure that any essential functions may be accessed by the user 2225 * in some other way (for example, in the Activity pointed to by {@link #contentIntent}). 2226 * 2227 * @param action The action to add. 2228 */ 2229 public Builder addAction(Action action) { 2230 mActions.add(action); 2231 return this; 2232 } 2233 2234 /** 2235 * Add a rich notification style to be applied at build time. 2236 * 2237 * @param style Object responsible for modifying the notification style. 2238 */ 2239 public Builder setStyle(Style style) { 2240 if (mStyle != style) { 2241 mStyle = style; 2242 if (mStyle != null) { 2243 mStyle.setBuilder(this); 2244 } 2245 } 2246 return this; 2247 } 2248 2249 /** 2250 * Specify the value of {@link #visibility}. 2251 * 2252 * @param visibility One of {@link #VISIBILITY_PRIVATE} (the default), 2253 * {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}. 2254 * 2255 * @return The same Builder. 2256 */ 2257 public Builder setVisibility(int visibility) { 2258 mVisibility = visibility; 2259 return this; 2260 } 2261 2262 /** 2263 * Supply a replacement Notification whose contents should be shown in insecure contexts 2264 * (i.e. atop the secure lockscreen). See {@link #visibility} and {@link #VISIBILITY_PUBLIC}. 2265 * @param n A replacement notification, presumably with some or all info redacted. 2266 * @return The same Builder. 2267 */ 2268 public Builder setPublicVersion(Notification n) { 2269 mPublicVersion = n; 2270 return this; 2271 } 2272 2273 /** 2274 * Apply an extender to this notification builder. Extenders may be used to add 2275 * metadata or change options on this builder. 2276 */ 2277 public Builder extend(Extender extender) { 2278 extender.extend(this); 2279 return this; 2280 } 2281 2282 private void setFlag(int mask, boolean value) { 2283 if (value) { 2284 mFlags |= mask; 2285 } else { 2286 mFlags &= ~mask; 2287 } 2288 } 2289 2290 /** 2291 * Sets {@link Notification#color}. 2292 * 2293 * @param argb The accent color to use 2294 * 2295 * @return The same Builder. 2296 */ 2297 public Builder setColor(int argb) { 2298 mColor = argb; 2299 return this; 2300 } 2301 2302 private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) { 2303 RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId); 2304 boolean showLine3 = false; 2305 boolean showLine2 = false; 2306 2307 if (mPriority < PRIORITY_LOW) { 2308 // TODO: Low priority presentation 2309 } 2310 if (mLargeIcon != null) { 2311 contentView.setImageViewBitmap(R.id.icon, mLargeIcon); 2312 processLargeIcon(mLargeIcon, contentView); 2313 contentView.setImageViewResource(R.id.right_icon, mSmallIcon); 2314 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE); 2315 processSmallRightIcon(mSmallIcon, contentView); 2316 } else { // small icon at left 2317 contentView.setImageViewResource(R.id.icon, mSmallIcon); 2318 contentView.setViewVisibility(R.id.icon, View.VISIBLE); 2319 processSmallIconAsLarge(mSmallIcon, contentView); 2320 } 2321 if (mContentTitle != null) { 2322 contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle)); 2323 } 2324 if (mContentText != null) { 2325 contentView.setTextViewText(R.id.text, processLegacyText(mContentText)); 2326 showLine3 = true; 2327 } 2328 if (mContentInfo != null) { 2329 contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo)); 2330 contentView.setViewVisibility(R.id.info, View.VISIBLE); 2331 showLine3 = true; 2332 } else if (mNumber > 0) { 2333 final int tooBig = mContext.getResources().getInteger( 2334 R.integer.status_bar_notification_info_maxnum); 2335 if (mNumber > tooBig) { 2336 contentView.setTextViewText(R.id.info, processLegacyText( 2337 mContext.getResources().getString( 2338 R.string.status_bar_notification_info_overflow))); 2339 } else { 2340 NumberFormat f = NumberFormat.getIntegerInstance(); 2341 contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber))); 2342 } 2343 contentView.setViewVisibility(R.id.info, View.VISIBLE); 2344 showLine3 = true; 2345 } else { 2346 contentView.setViewVisibility(R.id.info, View.GONE); 2347 } 2348 2349 // Need to show three lines? 2350 if (mSubText != null) { 2351 contentView.setTextViewText(R.id.text, processLegacyText(mSubText)); 2352 if (mContentText != null) { 2353 contentView.setTextViewText(R.id.text2, processLegacyText(mContentText)); 2354 contentView.setViewVisibility(R.id.text2, View.VISIBLE); 2355 showLine2 = true; 2356 } else { 2357 contentView.setViewVisibility(R.id.text2, View.GONE); 2358 } 2359 } else { 2360 contentView.setViewVisibility(R.id.text2, View.GONE); 2361 if (mProgressMax != 0 || mProgressIndeterminate) { 2362 contentView.setProgressBar( 2363 R.id.progress, mProgressMax, mProgress, mProgressIndeterminate); 2364 contentView.setViewVisibility(R.id.progress, View.VISIBLE); 2365 showLine2 = true; 2366 } else { 2367 contentView.setViewVisibility(R.id.progress, View.GONE); 2368 } 2369 } 2370 if (showLine2) { 2371 if (fitIn1U) { 2372 // need to shrink all the type to make sure everything fits 2373 final Resources res = mContext.getResources(); 2374 final float subTextSize = res.getDimensionPixelSize( 2375 R.dimen.notification_subtext_size); 2376 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize); 2377 } 2378 // vertical centering 2379 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 2380 } 2381 2382 if (mWhen != 0 && mShowWhen) { 2383 if (mUseChronometer) { 2384 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE); 2385 contentView.setLong(R.id.chronometer, "setBase", 2386 mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis())); 2387 contentView.setBoolean(R.id.chronometer, "setStarted", true); 2388 } else { 2389 contentView.setViewVisibility(R.id.time, View.VISIBLE); 2390 contentView.setLong(R.id.time, "setTime", mWhen); 2391 } 2392 } else { 2393 contentView.setViewVisibility(R.id.time, View.GONE); 2394 } 2395 2396 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE); 2397 contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE); 2398 return contentView; 2399 } 2400 2401 private RemoteViews applyStandardTemplateWithActions(int layoutId) { 2402 RemoteViews big = applyStandardTemplate(layoutId, false); 2403 2404 int N = mActions.size(); 2405 if (N > 0) { 2406 big.setViewVisibility(R.id.actions, View.VISIBLE); 2407 big.setViewVisibility(R.id.action_divider, View.VISIBLE); 2408 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS; 2409 big.removeAllViews(R.id.actions); 2410 for (int i=0; i<N; i++) { 2411 final RemoteViews button = generateActionButton(mActions.get(i)); 2412 big.addView(R.id.actions, button); 2413 } 2414 } 2415 return big; 2416 } 2417 2418 private RemoteViews makeContentView() { 2419 if (mContentView != null) { 2420 return mContentView; 2421 } else { 2422 return applyStandardTemplate(getBaseLayoutResource(), true); // no more special large_icon flavor 2423 } 2424 } 2425 2426 private RemoteViews makeTickerView() { 2427 if (mTickerView != null) { 2428 return mTickerView; 2429 } else { 2430 if (mContentView == null) { 2431 return applyStandardTemplate(mLargeIcon == null 2432 ? R.layout.status_bar_latest_event_ticker 2433 : R.layout.status_bar_latest_event_ticker_large_icon, true); 2434 } else { 2435 return null; 2436 } 2437 } 2438 } 2439 2440 private RemoteViews makeBigContentView() { 2441 if (mActions.size() == 0) return null; 2442 2443 return applyStandardTemplateWithActions(getBigBaseLayoutResource()); 2444 } 2445 2446 private RemoteViews makeHeadsUpContentView() { 2447 if (mActions.size() == 0) return null; 2448 2449 return applyStandardTemplateWithActions(getBigBaseLayoutResource()); 2450 } 2451 2452 2453 private RemoteViews generateActionButton(Action action) { 2454 final boolean tombstone = (action.actionIntent == null); 2455 RemoteViews button = new RemoteViews(mContext.getPackageName(), 2456 tombstone ? getActionTombstoneLayoutResource() 2457 : getActionLayoutResource()); 2458 button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0); 2459 button.setTextViewText(R.id.action0, processLegacyText(action.title)); 2460 if (!tombstone) { 2461 button.setOnClickPendingIntent(R.id.action0, action.actionIntent); 2462 } 2463 button.setContentDescription(R.id.action0, action.title); 2464 processLegacyAction(action, button); 2465 return button; 2466 } 2467 2468 /** 2469 * @return Whether we are currently building a notification from a legacy (an app that 2470 * doesn't create quantum notifications by itself) app. 2471 */ 2472 private boolean isLegacy() { 2473 return mColorUtil != null; 2474 } 2475 2476 private void processLegacyAction(Action action, RemoteViews button) { 2477 if (isLegacy()) { 2478 if (mColorUtil.isGrayscale(mContext, action.icon)) { 2479 button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0, 2480 mContext.getResources().getColor( 2481 R.color.notification_action_legacy_color_filter), 2482 PorterDuff.Mode.MULTIPLY); 2483 } 2484 } 2485 } 2486 2487 private CharSequence processLegacyText(CharSequence charSequence) { 2488 if (isLegacy()) { 2489 return mColorUtil.invertCharSequenceColors(charSequence); 2490 } else { 2491 return charSequence; 2492 } 2493 } 2494 2495 /** 2496 * Apply any necessary background to smallIcons being used in the largeIcon spot. 2497 */ 2498 private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) { 2499 if (!isLegacy() || mColorUtil.isGrayscale(mContext, largeIconId)) { 2500 applyLargeIconBackground(contentView); 2501 } 2502 } 2503 2504 /** 2505 * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is, 2506 * if it's grayscale). 2507 */ 2508 // TODO: also check bounds, transparency, that sort of thing. 2509 private void processLargeIcon(Bitmap largeIcon, RemoteViews contentView) { 2510 if (!isLegacy() || mColorUtil.isGrayscale(largeIcon)) { 2511 applyLargeIconBackground(contentView); 2512 } else { 2513 removeLargeIconBackground(contentView); 2514 } 2515 } 2516 2517 /** 2518 * Add a colored circle behind the largeIcon slot. 2519 */ 2520 private void applyLargeIconBackground(RemoteViews contentView) { 2521 contentView.setInt(R.id.icon, "setBackgroundResource", 2522 R.drawable.notification_icon_legacy_bg_inset); 2523 2524 contentView.setDrawableParameters( 2525 R.id.icon, 2526 true, 2527 -1, 2528 mColor, 2529 PorterDuff.Mode.SRC_ATOP, 2530 -1); 2531 } 2532 2533 private void removeLargeIconBackground(RemoteViews contentView) { 2534 contentView.setInt(R.id.icon, "setBackgroundResource", 0); 2535 } 2536 2537 /** 2538 * Recolor small icons when used in the R.id.right_icon slot. 2539 */ 2540 private void processSmallRightIcon(int smallIconDrawableId, 2541 RemoteViews contentView) { 2542 if (!isLegacy() || mColorUtil.isGrayscale(mContext, smallIconDrawableId)) { 2543 contentView.setDrawableParameters(R.id.right_icon, false, -1, 2544 0xFFFFFFFF, 2545 PorterDuff.Mode.SRC_ATOP, -1); 2546 2547 contentView.setInt(R.id.right_icon, 2548 "setBackgroundResource", 2549 R.drawable.notification_icon_legacy_bg); 2550 2551 contentView.setDrawableParameters( 2552 R.id.right_icon, 2553 true, 2554 -1, 2555 mColor, 2556 PorterDuff.Mode.SRC_ATOP, 2557 -1); 2558 } 2559 } 2560 2561 private int resolveColor() { 2562 if (mColor == COLOR_DEFAULT) { 2563 mColor = mContext.getResources().getColor(R.color.notification_icon_bg_color); 2564 } else { 2565 mColor |= 0xFF000000; // no alpha for custom colors 2566 } 2567 return mColor; 2568 } 2569 2570 /** 2571 * Apply the unstyled operations and return a new {@link Notification} object. 2572 * @hide 2573 */ 2574 public Notification buildUnstyled() { 2575 Notification n = new Notification(); 2576 n.when = mWhen; 2577 n.icon = mSmallIcon; 2578 n.iconLevel = mSmallIconLevel; 2579 n.number = mNumber; 2580 2581 n.color = resolveColor(); 2582 2583 n.contentView = makeContentView(); 2584 n.contentIntent = mContentIntent; 2585 n.deleteIntent = mDeleteIntent; 2586 n.fullScreenIntent = mFullScreenIntent; 2587 n.tickerText = mTickerText; 2588 n.tickerView = makeTickerView(); 2589 n.largeIcon = mLargeIcon; 2590 n.sound = mSound; 2591 n.audioStreamType = mAudioStreamType; 2592 n.vibrate = mVibrate; 2593 n.ledARGB = mLedArgb; 2594 n.ledOnMS = mLedOnMs; 2595 n.ledOffMS = mLedOffMs; 2596 n.defaults = mDefaults; 2597 n.flags = mFlags; 2598 n.bigContentView = makeBigContentView(); 2599 n.headsUpContentView = makeHeadsUpContentView(); 2600 if (mLedOnMs != 0 || mLedOffMs != 0) { 2601 n.flags |= FLAG_SHOW_LIGHTS; 2602 } 2603 if ((mDefaults & DEFAULT_LIGHTS) != 0) { 2604 n.flags |= FLAG_SHOW_LIGHTS; 2605 } 2606 n.category = mCategory; 2607 n.mGroupKey = mGroupKey; 2608 n.mSortKey = mSortKey; 2609 n.priority = mPriority; 2610 if (mActions.size() > 0) { 2611 n.actions = new Action[mActions.size()]; 2612 mActions.toArray(n.actions); 2613 } 2614 n.visibility = mVisibility; 2615 2616 if (mPublicVersion != null) { 2617 n.publicVersion = new Notification(); 2618 mPublicVersion.cloneInto(n.publicVersion, true); 2619 } 2620 2621 return n; 2622 } 2623 2624 /** 2625 * Capture, in the provided bundle, semantic information used in the construction of 2626 * this Notification object. 2627 * @hide 2628 */ 2629 public void populateExtras(Bundle extras) { 2630 // Store original information used in the construction of this object 2631 extras.putCharSequence(EXTRA_TITLE, mContentTitle); 2632 extras.putCharSequence(EXTRA_TEXT, mContentText); 2633 extras.putCharSequence(EXTRA_SUB_TEXT, mSubText); 2634 extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo); 2635 extras.putInt(EXTRA_SMALL_ICON, mSmallIcon); 2636 extras.putInt(EXTRA_PROGRESS, mProgress); 2637 extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax); 2638 extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate); 2639 extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer); 2640 extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen); 2641 if (mLargeIcon != null) { 2642 extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); 2643 } 2644 if (!mPeople.isEmpty()) { 2645 extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()])); 2646 } 2647 } 2648 2649 /** 2650 * @deprecated Use {@link #build()} instead. 2651 */ 2652 @Deprecated 2653 public Notification getNotification() { 2654 return build(); 2655 } 2656 2657 /** 2658 * Combine all of the options that have been set and return a new {@link Notification} 2659 * object. 2660 */ 2661 public Notification build() { 2662 Notification n = buildUnstyled(); 2663 2664 if (mStyle != null) { 2665 n = mStyle.buildStyled(n); 2666 } 2667 2668 n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle(); 2669 2670 populateExtras(n.extras); 2671 if (mStyle != null) { 2672 mStyle.addExtras(n.extras); 2673 } 2674 2675 return n; 2676 } 2677 2678 /** 2679 * Apply this Builder to an existing {@link Notification} object. 2680 * 2681 * @hide 2682 */ 2683 public Notification buildInto(Notification n) { 2684 build().cloneInto(n, true); 2685 return n; 2686 } 2687 2688 2689 private int getBaseLayoutResource() { 2690 return R.layout.notification_template_quantum_base; 2691 } 2692 2693 private int getBigBaseLayoutResource() { 2694 return R.layout.notification_template_quantum_big_base; 2695 } 2696 2697 private int getBigPictureLayoutResource() { 2698 return R.layout.notification_template_quantum_big_picture; 2699 } 2700 2701 private int getBigTextLayoutResource() { 2702 return R.layout.notification_template_quantum_big_text; 2703 } 2704 2705 private int getInboxLayoutResource() { 2706 return R.layout.notification_template_quantum_inbox; 2707 } 2708 2709 private int getActionLayoutResource() { 2710 return R.layout.notification_quantum_action; 2711 } 2712 2713 private int getActionTombstoneLayoutResource() { 2714 return R.layout.notification_quantum_action_tombstone; 2715 } 2716 } 2717 2718 /** 2719 * An object that can apply a rich notification style to a {@link Notification.Builder} 2720 * object. 2721 */ 2722 public static abstract class Style { 2723 private CharSequence mBigContentTitle; 2724 private CharSequence mSummaryText = null; 2725 private boolean mSummaryTextSet = false; 2726 2727 protected Builder mBuilder; 2728 2729 /** 2730 * Overrides ContentTitle in the big form of the template. 2731 * This defaults to the value passed to setContentTitle(). 2732 */ 2733 protected void internalSetBigContentTitle(CharSequence title) { 2734 mBigContentTitle = title; 2735 } 2736 2737 /** 2738 * Set the first line of text after the detail section in the big form of the template. 2739 */ 2740 protected void internalSetSummaryText(CharSequence cs) { 2741 mSummaryText = cs; 2742 mSummaryTextSet = true; 2743 } 2744 2745 public void setBuilder(Builder builder) { 2746 if (mBuilder != builder) { 2747 mBuilder = builder; 2748 if (mBuilder != null) { 2749 mBuilder.setStyle(this); 2750 } 2751 } 2752 } 2753 2754 protected void checkBuilder() { 2755 if (mBuilder == null) { 2756 throw new IllegalArgumentException("Style requires a valid Builder object"); 2757 } 2758 } 2759 2760 protected RemoteViews getStandardView(int layoutId) { 2761 checkBuilder(); 2762 2763 if (mBigContentTitle != null) { 2764 mBuilder.setContentTitle(mBigContentTitle); 2765 } 2766 2767 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId); 2768 2769 if (mBigContentTitle != null && mBigContentTitle.equals("")) { 2770 contentView.setViewVisibility(R.id.line1, View.GONE); 2771 } else { 2772 contentView.setViewVisibility(R.id.line1, View.VISIBLE); 2773 } 2774 2775 // The last line defaults to the subtext, but can be replaced by mSummaryText 2776 final CharSequence overflowText = 2777 mSummaryTextSet ? mSummaryText 2778 : mBuilder.mSubText; 2779 if (overflowText != null) { 2780 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText)); 2781 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE); 2782 contentView.setViewVisibility(R.id.line3, View.VISIBLE); 2783 } else { 2784 contentView.setViewVisibility(R.id.overflow_divider, View.GONE); 2785 contentView.setViewVisibility(R.id.line3, View.GONE); 2786 } 2787 2788 return contentView; 2789 } 2790 2791 /** 2792 * @hide 2793 */ 2794 public void addExtras(Bundle extras) { 2795 if (mSummaryTextSet) { 2796 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText); 2797 } 2798 if (mBigContentTitle != null) { 2799 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle); 2800 } 2801 extras.putString(EXTRA_TEMPLATE, this.getClass().getName()); 2802 } 2803 2804 /** 2805 * @hide 2806 */ 2807 public abstract Notification buildStyled(Notification wip); 2808 2809 /** 2810 * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is 2811 * attached to. 2812 * 2813 * @return the fully constructed Notification. 2814 */ 2815 public Notification build() { 2816 checkBuilder(); 2817 return mBuilder.build(); 2818 } 2819 } 2820 2821 /** 2822 * Helper class for generating large-format notifications that include a large image attachment. 2823 * 2824 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 2825 * <pre class="prettyprint"> 2826 * Notification noti = new Notification.BigPictureStyle( 2827 * new Notification.Builder() 2828 * .setContentTitle("New photo from " + sender.toString()) 2829 * .setContentText(subject) 2830 * .setSmallIcon(R.drawable.new_post) 2831 * .setLargeIcon(aBitmap)) 2832 * .bigPicture(aBigBitmap) 2833 * .build(); 2834 * </pre> 2835 * 2836 * @see Notification#bigContentView 2837 */ 2838 public static class BigPictureStyle extends Style { 2839 private Bitmap mPicture; 2840 private Bitmap mBigLargeIcon; 2841 private boolean mBigLargeIconSet = false; 2842 2843 public BigPictureStyle() { 2844 } 2845 2846 public BigPictureStyle(Builder builder) { 2847 setBuilder(builder); 2848 } 2849 2850 /** 2851 * Overrides ContentTitle in the big form of the template. 2852 * This defaults to the value passed to setContentTitle(). 2853 */ 2854 public BigPictureStyle setBigContentTitle(CharSequence title) { 2855 internalSetBigContentTitle(safeCharSequence(title)); 2856 return this; 2857 } 2858 2859 /** 2860 * Set the first line of text after the detail section in the big form of the template. 2861 */ 2862 public BigPictureStyle setSummaryText(CharSequence cs) { 2863 internalSetSummaryText(safeCharSequence(cs)); 2864 return this; 2865 } 2866 2867 /** 2868 * Provide the bitmap to be used as the payload for the BigPicture notification. 2869 */ 2870 public BigPictureStyle bigPicture(Bitmap b) { 2871 mPicture = b; 2872 return this; 2873 } 2874 2875 /** 2876 * Override the large icon when the big notification is shown. 2877 */ 2878 public BigPictureStyle bigLargeIcon(Bitmap b) { 2879 mBigLargeIconSet = true; 2880 mBigLargeIcon = b; 2881 return this; 2882 } 2883 2884 private RemoteViews makeBigContentView() { 2885 RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource()); 2886 2887 contentView.setImageViewBitmap(R.id.big_picture, mPicture); 2888 2889 return contentView; 2890 } 2891 2892 /** 2893 * @hide 2894 */ 2895 public void addExtras(Bundle extras) { 2896 super.addExtras(extras); 2897 2898 if (mBigLargeIconSet) { 2899 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon); 2900 } 2901 extras.putParcelable(EXTRA_PICTURE, mPicture); 2902 } 2903 2904 /** 2905 * @hide 2906 */ 2907 @Override 2908 public Notification buildStyled(Notification wip) { 2909 if (mBigLargeIconSet ) { 2910 mBuilder.mLargeIcon = mBigLargeIcon; 2911 } 2912 wip.bigContentView = makeBigContentView(); 2913 return wip; 2914 } 2915 } 2916 2917 /** 2918 * Helper class for generating large-format notifications that include a lot of text. 2919 * 2920 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 2921 * <pre class="prettyprint"> 2922 * Notification noti = new Notification.BigTextStyle( 2923 * new Notification.Builder() 2924 * .setContentTitle("New mail from " + sender.toString()) 2925 * .setContentText(subject) 2926 * .setSmallIcon(R.drawable.new_mail) 2927 * .setLargeIcon(aBitmap)) 2928 * .bigText(aVeryLongString) 2929 * .build(); 2930 * </pre> 2931 * 2932 * @see Notification#bigContentView 2933 */ 2934 public static class BigTextStyle extends Style { 2935 private CharSequence mBigText; 2936 2937 public BigTextStyle() { 2938 } 2939 2940 public BigTextStyle(Builder builder) { 2941 setBuilder(builder); 2942 } 2943 2944 /** 2945 * Overrides ContentTitle in the big form of the template. 2946 * This defaults to the value passed to setContentTitle(). 2947 */ 2948 public BigTextStyle setBigContentTitle(CharSequence title) { 2949 internalSetBigContentTitle(safeCharSequence(title)); 2950 return this; 2951 } 2952 2953 /** 2954 * Set the first line of text after the detail section in the big form of the template. 2955 */ 2956 public BigTextStyle setSummaryText(CharSequence cs) { 2957 internalSetSummaryText(safeCharSequence(cs)); 2958 return this; 2959 } 2960 2961 /** 2962 * Provide the longer text to be displayed in the big form of the 2963 * template in place of the content text. 2964 */ 2965 public BigTextStyle bigText(CharSequence cs) { 2966 mBigText = safeCharSequence(cs); 2967 return this; 2968 } 2969 2970 /** 2971 * @hide 2972 */ 2973 public void addExtras(Bundle extras) { 2974 super.addExtras(extras); 2975 2976 extras.putCharSequence(EXTRA_TEXT, mBigText); 2977 } 2978 2979 private RemoteViews makeBigContentView() { 2980 // Remove the content text so line3 only shows if you have a summary 2981 final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null); 2982 mBuilder.mContentText = null; 2983 2984 RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource()); 2985 2986 if (hadThreeLines) { 2987 // vertical centering 2988 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 2989 } 2990 2991 contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText)); 2992 contentView.setViewVisibility(R.id.big_text, View.VISIBLE); 2993 contentView.setViewVisibility(R.id.text2, View.GONE); 2994 2995 return contentView; 2996 } 2997 2998 /** 2999 * @hide 3000 */ 3001 @Override 3002 public Notification buildStyled(Notification wip) { 3003 wip.bigContentView = makeBigContentView(); 3004 3005 wip.extras.putCharSequence(EXTRA_TEXT, mBigText); 3006 3007 return wip; 3008 } 3009 } 3010 3011 /** 3012 * Helper class for generating large-format notifications that include a list of (up to 5) strings. 3013 * 3014 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 3015 * <pre class="prettyprint"> 3016 * Notification noti = new Notification.InboxStyle( 3017 * new Notification.Builder() 3018 * .setContentTitle("5 New mails from " + sender.toString()) 3019 * .setContentText(subject) 3020 * .setSmallIcon(R.drawable.new_mail) 3021 * .setLargeIcon(aBitmap)) 3022 * .addLine(str1) 3023 * .addLine(str2) 3024 * .setContentTitle("") 3025 * .setSummaryText("+3 more") 3026 * .build(); 3027 * </pre> 3028 * 3029 * @see Notification#bigContentView 3030 */ 3031 public static class InboxStyle extends Style { 3032 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5); 3033 3034 public InboxStyle() { 3035 } 3036 3037 public InboxStyle(Builder builder) { 3038 setBuilder(builder); 3039 } 3040 3041 /** 3042 * Overrides ContentTitle in the big form of the template. 3043 * This defaults to the value passed to setContentTitle(). 3044 */ 3045 public InboxStyle setBigContentTitle(CharSequence title) { 3046 internalSetBigContentTitle(safeCharSequence(title)); 3047 return this; 3048 } 3049 3050 /** 3051 * Set the first line of text after the detail section in the big form of the template. 3052 */ 3053 public InboxStyle setSummaryText(CharSequence cs) { 3054 internalSetSummaryText(safeCharSequence(cs)); 3055 return this; 3056 } 3057 3058 /** 3059 * Append a line to the digest section of the Inbox notification. 3060 */ 3061 public InboxStyle addLine(CharSequence cs) { 3062 mTexts.add(safeCharSequence(cs)); 3063 return this; 3064 } 3065 3066 /** 3067 * @hide 3068 */ 3069 public void addExtras(Bundle extras) { 3070 super.addExtras(extras); 3071 CharSequence[] a = new CharSequence[mTexts.size()]; 3072 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a)); 3073 } 3074 3075 private RemoteViews makeBigContentView() { 3076 // Remove the content text so line3 disappears unless you have a summary 3077 mBuilder.mContentText = null; 3078 RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource()); 3079 3080 contentView.setViewVisibility(R.id.text2, View.GONE); 3081 3082 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3, 3083 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6}; 3084 3085 // Make sure all rows are gone in case we reuse a view. 3086 for (int rowId : rowIds) { 3087 contentView.setViewVisibility(rowId, View.GONE); 3088 } 3089 3090 3091 int i=0; 3092 while (i < mTexts.size() && i < rowIds.length) { 3093 CharSequence str = mTexts.get(i); 3094 if (str != null && !str.equals("")) { 3095 contentView.setViewVisibility(rowIds[i], View.VISIBLE); 3096 contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str)); 3097 } 3098 i++; 3099 } 3100 3101 contentView.setViewVisibility(R.id.inbox_end_pad, 3102 mTexts.size() > 0 ? View.VISIBLE : View.GONE); 3103 3104 contentView.setViewVisibility(R.id.inbox_more, 3105 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE); 3106 3107 return contentView; 3108 } 3109 3110 /** 3111 * @hide 3112 */ 3113 @Override 3114 public Notification buildStyled(Notification wip) { 3115 wip.bigContentView = makeBigContentView(); 3116 3117 return wip; 3118 } 3119 } 3120 3121 /** 3122 * Notification style for media playback notifications. 3123 * 3124 * In the expanded form, {@link Notification#bigContentView}, up to 5 3125 * {@link Notification.Action}s specified with 3126 * {@link Notification.Builder#addAction(int, CharSequence, PendingIntent) addAction} will be 3127 * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to 3128 * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be 3129 * treated as album artwork. 3130 * 3131 * Unlike the other styles provided here, MediaStyle can also modify the standard-size 3132 * {@link Notification#contentView}; by providing action indices to 3133 * {@link #setShowActionsInCompactView(int...)} you can promote up to 2 actions to be displayed 3134 * in the standard view alongside the usual content. 3135 * 3136 * Finally, if you attach a {@link android.media.session.MediaSessionToken} using 3137 * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSessionToken)}, 3138 * the System UI can identify this as a notification representing an active media session 3139 * and respond accordingly (by showing album artwork in the lockscreen, for example). 3140 * 3141 * To use this style with your Notification, feed it to 3142 * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so: 3143 * <pre class="prettyprint"> 3144 * Notification noti = new Notification.Builder() 3145 * .setSmallIcon(R.drawable.ic_stat_player) 3146 * .setContentTitle("Track title") // these three lines are optional 3147 * .setContentText("Artist - Album") // if you use 3148 * .setLargeIcon(albumArtBitmap)) // setMediaSession(token, true) 3149 * .setMediaSession(mySession, true) 3150 * .setStyle(<b>new Notification.MediaStyle()</b>) 3151 * .build(); 3152 * </pre> 3153 * 3154 * @see Notification#bigContentView 3155 */ 3156 public static class MediaStyle extends Style { 3157 static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 2; 3158 static final int MAX_MEDIA_BUTTONS = 5; 3159 3160 private int[] mActionsToShowInCompact = null; 3161 private MediaSessionToken mToken; 3162 3163 public MediaStyle() { 3164 } 3165 3166 public MediaStyle(Builder builder) { 3167 setBuilder(builder); 3168 } 3169 3170 /** 3171 * Request up to 2 actions (by index in the order of addition) to be shown in the compact 3172 * notification view. 3173 */ 3174 public MediaStyle setShowActionsInCompactView(int...actions) { 3175 mActionsToShowInCompact = actions; 3176 return this; 3177 } 3178 3179 /** 3180 * Attach a {@link android.media.session.MediaSessionToken} to this Notification to provide 3181 * additional playback information and control to the SystemUI. 3182 */ 3183 public MediaStyle setMediaSession(MediaSessionToken token) { 3184 mToken = token; 3185 return this; 3186 } 3187 3188 @Override 3189 public Notification buildStyled(Notification wip) { 3190 wip.contentView = makeMediaContentView(); 3191 wip.bigContentView = makeMediaBigContentView(); 3192 3193 return wip; 3194 } 3195 3196 /** @hide */ 3197 @Override 3198 public void addExtras(Bundle extras) { 3199 super.addExtras(extras); 3200 3201 if (mToken != null) { 3202 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken); 3203 } 3204 } 3205 3206 private RemoteViews generateMediaActionButton(Action action) { 3207 final boolean tombstone = (action.actionIntent == null); 3208 RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(), 3209 R.layout.notification_quantum_media_action); 3210 button.setImageViewResource(R.id.action0, action.icon); 3211 if (!tombstone) { 3212 button.setOnClickPendingIntent(R.id.action0, action.actionIntent); 3213 } 3214 button.setContentDescription(R.id.action0, action.title); 3215 return button; 3216 } 3217 3218 private RemoteViews makeMediaContentView() { 3219 RemoteViews view = mBuilder.applyStandardTemplate( 3220 R.layout.notification_template_quantum_media, true /* 1U */); 3221 3222 final int numActions = mBuilder.mActions.size(); 3223 final int N = mActionsToShowInCompact == null 3224 ? 0 3225 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT); 3226 if (N > 0) { 3227 view.removeAllViews(R.id.actions); 3228 for (int i = 0; i < N; i++) { 3229 if (i >= numActions) { 3230 throw new IllegalArgumentException(String.format( 3231 "setShowActionsInCompactView: action %d out of bounds (max %d)", 3232 i, numActions - 1)); 3233 } 3234 3235 final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]); 3236 final RemoteViews button = generateMediaActionButton(action); 3237 view.addView(R.id.actions, button); 3238 } 3239 } 3240 return view; 3241 } 3242 3243 private RemoteViews makeMediaBigContentView() { 3244 RemoteViews big = mBuilder.applyStandardTemplate( 3245 R.layout.notification_template_quantum_big_media, false); 3246 3247 final int N = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS); 3248 if (N > 0) { 3249 big.removeAllViews(R.id.actions); 3250 for (int i=0; i<N; i++) { 3251 final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i)); 3252 big.addView(R.id.actions, button); 3253 } 3254 } 3255 return big; 3256 } 3257 } 3258 3259 /** 3260 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add 3261 * metadata or change options on a notification builder. 3262 */ 3263 public interface Extender { 3264 /** 3265 * Apply this extender to a notification builder. 3266 * @param builder the builder to be modified. 3267 * @return the build object for chaining. 3268 */ 3269 public Builder extend(Builder builder); 3270 } 3271 3272 /** 3273 * Helper class to add wearable extensions to notifications. 3274 * <p class="note"> See 3275 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications 3276 * for Android Wear</a> for more information on how to use this class. 3277 * <p> 3278 * To create a notification with wearable extensions: 3279 * <ol> 3280 * <li>Create a {@link android.app.Notification.Builder}, setting any desired 3281 * properties. 3282 * <li>Create a {@link android.app.Notification.WearableExtender}. 3283 * <li>Set wearable-specific properties using the 3284 * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}. 3285 * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a 3286 * notification. 3287 * <li>Post the notification to the notification system with the 3288 * {@code NotificationManager.notify(...)} methods. 3289 * </ol> 3290 * 3291 * <pre class="prettyprint"> 3292 * Notification notif = new Notification.Builder(mContext) 3293 * .setContentTitle("New mail from " + sender.toString()) 3294 * .setContentText(subject) 3295 * .setSmallIcon(R.drawable.new_mail) 3296 * .extend(new Notification.WearableExtender() 3297 * .setContentIcon(R.drawable.new_mail)) 3298 * .build(); 3299 * NotificationManager notificationManger = 3300 * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 3301 * notificationManger.notify(0, notif);</pre> 3302 * 3303 * <p>Wearable extensions can be accessed on an existing notification by using the 3304 * {@code WearableExtender(Notification)} constructor, 3305 * and then using the {@code get} methods to access values. 3306 * 3307 * <pre class="prettyprint"> 3308 * Notification.WearableExtender wearableExtender = new Notification.WearableExtender( 3309 * notification); 3310 * List<Notification> pages = wearableExtender.getPages(); 3311 * </pre> 3312 */ 3313 public static final class WearableExtender implements Extender { 3314 /** 3315 * Sentinel value for an action index that is unset. 3316 */ 3317 public static final int UNSET_ACTION_INDEX = -1; 3318 3319 /** 3320 * Size value for use with {@link #setCustomSizePreset} to show this notification with 3321 * default sizing. 3322 * <p>For custom display notifications created using {@link #setDisplayIntent}, 3323 * the default is {@link #SIZE_LARGE}. All other notifications size automatically based 3324 * on their content. 3325 */ 3326 public static final int SIZE_DEFAULT = 0; 3327 3328 /** 3329 * Size value for use with {@link #setCustomSizePreset} to show this notification 3330 * with an extra small size. 3331 * <p>This value is only applicable for custom display notifications created using 3332 * {@link #setDisplayIntent}. 3333 */ 3334 public static final int SIZE_XSMALL = 1; 3335 3336 /** 3337 * Size value for use with {@link #setCustomSizePreset} to show this notification 3338 * with a small size. 3339 * <p>This value is only applicable for custom display notifications created using 3340 * {@link #setDisplayIntent}. 3341 */ 3342 public static final int SIZE_SMALL = 2; 3343 3344 /** 3345 * Size value for use with {@link #setCustomSizePreset} to show this notification 3346 * with a medium size. 3347 * <p>This value is only applicable for custom display notifications created using 3348 * {@link #setDisplayIntent}. 3349 */ 3350 public static final int SIZE_MEDIUM = 3; 3351 3352 /** 3353 * Size value for use with {@link #setCustomSizePreset} to show this notification 3354 * with a large size. 3355 * <p>This value is only applicable for custom display notifications created using 3356 * {@link #setDisplayIntent}. 3357 */ 3358 public static final int SIZE_LARGE = 4; 3359 3360 /** Notification extra which contains wearable extensions */ 3361 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; 3362 3363 // Keys within EXTRA_WEARABLE_OPTIONS for wearable options. 3364 private static final String KEY_ACTIONS = "actions"; 3365 private static final String KEY_FLAGS = "flags"; 3366 private static final String KEY_DISPLAY_INTENT = "displayIntent"; 3367 private static final String KEY_PAGES = "pages"; 3368 private static final String KEY_BACKGROUND = "background"; 3369 private static final String KEY_CONTENT_ICON = "contentIcon"; 3370 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity"; 3371 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex"; 3372 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset"; 3373 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight"; 3374 private static final String KEY_GRAVITY = "gravity"; 3375 3376 // Flags bitwise-ored to mFlags 3377 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1; 3378 private static final int FLAG_HINT_HIDE_ICON = 1 << 1; 3379 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2; 3380 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3; 3381 3382 // Default value for flags integer 3383 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE; 3384 3385 private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END; 3386 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM; 3387 3388 private ArrayList<Action> mActions = new ArrayList<Action>(); 3389 private int mFlags = DEFAULT_FLAGS; 3390 private PendingIntent mDisplayIntent; 3391 private ArrayList<Notification> mPages = new ArrayList<Notification>(); 3392 private Bitmap mBackground; 3393 private int mContentIcon; 3394 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY; 3395 private int mContentActionIndex = UNSET_ACTION_INDEX; 3396 private int mCustomSizePreset = SIZE_DEFAULT; 3397 private int mCustomContentHeight; 3398 private int mGravity = DEFAULT_GRAVITY; 3399 3400 /** 3401 * Create a {@link android.app.Notification.WearableExtender} with default 3402 * options. 3403 */ 3404 public WearableExtender() { 3405 } 3406 3407 public WearableExtender(Notification notif) { 3408 Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS); 3409 if (wearableBundle != null) { 3410 List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS); 3411 if (actions != null) { 3412 mActions.addAll(actions); 3413 } 3414 3415 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS); 3416 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT); 3417 3418 Notification[] pages = getNotificationArrayFromBundle( 3419 wearableBundle, KEY_PAGES); 3420 if (pages != null) { 3421 Collections.addAll(mPages, pages); 3422 } 3423 3424 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND); 3425 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON); 3426 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY, 3427 DEFAULT_CONTENT_ICON_GRAVITY); 3428 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX, 3429 UNSET_ACTION_INDEX); 3430 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET, 3431 SIZE_DEFAULT); 3432 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT); 3433 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY); 3434 } 3435 } 3436 3437 /** 3438 * Apply wearable extensions to a notification that is being built. This is typically 3439 * called by the {@link android.app.Notification.Builder#extend} method of 3440 * {@link android.app.Notification.Builder}. 3441 */ 3442 @Override 3443 public Notification.Builder extend(Notification.Builder builder) { 3444 Bundle wearableBundle = new Bundle(); 3445 3446 if (!mActions.isEmpty()) { 3447 wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions); 3448 } 3449 if (mFlags != DEFAULT_FLAGS) { 3450 wearableBundle.putInt(KEY_FLAGS, mFlags); 3451 } 3452 if (mDisplayIntent != null) { 3453 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent); 3454 } 3455 if (!mPages.isEmpty()) { 3456 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray( 3457 new Notification[mPages.size()])); 3458 } 3459 if (mBackground != null) { 3460 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground); 3461 } 3462 if (mContentIcon != 0) { 3463 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon); 3464 } 3465 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) { 3466 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity); 3467 } 3468 if (mContentActionIndex != UNSET_ACTION_INDEX) { 3469 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX, 3470 mContentActionIndex); 3471 } 3472 if (mCustomSizePreset != SIZE_DEFAULT) { 3473 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset); 3474 } 3475 if (mCustomContentHeight != 0) { 3476 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight); 3477 } 3478 if (mGravity != DEFAULT_GRAVITY) { 3479 wearableBundle.putInt(KEY_GRAVITY, mGravity); 3480 } 3481 3482 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); 3483 return builder; 3484 } 3485 3486 @Override 3487 public WearableExtender clone() { 3488 WearableExtender that = new WearableExtender(); 3489 that.mActions = new ArrayList<Action>(this.mActions); 3490 that.mFlags = this.mFlags; 3491 that.mDisplayIntent = this.mDisplayIntent; 3492 that.mPages = new ArrayList<Notification>(this.mPages); 3493 that.mBackground = this.mBackground; 3494 that.mContentIcon = this.mContentIcon; 3495 that.mContentIconGravity = this.mContentIconGravity; 3496 that.mContentActionIndex = this.mContentActionIndex; 3497 that.mCustomSizePreset = this.mCustomSizePreset; 3498 that.mCustomContentHeight = this.mCustomContentHeight; 3499 that.mGravity = this.mGravity; 3500 return that; 3501 } 3502 3503 /** 3504 * Add a wearable action to this notification. 3505 * 3506 * <p>When wearable actions are added using this method, the set of actions that 3507 * show on a wearable device splits from devices that only show actions added 3508 * using {@link android.app.Notification.Builder#addAction}. This allows for customization 3509 * of which actions display on different devices. 3510 * 3511 * @param action the action to add to this notification 3512 * @return this object for method chaining 3513 * @see android.app.Notification.Action 3514 */ 3515 public WearableExtender addAction(Action action) { 3516 mActions.add(action); 3517 return this; 3518 } 3519 3520 /** 3521 * Adds wearable actions to this notification. 3522 * 3523 * <p>When wearable actions are added using this method, the set of actions that 3524 * show on a wearable device splits from devices that only show actions added 3525 * using {@link android.app.Notification.Builder#addAction}. This allows for customization 3526 * of which actions display on different devices. 3527 * 3528 * @param actions the actions to add to this notification 3529 * @return this object for method chaining 3530 * @see android.app.Notification.Action 3531 */ 3532 public WearableExtender addActions(List<Action> actions) { 3533 mActions.addAll(actions); 3534 return this; 3535 } 3536 3537 /** 3538 * Clear all wearable actions present on this builder. 3539 * @return this object for method chaining. 3540 * @see #addAction 3541 */ 3542 public WearableExtender clearActions() { 3543 mActions.clear(); 3544 return this; 3545 } 3546 3547 /** 3548 * Get the wearable actions present on this notification. 3549 */ 3550 public List<Action> getActions() { 3551 return mActions; 3552 } 3553 3554 /** 3555 * Set an intent to launch inside of an activity view when displaying 3556 * this notification. This {@link PendingIntent} should be for an activity. 3557 * 3558 * @param intent the {@link PendingIntent} for an activity 3559 * @return this object for method chaining 3560 * @see android.app.Notification.WearableExtender#getDisplayIntent 3561 */ 3562 public WearableExtender setDisplayIntent(PendingIntent intent) { 3563 mDisplayIntent = intent; 3564 return this; 3565 } 3566 3567 /** 3568 * Get the intent to launch inside of an activity view when displaying this 3569 * notification. This {@code PendingIntent} should be for an activity. 3570 */ 3571 public PendingIntent getDisplayIntent() { 3572 return mDisplayIntent; 3573 } 3574 3575 /** 3576 * Add an additional page of content to display with this notification. The current 3577 * notification forms the first page, and pages added using this function form 3578 * subsequent pages. This field can be used to separate a notification into multiple 3579 * sections. 3580 * 3581 * @param page the notification to add as another page 3582 * @return this object for method chaining 3583 * @see android.app.Notification.WearableExtender#getPages 3584 */ 3585 public WearableExtender addPage(Notification page) { 3586 mPages.add(page); 3587 return this; 3588 } 3589 3590 /** 3591 * Add additional pages of content to display with this notification. The current 3592 * notification forms the first page, and pages added using this function form 3593 * subsequent pages. This field can be used to separate a notification into multiple 3594 * sections. 3595 * 3596 * @param pages a list of notifications 3597 * @return this object for method chaining 3598 * @see android.app.Notification.WearableExtender#getPages 3599 */ 3600 public WearableExtender addPages(List<Notification> pages) { 3601 mPages.addAll(pages); 3602 return this; 3603 } 3604 3605 /** 3606 * Clear all additional pages present on this builder. 3607 * @return this object for method chaining. 3608 * @see #addPage 3609 */ 3610 public WearableExtender clearPages() { 3611 mPages.clear(); 3612 return this; 3613 } 3614 3615 /** 3616 * Get the array of additional pages of content for displaying this notification. The 3617 * current notification forms the first page, and elements within this array form 3618 * subsequent pages. This field can be used to separate a notification into multiple 3619 * sections. 3620 * @return the pages for this notification 3621 */ 3622 public List<Notification> getPages() { 3623 return mPages; 3624 } 3625 3626 /** 3627 * Set a background image to be displayed behind the notification content. 3628 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background 3629 * will work with any notification style. 3630 * 3631 * @param background the background bitmap 3632 * @return this object for method chaining 3633 * @see android.app.Notification.WearableExtender#getBackground 3634 */ 3635 public WearableExtender setBackground(Bitmap background) { 3636 mBackground = background; 3637 return this; 3638 } 3639 3640 /** 3641 * Get a background image to be displayed behind the notification content. 3642 * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background 3643 * will work with any notification style. 3644 * 3645 * @return the background image 3646 * @see android.app.Notification.WearableExtender#setBackground 3647 */ 3648 public Bitmap getBackground() { 3649 return mBackground; 3650 } 3651 3652 /** 3653 * Set an icon that goes with the content of this notification. 3654 */ 3655 public WearableExtender setContentIcon(int icon) { 3656 mContentIcon = icon; 3657 return this; 3658 } 3659 3660 /** 3661 * Get an icon that goes with the content of this notification. 3662 */ 3663 public int getContentIcon() { 3664 return mContentIcon; 3665 } 3666 3667 /** 3668 * Set the gravity that the content icon should have within the notification display. 3669 * Supported values include {@link android.view.Gravity#START} and 3670 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}. 3671 * @see #setContentIcon 3672 */ 3673 public WearableExtender setContentIconGravity(int contentIconGravity) { 3674 mContentIconGravity = contentIconGravity; 3675 return this; 3676 } 3677 3678 /** 3679 * Get the gravity that the content icon should have within the notification display. 3680 * Supported values include {@link android.view.Gravity#START} and 3681 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}. 3682 * @see #getContentIcon 3683 */ 3684 public int getContentIconGravity() { 3685 return mContentIconGravity; 3686 } 3687 3688 /** 3689 * Set an action from this notification's actions to be clickable with the content of 3690 * this notification page. This action will no longer display separately from the 3691 * notification content. This action's icon will display with optional subtext provided 3692 * by the action's title. 3693 * @param actionIndex The index of the action to hoist on the current notification page. 3694 * If wearable actions are present, this index will apply to that list, 3695 * otherwise it will apply to the main notification's actions list. 3696 */ 3697 public WearableExtender setContentAction(int actionIndex) { 3698 mContentActionIndex = actionIndex; 3699 return this; 3700 } 3701 3702 /** 3703 * Get the action index of an action from this notification to show as clickable with 3704 * the content of this notification page. When the user clicks this notification page, 3705 * this action will trigger. This action will no longer display separately from the 3706 * notification content. The action's icon will display with optional subtext provided 3707 * by the action's title. 3708 * 3709 * <p>If wearable specific actions are present, this index will apply to that list, 3710 * otherwise it will apply to the main notification's actions list. 3711 */ 3712 public int getContentAction() { 3713 return mContentActionIndex; 3714 } 3715 3716 /** 3717 * Set the gravity that this notification should have within the available viewport space. 3718 * Supported values include {@link android.view.Gravity#TOP}, 3719 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}. 3720 * The default value is {@link android.view.Gravity#BOTTOM}. 3721 */ 3722 public WearableExtender setGravity(int gravity) { 3723 mGravity = gravity; 3724 return this; 3725 } 3726 3727 /** 3728 * Get the gravity that this notification should have within the available viewport space. 3729 * Supported values include {@link android.view.Gravity#TOP}, 3730 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}. 3731 * The default value is {@link android.view.Gravity#BOTTOM}. 3732 */ 3733 public int getGravity() { 3734 return mGravity; 3735 } 3736 3737 /** 3738 * Set the custom size preset for the display of this notification out of the available 3739 * presets found in {@link android.app.Notification.WearableExtender}, e.g. 3740 * {@link #SIZE_LARGE}. 3741 * <p>Some custom size presets are only applicable for custom display notifications created 3742 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the 3743 * documentation for the preset in question. See also 3744 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}. 3745 */ 3746 public WearableExtender setCustomSizePreset(int sizePreset) { 3747 mCustomSizePreset = sizePreset; 3748 return this; 3749 } 3750 3751 /** 3752 * Get the custom size preset for the display of this notification out of the available 3753 * presets found in {@link android.app.Notification.WearableExtender}, e.g. 3754 * {@link #SIZE_LARGE}. 3755 * <p>Some custom size presets are only applicable for custom display notifications created 3756 * using {@link #setDisplayIntent}. Check the documentation for the preset in question. 3757 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}. 3758 */ 3759 public int getCustomSizePreset() { 3760 return mCustomSizePreset; 3761 } 3762 3763 /** 3764 * Set the custom height in pixels for the display of this notification's content. 3765 * <p>This option is only available for custom display notifications created 3766 * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also 3767 * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and 3768 * {@link #getCustomContentHeight}. 3769 */ 3770 public WearableExtender setCustomContentHeight(int height) { 3771 mCustomContentHeight = height; 3772 return this; 3773 } 3774 3775 /** 3776 * Get the custom height in pixels for the display of this notification's content. 3777 * <p>This option is only available for custom display notifications created 3778 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and 3779 * {@link #setCustomContentHeight}. 3780 */ 3781 public int getCustomContentHeight() { 3782 return mCustomContentHeight; 3783 } 3784 3785 /** 3786 * Set whether the scrolling position for the contents of this notification should start 3787 * at the bottom of the contents instead of the top when the contents are too long to 3788 * display within the screen. Default is false (start scroll at the top). 3789 */ 3790 public WearableExtender setStartScrollBottom(boolean startScrollBottom) { 3791 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom); 3792 return this; 3793 } 3794 3795 /** 3796 * Get whether the scrolling position for the contents of this notification should start 3797 * at the bottom of the contents instead of the top when the contents are too long to 3798 * display within the screen. Default is false (start scroll at the top). 3799 */ 3800 public boolean getStartScrollBottom() { 3801 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0; 3802 } 3803 3804 /** 3805 * Set whether the content intent is available when the wearable device is not connected 3806 * to a companion device. The user can still trigger this intent when the wearable device 3807 * is offline, but a visual hint will indicate that the content intent may not be available. 3808 * Defaults to true. 3809 */ 3810 public WearableExtender setContentIntentAvailableOffline( 3811 boolean contentIntentAvailableOffline) { 3812 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline); 3813 return this; 3814 } 3815 3816 /** 3817 * Get whether the content intent is available when the wearable device is not connected 3818 * to a companion device. The user can still trigger this intent when the wearable device 3819 * is offline, but a visual hint will indicate that the content intent may not be available. 3820 * Defaults to true. 3821 */ 3822 public boolean getContentIntentAvailableOffline() { 3823 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0; 3824 } 3825 3826 /** 3827 * Set a hint that this notification's icon should not be displayed. 3828 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise. 3829 * @return this object for method chaining 3830 */ 3831 public WearableExtender setHintHideIcon(boolean hintHideIcon) { 3832 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon); 3833 return this; 3834 } 3835 3836 /** 3837 * Get a hint that this notification's icon should not be displayed. 3838 * @return {@code true} if this icon should not be displayed, false otherwise. 3839 * The default value is {@code false} if this was never set. 3840 */ 3841 public boolean getHintHideIcon() { 3842 return (mFlags & FLAG_HINT_HIDE_ICON) != 0; 3843 } 3844 3845 /** 3846 * Set a visual hint that only the background image of this notification should be 3847 * displayed, and other semantic content should be hidden. This hint is only applicable 3848 * to sub-pages added using {@link #addPage}. 3849 */ 3850 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) { 3851 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly); 3852 return this; 3853 } 3854 3855 /** 3856 * Get a visual hint that only the background image of this notification should be 3857 * displayed, and other semantic content should be hidden. This hint is only applicable 3858 * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}. 3859 */ 3860 public boolean getHintShowBackgroundOnly() { 3861 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0; 3862 } 3863 3864 private void setFlag(int mask, boolean value) { 3865 if (value) { 3866 mFlags |= mask; 3867 } else { 3868 mFlags &= ~mask; 3869 } 3870 } 3871 } 3872 3873 /** 3874 * Get an array of Notification objects from a parcelable array bundle field. 3875 * Update the bundle to have a typed array so fetches in the future don't need 3876 * to do an array copy. 3877 */ 3878 private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) { 3879 Parcelable[] array = bundle.getParcelableArray(key); 3880 if (array instanceof Notification[] || array == null) { 3881 return (Notification[]) array; 3882 } 3883 Notification[] typedArray = Arrays.copyOf(array, array.length, 3884 Notification[].class); 3885 bundle.putParcelableArray(key, typedArray); 3886 return typedArray; 3887 } 3888} 3889