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