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