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