Notification.java revision 9a05b31aab8b4f2c431fda8c14aa7816eb4a91ad
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 com.android.internal.R; 20 21import android.content.Context; 22import android.content.Intent; 23import android.content.res.Resources; 24import android.graphics.Bitmap; 25import android.media.AudioManager; 26import android.net.Uri; 27import android.os.BadParcelableException; 28import android.os.Bundle; 29import android.os.Parcel; 30import android.os.Parcelable; 31import android.os.SystemClock; 32import android.os.UserHandle; 33import android.text.TextUtils; 34import android.util.Log; 35import android.util.TypedValue; 36import android.view.View; 37import android.widget.ProgressBar; 38import android.widget.RemoteViews; 39 40import java.text.NumberFormat; 41import java.util.ArrayList; 42 43/** 44 * A class that represents how a persistent notification is to be presented to 45 * the user using the {@link android.app.NotificationManager}. 46 * 47 * <p>The {@link Notification.Builder Notification.Builder} has been added to make it 48 * easier to construct Notifications.</p> 49 * 50 * <div class="special reference"> 51 * <h3>Developer Guides</h3> 52 * <p>For a guide to creating notifications, read the 53 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a> 54 * developer guide.</p> 55 * </div> 56 */ 57public class Notification implements Parcelable 58{ 59 private static final String TAG = "Notification"; 60 61 /** 62 * Use all default values (where applicable). 63 */ 64 public static final int DEFAULT_ALL = ~0; 65 66 /** 67 * Use the default notification sound. This will ignore any given 68 * {@link #sound}. 69 * 70 71 * @see #defaults 72 */ 73 74 public static final int DEFAULT_SOUND = 1; 75 76 /** 77 * Use the default notification vibrate. This will ignore any given 78 * {@link #vibrate}. Using phone vibration requires the 79 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission. 80 * 81 * @see #defaults 82 */ 83 84 public static final int DEFAULT_VIBRATE = 2; 85 86 /** 87 * Use the default notification lights. This will ignore the 88 * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or 89 * {@link #ledOnMS}. 90 * 91 * @see #defaults 92 */ 93 94 public static final int DEFAULT_LIGHTS = 4; 95 96 /** 97 * A timestamp related to this notification, in milliseconds since the epoch. 98 * 99 * Default value: {@link System#currentTimeMillis() Now}. 100 * 101 * Choose a timestamp that will be most relevant to the user. For most finite events, this 102 * corresponds to the time the event happened (or will happen, in the case of events that have 103 * yet to occur but about which the user is being informed). Indefinite events should be 104 * timestamped according to when the activity began. 105 * 106 * Some examples: 107 * 108 * <ul> 109 * <li>Notification of a new chat message should be stamped when the message was received.</li> 110 * <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li> 111 * <li>Notification of a completed file download should be stamped when the download finished.</li> 112 * <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li> 113 * <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time. 114 * <li>Notification of an ongoing countdown timer should be stamped with the timer's end time. 115 * </ul> 116 * 117 */ 118 public long when; 119 120 /** 121 * The resource id of a drawable to use as the icon in the status bar. 122 * This is required; notifications with an invalid icon resource will not be shown. 123 */ 124 public int icon; 125 126 /** 127 * If the icon in the status bar is to have more than one level, you can set this. Otherwise, 128 * leave it at its default value of 0. 129 * 130 * @see android.widget.ImageView#setImageLevel 131 * @see android.graphics.drawable#setLevel 132 */ 133 public int iconLevel; 134 135 /** 136 * The number of events that this notification represents. For example, in a new mail 137 * notification, this could be the number of unread messages. 138 * 139 * The system may or may not use this field to modify the appearance of the notification. For 140 * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was 141 * superimposed over the icon in the status bar. Starting with 142 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by 143 * {@link Notification.Builder} has displayed the number in the expanded notification view. 144 * 145 * If the number is 0 or negative, it is never shown. 146 */ 147 public int number; 148 149 /** 150 * The intent to execute when the expanded status entry is clicked. If 151 * this is an activity, it must include the 152 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires 153 * that you take care of task management as described in the 154 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back 155 * Stack</a> document. In particular, make sure to read the notification section 156 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling 157 * Notifications</a> for the correct ways to launch an application from a 158 * notification. 159 */ 160 public PendingIntent contentIntent; 161 162 /** 163 * The intent to execute when the notification is explicitly dismissed by the user, either with 164 * the "Clear All" button or by swiping it away individually. 165 * 166 * This probably shouldn't be launching an activity since several of those will be sent 167 * at the same time. 168 */ 169 public PendingIntent deleteIntent; 170 171 /** 172 * An intent to launch instead of posting the notification to the status bar. 173 * 174 * @see Notification.Builder#setFullScreenIntent 175 */ 176 public PendingIntent fullScreenIntent; 177 178 /** 179 * Text to scroll across the screen when this item is added to 180 * the status bar on large and smaller devices. 181 * 182 * @see #tickerView 183 */ 184 public CharSequence tickerText; 185 186 /** 187 * The view to show as the ticker in the status bar when the notification 188 * is posted. 189 */ 190 public RemoteViews tickerView; 191 192 /** 193 * The view that will represent this notification in the expanded status bar. 194 */ 195 public RemoteViews contentView; 196 197 /** 198 * A large-format version of {@link #contentView}, giving the Notification an 199 * opportunity to show more detail. The system UI may choose to show this 200 * instead of the normal content view at its discretion. 201 */ 202 public RemoteViews bigContentView; 203 204 /** 205 * The bitmap that may escape the bounds of the panel and bar. 206 */ 207 public Bitmap largeIcon; 208 209 /** 210 * The sound to play. 211 * 212 * <p> 213 * To play the default notification sound, see {@link #defaults}. 214 * </p> 215 */ 216 public Uri sound; 217 218 /** 219 * Use this constant as the value for audioStreamType to request that 220 * the default stream type for notifications be used. Currently the 221 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}. 222 */ 223 public static final int STREAM_DEFAULT = -1; 224 225 /** 226 * The audio stream type to use when playing the sound. 227 * Should be one of the STREAM_ constants from 228 * {@link android.media.AudioManager}. 229 */ 230 public int audioStreamType = STREAM_DEFAULT; 231 232 /** 233 * The pattern with which to vibrate. 234 * 235 * <p> 236 * To vibrate the default pattern, see {@link #defaults}. 237 * </p> 238 * 239 * @see android.os.Vibrator#vibrate(long[],int) 240 */ 241 public long[] vibrate; 242 243 /** 244 * The color of the led. The hardware will do its best approximation. 245 * 246 * @see #FLAG_SHOW_LIGHTS 247 * @see #flags 248 */ 249 public int ledARGB; 250 251 /** 252 * The number of milliseconds for the LED to be on while it's flashing. 253 * The hardware will do its best approximation. 254 * 255 * @see #FLAG_SHOW_LIGHTS 256 * @see #flags 257 */ 258 public int ledOnMS; 259 260 /** 261 * The number of milliseconds for the LED to be off while it's flashing. 262 * The hardware will do its best approximation. 263 * 264 * @see #FLAG_SHOW_LIGHTS 265 * @see #flags 266 */ 267 public int ledOffMS; 268 269 /** 270 * Specifies which values should be taken from the defaults. 271 * <p> 272 * To set, OR the desired from {@link #DEFAULT_SOUND}, 273 * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default 274 * values, use {@link #DEFAULT_ALL}. 275 * </p> 276 */ 277 public int defaults; 278 279 /** 280 * Bit to be bitwise-ored into the {@link #flags} field that should be 281 * set if you want the LED on for this notification. 282 * <ul> 283 * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB 284 * or 0 for both ledOnMS and ledOffMS.</li> 285 * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li> 286 * <li>To flash the LED, pass the number of milliseconds that it should 287 * be on and off to ledOnMS and ledOffMS.</li> 288 * </ul> 289 * <p> 290 * Since hardware varies, you are not guaranteed that any of the values 291 * you pass are honored exactly. Use the system defaults (TODO) if possible 292 * because they will be set to values that work on any given hardware. 293 * <p> 294 * The alpha channel must be set for forward compatibility. 295 * 296 */ 297 public static final int FLAG_SHOW_LIGHTS = 0x00000001; 298 299 /** 300 * Bit to be bitwise-ored into the {@link #flags} field that should be 301 * set if this notification is in reference to something that is ongoing, 302 * like a phone call. It should not be set if this notification is in 303 * reference to something that happened at a particular point in time, 304 * like a missed phone call. 305 */ 306 public static final int FLAG_ONGOING_EVENT = 0x00000002; 307 308 /** 309 * Bit to be bitwise-ored into the {@link #flags} field that if set, 310 * the audio will be repeated until the notification is 311 * cancelled or the notification window is opened. 312 */ 313 public static final int FLAG_INSISTENT = 0x00000004; 314 315 /** 316 * Bit to be bitwise-ored into the {@link #flags} field that should be 317 * set if you want the sound and/or vibration play each time the 318 * notification is sent, even if it has not been canceled before that. 319 */ 320 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008; 321 322 /** 323 * Bit to be bitwise-ored into the {@link #flags} field that should be 324 * set if the notification should be canceled when it is clicked by the 325 * user. 326 327 */ 328 public static final int FLAG_AUTO_CANCEL = 0x00000010; 329 330 /** 331 * Bit to be bitwise-ored into the {@link #flags} field that should be 332 * set if the notification should not be canceled when the user clicks 333 * the Clear all button. 334 */ 335 public static final int FLAG_NO_CLEAR = 0x00000020; 336 337 /** 338 * Bit to be bitwise-ored into the {@link #flags} field that should be 339 * set if this notification represents a currently running service. This 340 * will normally be set for you by {@link Service#startForeground}. 341 */ 342 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040; 343 344 /** 345 * Obsolete flag indicating high-priority notifications; use the priority field instead. 346 * 347 * @deprecated Use {@link #priority} with a positive value. 348 */ 349 public static final int FLAG_HIGH_PRIORITY = 0x00000080; 350 351 public int flags; 352 353 /** 354 * Default notification {@link #priority}. If your application does not prioritize its own 355 * notifications, use this value for all notifications. 356 */ 357 public static final int PRIORITY_DEFAULT = 0; 358 359 /** 360 * Lower {@link #priority}, for items that are less important. The UI may choose to show these 361 * items smaller, or at a different position in the list, compared with your app's 362 * {@link #PRIORITY_DEFAULT} items. 363 */ 364 public static final int PRIORITY_LOW = -1; 365 366 /** 367 * Lowest {@link #priority}; these items might not be shown to the user except under special 368 * circumstances, such as detailed notification logs. 369 */ 370 public static final int PRIORITY_MIN = -2; 371 372 /** 373 * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to 374 * show these items larger, or at a different position in notification lists, compared with 375 * your app's {@link #PRIORITY_DEFAULT} items. 376 */ 377 public static final int PRIORITY_HIGH = 1; 378 379 /** 380 * Highest {@link #priority}, for your application's most important items that require the 381 * user's prompt attention or input. 382 */ 383 public static final int PRIORITY_MAX = 2; 384 385 /** 386 * Relative priority for this notification. 387 * 388 * Priority is an indication of how much of the user's valuable attention should be consumed by 389 * this notification. Low-priority notifications may be hidden from the user in certain 390 * situations, while the user might be interrupted for a higher-priority notification. The 391 * system will make a determination about how to interpret this priority when presenting 392 * the notification. 393 */ 394 public int priority; 395 396 /** 397 * @hide 398 * Notification type: incoming call (voice or video) or similar synchronous communication request. 399 */ 400 public static final String KIND_CALL = "android.call"; 401 402 /** 403 * @hide 404 * Notification type: incoming direct message (SMS, instant message, etc.). 405 */ 406 public static final String KIND_MESSAGE = "android.message"; 407 408 /** 409 * @hide 410 * Notification type: asynchronous bulk message (email). 411 */ 412 public static final String KIND_EMAIL = "android.email"; 413 414 /** 415 * @hide 416 * Notification type: calendar event. 417 */ 418 public static final String KIND_EVENT = "android.event"; 419 420 /** 421 * @hide 422 * Notification type: promotion or advertisement. 423 */ 424 public static final String KIND_PROMO = "android.promo"; 425 426 /** 427 * @hide 428 * If this notification matches of one or more special types (see the <code>KIND_*</code> 429 * constants), add them here, best match first. 430 */ 431 public String[] kind; 432 433 /** 434 * Additional semantic data to be carried around with this Notification. 435 */ 436 public Bundle extras = new Bundle(); 437 438 // extras keys for Builder inputs 439 public static final String EXTRA_TITLE = "android.title"; 440 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big"; 441 public static final String EXTRA_TEXT = "android.text"; 442 public static final String EXTRA_SUB_TEXT = "android.subText"; 443 public static final String EXTRA_INFO_TEXT = "android.infoText"; 444 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText"; 445 public static final String EXTRA_SMALL_ICON = "android.icon"; 446 public static final String EXTRA_LARGE_ICON = "android.largeIcon"; 447 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big"; 448 public static final String EXTRA_PROGRESS = "android.progress"; 449 public static final String EXTRA_PROGRESS_MAX = "android.progressMax"; 450 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate"; 451 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer"; 452 public static final String EXTRA_SHOW_WHEN = "android.showWhen"; 453 public static final String EXTRA_PICTURE = "android.picture"; 454 public static final String EXTRA_TEXT_LINES = "android.textLines"; 455 456 // extras keys for other interesting pieces of information 457 public static final String EXTRA_PEOPLE = "android.people"; 458 459 /** 460 * @hide 461 * Extra added by NotificationManagerService to indicate whether a NotificationScorer 462 * modified the Notifications's score. 463 */ 464 public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified"; 465 466 /** 467 * Notification extra to specify heads up display preference. 468 * @hide 469 */ 470 public static final String EXTRA_AS_HEADS_UP = "headsup"; 471 472 /** 473 * Value for {@link #EXTRA_AS_HEADS_UP} indicating that heads up display is not appropriate. 474 * @hide 475 */ 476 public static final int HEADS_UP_NEVER = 0; 477 478 /** 479 * Default value for {@link #EXTRA_AS_HEADS_UP} indicating that heads up display is appropriate. 480 * @hide 481 */ 482 public static final int HEADS_UP_ALLOWED = 1; 483 484 /** 485 * Value for {@link #EXTRA_AS_HEADS_UP} that advocates for heads up display. 486 * @hide 487 */ 488 public static final int HEADS_UP_REQUESTED = 2; 489 490 /** 491 * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification. 492 */ 493 public static class Action implements Parcelable { 494 public int icon; 495 public CharSequence title; 496 public PendingIntent actionIntent; 497 @SuppressWarnings("unused") 498 public Action() { } 499 private Action(Parcel in) { 500 icon = in.readInt(); 501 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 502 if (in.readInt() == 1) { 503 actionIntent = PendingIntent.CREATOR.createFromParcel(in); 504 } 505 } 506 public Action(int icon_, CharSequence title_, PendingIntent intent_) { 507 this.icon = icon_; 508 this.title = title_; 509 this.actionIntent = intent_; 510 } 511 @Override 512 public Action clone() { 513 return new Action( 514 this.icon, 515 this.title.toString(), 516 this.actionIntent // safe to alias 517 ); 518 } 519 @Override 520 public int describeContents() { 521 return 0; 522 } 523 @Override 524 public void writeToParcel(Parcel out, int flags) { 525 out.writeInt(icon); 526 TextUtils.writeToParcel(title, out, flags); 527 if (actionIntent != null) { 528 out.writeInt(1); 529 actionIntent.writeToParcel(out, flags); 530 } else { 531 out.writeInt(0); 532 } 533 } 534 public static final Parcelable.Creator<Action> CREATOR 535 = new Parcelable.Creator<Action>() { 536 public Action createFromParcel(Parcel in) { 537 return new Action(in); 538 } 539 public Action[] newArray(int size) { 540 return new Action[size]; 541 } 542 }; 543 } 544 545 public Action[] actions; 546 547 /** 548 * Constructs a Notification object with default values. 549 * You might want to consider using {@link Builder} instead. 550 */ 551 public Notification() 552 { 553 this.when = System.currentTimeMillis(); 554 this.priority = PRIORITY_DEFAULT; 555 } 556 557 /** 558 * @hide 559 */ 560 public Notification(Context context, int icon, CharSequence tickerText, long when, 561 CharSequence contentTitle, CharSequence contentText, Intent contentIntent) 562 { 563 this.when = when; 564 this.icon = icon; 565 this.tickerText = tickerText; 566 setLatestEventInfo(context, contentTitle, contentText, 567 PendingIntent.getActivity(context, 0, contentIntent, 0)); 568 } 569 570 /** 571 * Constructs a Notification object with the information needed to 572 * have a status bar icon without the standard expanded view. 573 * 574 * @param icon The resource id of the icon to put in the status bar. 575 * @param tickerText The text that flows by in the status bar when the notification first 576 * activates. 577 * @param when The time to show in the time field. In the System.currentTimeMillis 578 * timebase. 579 * 580 * @deprecated Use {@link Builder} instead. 581 */ 582 @Deprecated 583 public Notification(int icon, CharSequence tickerText, long when) 584 { 585 this.icon = icon; 586 this.tickerText = tickerText; 587 this.when = when; 588 } 589 590 /** 591 * Unflatten the notification from a parcel. 592 */ 593 public Notification(Parcel parcel) 594 { 595 int version = parcel.readInt(); 596 597 when = parcel.readLong(); 598 icon = parcel.readInt(); 599 number = parcel.readInt(); 600 if (parcel.readInt() != 0) { 601 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel); 602 } 603 if (parcel.readInt() != 0) { 604 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel); 605 } 606 if (parcel.readInt() != 0) { 607 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 608 } 609 if (parcel.readInt() != 0) { 610 tickerView = RemoteViews.CREATOR.createFromParcel(parcel); 611 } 612 if (parcel.readInt() != 0) { 613 contentView = RemoteViews.CREATOR.createFromParcel(parcel); 614 } 615 if (parcel.readInt() != 0) { 616 largeIcon = Bitmap.CREATOR.createFromParcel(parcel); 617 } 618 defaults = parcel.readInt(); 619 flags = parcel.readInt(); 620 if (parcel.readInt() != 0) { 621 sound = Uri.CREATOR.createFromParcel(parcel); 622 } 623 624 audioStreamType = parcel.readInt(); 625 vibrate = parcel.createLongArray(); 626 ledARGB = parcel.readInt(); 627 ledOnMS = parcel.readInt(); 628 ledOffMS = parcel.readInt(); 629 iconLevel = parcel.readInt(); 630 631 if (parcel.readInt() != 0) { 632 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel); 633 } 634 635 priority = parcel.readInt(); 636 637 kind = parcel.createStringArray(); // may set kind to null 638 639 extras = parcel.readBundle(); // may be null 640 641 actions = parcel.createTypedArray(Action.CREATOR); // may be null 642 643 if (parcel.readInt() != 0) { 644 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel); 645 } 646 } 647 648 @Override 649 public Notification clone() { 650 Notification that = new Notification(); 651 cloneInto(that, true); 652 return that; 653 } 654 655 /** 656 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members 657 * of this into that. 658 * @hide 659 */ 660 public void cloneInto(Notification that, boolean heavy) { 661 that.when = this.when; 662 that.icon = this.icon; 663 that.number = this.number; 664 665 // PendingIntents are global, so there's no reason (or way) to clone them. 666 that.contentIntent = this.contentIntent; 667 that.deleteIntent = this.deleteIntent; 668 that.fullScreenIntent = this.fullScreenIntent; 669 670 if (this.tickerText != null) { 671 that.tickerText = this.tickerText.toString(); 672 } 673 if (heavy && this.tickerView != null) { 674 that.tickerView = this.tickerView.clone(); 675 } 676 if (heavy && this.contentView != null) { 677 that.contentView = this.contentView.clone(); 678 } 679 if (heavy && this.largeIcon != null) { 680 that.largeIcon = Bitmap.createBitmap(this.largeIcon); 681 } 682 that.iconLevel = this.iconLevel; 683 that.sound = this.sound; // android.net.Uri is immutable 684 that.audioStreamType = this.audioStreamType; 685 686 final long[] vibrate = this.vibrate; 687 if (vibrate != null) { 688 final int N = vibrate.length; 689 final long[] vib = that.vibrate = new long[N]; 690 System.arraycopy(vibrate, 0, vib, 0, N); 691 } 692 693 that.ledARGB = this.ledARGB; 694 that.ledOnMS = this.ledOnMS; 695 that.ledOffMS = this.ledOffMS; 696 that.defaults = this.defaults; 697 698 that.flags = this.flags; 699 700 that.priority = this.priority; 701 702 final String[] thiskind = this.kind; 703 if (thiskind != null) { 704 final int N = thiskind.length; 705 final String[] thatkind = that.kind = new String[N]; 706 System.arraycopy(thiskind, 0, thatkind, 0, N); 707 } 708 709 if (this.extras != null) { 710 try { 711 that.extras = new Bundle(this.extras); 712 // will unparcel 713 that.extras.size(); 714 } catch (BadParcelableException e) { 715 Log.e(TAG, "could not unparcel extras from notification: " + this, e); 716 that.extras = null; 717 } 718 } 719 720 if (this.actions != null) { 721 that.actions = new Action[this.actions.length]; 722 for(int i=0; i<this.actions.length; i++) { 723 that.actions[i] = this.actions[i].clone(); 724 } 725 } 726 727 if (heavy && this.bigContentView != null) { 728 that.bigContentView = this.bigContentView.clone(); 729 } 730 731 if (!heavy) { 732 that.lightenPayload(); // will clean out extras 733 } 734 } 735 736 /** 737 * Removes heavyweight parts of the Notification object for archival or for sending to 738 * listeners when the full contents are not necessary. 739 * @hide 740 */ 741 public final void lightenPayload() { 742 tickerView = null; 743 contentView = null; 744 bigContentView = null; 745 largeIcon = null; 746 if (extras != null) { 747 extras.remove(Notification.EXTRA_LARGE_ICON); 748 extras.remove(Notification.EXTRA_LARGE_ICON_BIG); 749 extras.remove(Notification.EXTRA_PICTURE); 750 } 751 } 752 753 /** 754 * Make sure this CharSequence is safe to put into a bundle, which basically 755 * means it had better not be some custom Parcelable implementation. 756 * @hide 757 */ 758 public static CharSequence safeCharSequence(CharSequence cs) { 759 if (cs instanceof Parcelable) { 760 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName() 761 + " instance is a custom Parcelable and not allowed in Notification"); 762 return cs.toString(); 763 } 764 765 return cs; 766 } 767 768 public int describeContents() { 769 return 0; 770 } 771 772 /** 773 * Flatten this notification from a parcel. 774 */ 775 public void writeToParcel(Parcel parcel, int flags) 776 { 777 parcel.writeInt(1); 778 779 parcel.writeLong(when); 780 parcel.writeInt(icon); 781 parcel.writeInt(number); 782 if (contentIntent != null) { 783 parcel.writeInt(1); 784 contentIntent.writeToParcel(parcel, 0); 785 } else { 786 parcel.writeInt(0); 787 } 788 if (deleteIntent != null) { 789 parcel.writeInt(1); 790 deleteIntent.writeToParcel(parcel, 0); 791 } else { 792 parcel.writeInt(0); 793 } 794 if (tickerText != null) { 795 parcel.writeInt(1); 796 TextUtils.writeToParcel(tickerText, parcel, flags); 797 } else { 798 parcel.writeInt(0); 799 } 800 if (tickerView != null) { 801 parcel.writeInt(1); 802 tickerView.writeToParcel(parcel, 0); 803 } else { 804 parcel.writeInt(0); 805 } 806 if (contentView != null) { 807 parcel.writeInt(1); 808 contentView.writeToParcel(parcel, 0); 809 } else { 810 parcel.writeInt(0); 811 } 812 if (largeIcon != null) { 813 parcel.writeInt(1); 814 largeIcon.writeToParcel(parcel, 0); 815 } else { 816 parcel.writeInt(0); 817 } 818 819 parcel.writeInt(defaults); 820 parcel.writeInt(this.flags); 821 822 if (sound != null) { 823 parcel.writeInt(1); 824 sound.writeToParcel(parcel, 0); 825 } else { 826 parcel.writeInt(0); 827 } 828 parcel.writeInt(audioStreamType); 829 parcel.writeLongArray(vibrate); 830 parcel.writeInt(ledARGB); 831 parcel.writeInt(ledOnMS); 832 parcel.writeInt(ledOffMS); 833 parcel.writeInt(iconLevel); 834 835 if (fullScreenIntent != null) { 836 parcel.writeInt(1); 837 fullScreenIntent.writeToParcel(parcel, 0); 838 } else { 839 parcel.writeInt(0); 840 } 841 842 parcel.writeInt(priority); 843 844 parcel.writeStringArray(kind); // ok for null 845 846 parcel.writeBundle(extras); // null ok 847 848 parcel.writeTypedArray(actions, 0); // null ok 849 850 if (bigContentView != null) { 851 parcel.writeInt(1); 852 bigContentView.writeToParcel(parcel, 0); 853 } else { 854 parcel.writeInt(0); 855 } 856 } 857 858 /** 859 * Parcelable.Creator that instantiates Notification objects 860 */ 861 public static final Parcelable.Creator<Notification> CREATOR 862 = new Parcelable.Creator<Notification>() 863 { 864 public Notification createFromParcel(Parcel parcel) 865 { 866 return new Notification(parcel); 867 } 868 869 public Notification[] newArray(int size) 870 { 871 return new Notification[size]; 872 } 873 }; 874 875 /** 876 * Sets the {@link #contentView} field to be a view with the standard "Latest Event" 877 * layout. 878 * 879 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields 880 * in the view.</p> 881 * @param context The context for your application / activity. 882 * @param contentTitle The title that goes in the expanded entry. 883 * @param contentText The text that goes in the expanded entry. 884 * @param contentIntent The intent to launch when the user clicks the expanded notification. 885 * If this is an activity, it must include the 886 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires 887 * that you take care of task management as described in the 888 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back 889 * Stack</a> document. 890 * 891 * @deprecated Use {@link Builder} instead. 892 */ 893 @Deprecated 894 public void setLatestEventInfo(Context context, 895 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { 896 Notification.Builder builder = new Notification.Builder(context); 897 898 // First, ensure that key pieces of information that may have been set directly 899 // are preserved 900 builder.setWhen(this.when); 901 builder.setSmallIcon(this.icon); 902 builder.setPriority(this.priority); 903 builder.setTicker(this.tickerText); 904 builder.setNumber(this.number); 905 builder.mFlags = this.flags; 906 builder.setSound(this.sound, this.audioStreamType); 907 builder.setDefaults(this.defaults); 908 builder.setVibrate(this.vibrate); 909 910 // now apply the latestEventInfo fields 911 if (contentTitle != null) { 912 builder.setContentTitle(contentTitle); 913 } 914 if (contentText != null) { 915 builder.setContentText(contentText); 916 } 917 builder.setContentIntent(contentIntent); 918 builder.buildInto(this); 919 } 920 921 @Override 922 public String toString() { 923 StringBuilder sb = new StringBuilder(); 924 sb.append("Notification(pri="); 925 sb.append(priority); 926 sb.append(" contentView="); 927 if (contentView != null) { 928 sb.append(contentView.getPackage()); 929 sb.append("/0x"); 930 sb.append(Integer.toHexString(contentView.getLayoutId())); 931 } else { 932 sb.append("null"); 933 } 934 // TODO(dsandler): defaults take precedence over local values, so reorder the branches below 935 sb.append(" vibrate="); 936 if ((this.defaults & DEFAULT_VIBRATE) != 0) { 937 sb.append("default"); 938 } else if (this.vibrate != null) { 939 int N = this.vibrate.length-1; 940 sb.append("["); 941 for (int i=0; i<N; i++) { 942 sb.append(this.vibrate[i]); 943 sb.append(','); 944 } 945 if (N != -1) { 946 sb.append(this.vibrate[N]); 947 } 948 sb.append("]"); 949 } else { 950 sb.append("null"); 951 } 952 sb.append(" sound="); 953 if ((this.defaults & DEFAULT_SOUND) != 0) { 954 sb.append("default"); 955 } else if (this.sound != null) { 956 sb.append(this.sound.toString()); 957 } else { 958 sb.append("null"); 959 } 960 sb.append(" defaults=0x"); 961 sb.append(Integer.toHexString(this.defaults)); 962 sb.append(" flags=0x"); 963 sb.append(Integer.toHexString(this.flags)); 964 sb.append(" kind=["); 965 if (this.kind == null) { 966 sb.append("null"); 967 } else { 968 for (int i=0; i<this.kind.length; i++) { 969 if (i>0) sb.append(","); 970 sb.append(this.kind[i]); 971 } 972 } 973 sb.append("]"); 974 if (actions != null) { 975 sb.append(" "); 976 sb.append(actions.length); 977 sb.append(" action"); 978 if (actions.length > 1) sb.append("s"); 979 } 980 sb.append(")"); 981 return sb.toString(); 982 } 983 984 /** {@hide} */ 985 public void setUser(UserHandle user) { 986 if (user.getIdentifier() == UserHandle.USER_ALL) { 987 user = UserHandle.OWNER; 988 } 989 if (tickerView != null) { 990 tickerView.setUser(user); 991 } 992 if (contentView != null) { 993 contentView.setUser(user); 994 } 995 if (bigContentView != null) { 996 bigContentView.setUser(user); 997 } 998 } 999 1000 /** 1001 * Builder class for {@link Notification} objects. 1002 * 1003 * Provides a convenient way to set the various fields of a {@link Notification} and generate 1004 * content views using the platform's notification layout template. If your app supports 1005 * versions of Android as old as API level 4, you can instead use 1006 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}, 1007 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support 1008 * library</a>. 1009 * 1010 * <p>Example: 1011 * 1012 * <pre class="prettyprint"> 1013 * Notification noti = new Notification.Builder(mContext) 1014 * .setContentTitle("New mail from " + sender.toString()) 1015 * .setContentText(subject) 1016 * .setSmallIcon(R.drawable.new_mail) 1017 * .setLargeIcon(aBitmap) 1018 * .build(); 1019 * </pre> 1020 */ 1021 public static class Builder { 1022 private static final int MAX_ACTION_BUTTONS = 3; 1023 1024 private Context mContext; 1025 1026 private long mWhen; 1027 private int mSmallIcon; 1028 private int mSmallIconLevel; 1029 private int mNumber; 1030 private CharSequence mContentTitle; 1031 private CharSequence mContentText; 1032 private CharSequence mContentInfo; 1033 private CharSequence mSubText; 1034 private PendingIntent mContentIntent; 1035 private RemoteViews mContentView; 1036 private PendingIntent mDeleteIntent; 1037 private PendingIntent mFullScreenIntent; 1038 private CharSequence mTickerText; 1039 private RemoteViews mTickerView; 1040 private Bitmap mLargeIcon; 1041 private Uri mSound; 1042 private int mAudioStreamType; 1043 private long[] mVibrate; 1044 private int mLedArgb; 1045 private int mLedOnMs; 1046 private int mLedOffMs; 1047 private int mDefaults; 1048 private int mFlags; 1049 private int mProgressMax; 1050 private int mProgress; 1051 private boolean mProgressIndeterminate; 1052 private ArrayList<String> mKindList = new ArrayList<String>(1); 1053 private Bundle mExtras; 1054 private int mPriority; 1055 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS); 1056 private boolean mUseChronometer; 1057 private Style mStyle; 1058 private boolean mShowWhen = true; 1059 1060 /** 1061 * Constructs a new Builder with the defaults: 1062 * 1063 1064 * <table> 1065 * <tr><th align=right>priority</th> 1066 * <td>{@link #PRIORITY_DEFAULT}</td></tr> 1067 * <tr><th align=right>when</th> 1068 * <td>now ({@link System#currentTimeMillis()})</td></tr> 1069 * <tr><th align=right>audio stream</th> 1070 * <td>{@link #STREAM_DEFAULT}</td></tr> 1071 * </table> 1072 * 1073 1074 * @param context 1075 * A {@link Context} that will be used by the Builder to construct the 1076 * RemoteViews. The Context will not be held past the lifetime of this Builder 1077 * object. 1078 */ 1079 public Builder(Context context) { 1080 mContext = context; 1081 1082 // Set defaults to match the defaults of a Notification 1083 mWhen = System.currentTimeMillis(); 1084 mAudioStreamType = STREAM_DEFAULT; 1085 mPriority = PRIORITY_DEFAULT; 1086 } 1087 1088 /** 1089 * Add a timestamp pertaining to the notification (usually the time the event occurred). 1090 * It will be shown in the notification content view by default; use 1091 * {@link Builder#setShowWhen(boolean) setShowWhen} to control this. 1092 * 1093 * @see Notification#when 1094 */ 1095 public Builder setWhen(long when) { 1096 mWhen = when; 1097 return this; 1098 } 1099 1100 /** 1101 * Control whether the timestamp set with {@link Builder#setWhen(long) setWhen} is shown 1102 * in the content view. 1103 */ 1104 public Builder setShowWhen(boolean show) { 1105 mShowWhen = show; 1106 return this; 1107 } 1108 1109 /** 1110 * Show the {@link Notification#when} field as a stopwatch. 1111 * 1112 * Instead of presenting <code>when</code> as a timestamp, the notification will show an 1113 * automatically updating display of the minutes and seconds since <code>when</code>. 1114 * 1115 * Useful when showing an elapsed time (like an ongoing phone call). 1116 * 1117 * @see android.widget.Chronometer 1118 * @see Notification#when 1119 */ 1120 public Builder setUsesChronometer(boolean b) { 1121 mUseChronometer = b; 1122 return this; 1123 } 1124 1125 /** 1126 * Set the small icon resource, which will be used to represent the notification in the 1127 * status bar. 1128 * 1129 1130 * The platform template for the expanded view will draw this icon in the left, unless a 1131 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small 1132 * icon will be moved to the right-hand side. 1133 * 1134 1135 * @param icon 1136 * A resource ID in the application's package of the drawable to use. 1137 * @see Notification#icon 1138 */ 1139 public Builder setSmallIcon(int icon) { 1140 mSmallIcon = icon; 1141 return this; 1142 } 1143 1144 /** 1145 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional 1146 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable 1147 * LevelListDrawable}. 1148 * 1149 * @param icon A resource ID in the application's package of the drawable to use. 1150 * @param level The level to use for the icon. 1151 * 1152 * @see Notification#icon 1153 * @see Notification#iconLevel 1154 */ 1155 public Builder setSmallIcon(int icon, int level) { 1156 mSmallIcon = icon; 1157 mSmallIconLevel = level; 1158 return this; 1159 } 1160 1161 /** 1162 * Set the first line of text in the platform notification template. 1163 */ 1164 public Builder setContentTitle(CharSequence title) { 1165 mContentTitle = safeCharSequence(title); 1166 return this; 1167 } 1168 1169 /** 1170 * Set the second line of text in the platform notification template. 1171 */ 1172 public Builder setContentText(CharSequence text) { 1173 mContentText = safeCharSequence(text); 1174 return this; 1175 } 1176 1177 /** 1178 * Set the third line of text in the platform notification template. 1179 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the 1180 * same location in the standard template. 1181 */ 1182 public Builder setSubText(CharSequence text) { 1183 mSubText = safeCharSequence(text); 1184 return this; 1185 } 1186 1187 /** 1188 * Set the large number at the right-hand side of the notification. This is 1189 * equivalent to setContentInfo, although it might show the number in a different 1190 * font size for readability. 1191 */ 1192 public Builder setNumber(int number) { 1193 mNumber = number; 1194 return this; 1195 } 1196 1197 /** 1198 * A small piece of additional information pertaining to this notification. 1199 * 1200 * The platform template will draw this on the last line of the notification, at the far 1201 * right (to the right of a smallIcon if it has been placed there). 1202 */ 1203 public Builder setContentInfo(CharSequence info) { 1204 mContentInfo = safeCharSequence(info); 1205 return this; 1206 } 1207 1208 /** 1209 * Set the progress this notification represents. 1210 * 1211 * The platform template will represent this using a {@link ProgressBar}. 1212 */ 1213 public Builder setProgress(int max, int progress, boolean indeterminate) { 1214 mProgressMax = max; 1215 mProgress = progress; 1216 mProgressIndeterminate = indeterminate; 1217 return this; 1218 } 1219 1220 /** 1221 * Supply a custom RemoteViews to use instead of the platform template. 1222 * 1223 * @see Notification#contentView 1224 */ 1225 public Builder setContent(RemoteViews views) { 1226 mContentView = views; 1227 return this; 1228 } 1229 1230 /** 1231 * Supply a {@link PendingIntent} to be sent when the notification is clicked. 1232 * 1233 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you 1234 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use 1235 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)} 1236 * to assign PendingIntents to individual views in that custom layout (i.e., to create 1237 * clickable buttons inside the notification view). 1238 * 1239 * @see Notification#contentIntent Notification.contentIntent 1240 */ 1241 public Builder setContentIntent(PendingIntent intent) { 1242 mContentIntent = intent; 1243 return this; 1244 } 1245 1246 /** 1247 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user. 1248 * 1249 * @see Notification#deleteIntent 1250 */ 1251 public Builder setDeleteIntent(PendingIntent intent) { 1252 mDeleteIntent = intent; 1253 return this; 1254 } 1255 1256 /** 1257 * An intent to launch instead of posting the notification to the status bar. 1258 * Only for use with extremely high-priority notifications demanding the user's 1259 * <strong>immediate</strong> attention, such as an incoming phone call or 1260 * alarm clock that the user has explicitly set to a particular time. 1261 * If this facility is used for something else, please give the user an option 1262 * to turn it off and use a normal notification, as this can be extremely 1263 * disruptive. 1264 * 1265 * @param intent The pending intent to launch. 1266 * @param highPriority Passing true will cause this notification to be sent 1267 * even if other notifications are suppressed. 1268 * 1269 * @see Notification#fullScreenIntent 1270 */ 1271 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) { 1272 mFullScreenIntent = intent; 1273 setFlag(FLAG_HIGH_PRIORITY, highPriority); 1274 return this; 1275 } 1276 1277 /** 1278 * Set the "ticker" text which is displayed in the status bar when the notification first 1279 * arrives. 1280 * 1281 * @see Notification#tickerText 1282 */ 1283 public Builder setTicker(CharSequence tickerText) { 1284 mTickerText = safeCharSequence(tickerText); 1285 return this; 1286 } 1287 1288 /** 1289 * Set the text that is displayed in the status bar when the notification first 1290 * arrives, and also a RemoteViews object that may be displayed instead on some 1291 * devices. 1292 * 1293 * @see Notification#tickerText 1294 * @see Notification#tickerView 1295 */ 1296 public Builder setTicker(CharSequence tickerText, RemoteViews views) { 1297 mTickerText = safeCharSequence(tickerText); 1298 mTickerView = views; 1299 return this; 1300 } 1301 1302 /** 1303 * Add a large icon to the notification (and the ticker on some devices). 1304 * 1305 * In the platform template, this image will be shown on the left of the notification view 1306 * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side). 1307 * 1308 * @see Notification#largeIcon 1309 */ 1310 public Builder setLargeIcon(Bitmap icon) { 1311 mLargeIcon = icon; 1312 return this; 1313 } 1314 1315 /** 1316 * Set the sound to play. 1317 * 1318 * It will be played on the {@link #STREAM_DEFAULT default stream} for notifications. 1319 * 1320 * @see Notification#sound 1321 */ 1322 public Builder setSound(Uri sound) { 1323 mSound = sound; 1324 mAudioStreamType = STREAM_DEFAULT; 1325 return this; 1326 } 1327 1328 /** 1329 * Set the sound to play, along with a specific stream on which to play it. 1330 * 1331 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants. 1332 * 1333 * @see Notification#sound 1334 */ 1335 public Builder setSound(Uri sound, int streamType) { 1336 mSound = sound; 1337 mAudioStreamType = streamType; 1338 return this; 1339 } 1340 1341 /** 1342 * Set the vibration pattern to use. 1343 * 1344 1345 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the 1346 * <code>pattern</code> parameter. 1347 * 1348 1349 * @see Notification#vibrate 1350 */ 1351 public Builder setVibrate(long[] pattern) { 1352 mVibrate = pattern; 1353 return this; 1354 } 1355 1356 /** 1357 * Set the desired color for the indicator LED on the device, as well as the 1358 * blink duty cycle (specified in milliseconds). 1359 * 1360 1361 * Not all devices will honor all (or even any) of these values. 1362 * 1363 1364 * @see Notification#ledARGB 1365 * @see Notification#ledOnMS 1366 * @see Notification#ledOffMS 1367 */ 1368 public Builder setLights(int argb, int onMs, int offMs) { 1369 mLedArgb = argb; 1370 mLedOnMs = onMs; 1371 mLedOffMs = offMs; 1372 return this; 1373 } 1374 1375 /** 1376 * Set whether this is an "ongoing" notification. 1377 * 1378 1379 * Ongoing notifications cannot be dismissed by the user, so your application or service 1380 * must take care of canceling them. 1381 * 1382 1383 * They are typically used to indicate a background task that the user is actively engaged 1384 * with (e.g., playing music) or is pending in some way and therefore occupying the device 1385 * (e.g., a file download, sync operation, active network connection). 1386 * 1387 1388 * @see Notification#FLAG_ONGOING_EVENT 1389 * @see Service#setForeground(boolean) 1390 */ 1391 public Builder setOngoing(boolean ongoing) { 1392 setFlag(FLAG_ONGOING_EVENT, ongoing); 1393 return this; 1394 } 1395 1396 /** 1397 * Set this flag if you would only like the sound, vibrate 1398 * and ticker to be played if the notification is not already showing. 1399 * 1400 * @see Notification#FLAG_ONLY_ALERT_ONCE 1401 */ 1402 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) { 1403 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce); 1404 return this; 1405 } 1406 1407 /** 1408 * Make this notification automatically dismissed when the user touches it. The 1409 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens. 1410 * 1411 * @see Notification#FLAG_AUTO_CANCEL 1412 */ 1413 public Builder setAutoCancel(boolean autoCancel) { 1414 setFlag(FLAG_AUTO_CANCEL, autoCancel); 1415 return this; 1416 } 1417 1418 /** 1419 * Set which notification properties will be inherited from system defaults. 1420 * <p> 1421 * The value should be one or more of the following fields combined with 1422 * bitwise-or: 1423 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. 1424 * <p> 1425 * For all default values, use {@link #DEFAULT_ALL}. 1426 */ 1427 public Builder setDefaults(int defaults) { 1428 mDefaults = defaults; 1429 return this; 1430 } 1431 1432 /** 1433 * Set the priority of this notification. 1434 * 1435 * @see Notification#priority 1436 */ 1437 public Builder setPriority(int pri) { 1438 mPriority = pri; 1439 return this; 1440 } 1441 1442 /** 1443 * @hide 1444 * 1445 * Add a kind (category) to this notification. Optional. 1446 * 1447 * @see Notification#kind 1448 */ 1449 public Builder addKind(String k) { 1450 mKindList.add(k); 1451 return this; 1452 } 1453 1454 /** 1455 * Add metadata to this notification. 1456 * 1457 * A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's 1458 * current contents are copied into the Notification each time {@link #build()} is 1459 * called. 1460 * 1461 * @see Notification#extras 1462 */ 1463 public Builder setExtras(Bundle bag) { 1464 mExtras = bag; 1465 return this; 1466 } 1467 1468 /** 1469 * Add an action to this notification. Actions are typically displayed by 1470 * the system as a button adjacent to the notification content. 1471 * <br> 1472 * A notification displays up to 3 actions, from left to right in the order they were added. 1473 * 1474 * @param icon Resource ID of a drawable that represents the action. 1475 * @param title Text describing the action. 1476 * @param intent PendingIntent to be fired when the action is invoked. 1477 */ 1478 public Builder addAction(int icon, CharSequence title, PendingIntent intent) { 1479 mActions.add(new Action(icon, safeCharSequence(title), intent)); 1480 return this; 1481 } 1482 1483 /** 1484 * Add a rich notification style to be applied at build time. 1485 * 1486 * @param style Object responsible for modifying the notification style. 1487 */ 1488 public Builder setStyle(Style style) { 1489 if (mStyle != style) { 1490 mStyle = style; 1491 if (mStyle != null) { 1492 mStyle.setBuilder(this); 1493 } 1494 } 1495 return this; 1496 } 1497 1498 private void setFlag(int mask, boolean value) { 1499 if (value) { 1500 mFlags |= mask; 1501 } else { 1502 mFlags &= ~mask; 1503 } 1504 } 1505 1506 private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) { 1507 RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId); 1508 boolean showLine3 = false; 1509 boolean showLine2 = false; 1510 int smallIconImageViewId = R.id.icon; 1511 if (mLargeIcon != null) { 1512 contentView.setImageViewBitmap(R.id.icon, mLargeIcon); 1513 smallIconImageViewId = R.id.right_icon; 1514 } 1515 if (mPriority < PRIORITY_LOW) { 1516 contentView.setInt(R.id.icon, 1517 "setBackgroundResource", R.drawable.notification_template_icon_low_bg); 1518 contentView.setInt(R.id.status_bar_latest_event_content, 1519 "setBackgroundResource", R.drawable.notification_bg_low); 1520 } 1521 if (mSmallIcon != 0) { 1522 contentView.setImageViewResource(smallIconImageViewId, mSmallIcon); 1523 contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE); 1524 } else { 1525 contentView.setViewVisibility(smallIconImageViewId, View.GONE); 1526 } 1527 if (mContentTitle != null) { 1528 contentView.setTextViewText(R.id.title, mContentTitle); 1529 } 1530 if (mContentText != null) { 1531 contentView.setTextViewText(R.id.text, mContentText); 1532 showLine3 = true; 1533 } 1534 if (mContentInfo != null) { 1535 contentView.setTextViewText(R.id.info, mContentInfo); 1536 contentView.setViewVisibility(R.id.info, View.VISIBLE); 1537 showLine3 = true; 1538 } else if (mNumber > 0) { 1539 final int tooBig = mContext.getResources().getInteger( 1540 R.integer.status_bar_notification_info_maxnum); 1541 if (mNumber > tooBig) { 1542 contentView.setTextViewText(R.id.info, mContext.getResources().getString( 1543 R.string.status_bar_notification_info_overflow)); 1544 } else { 1545 NumberFormat f = NumberFormat.getIntegerInstance(); 1546 contentView.setTextViewText(R.id.info, f.format(mNumber)); 1547 } 1548 contentView.setViewVisibility(R.id.info, View.VISIBLE); 1549 showLine3 = true; 1550 } else { 1551 contentView.setViewVisibility(R.id.info, View.GONE); 1552 } 1553 1554 // Need to show three lines? 1555 if (mSubText != null) { 1556 contentView.setTextViewText(R.id.text, mSubText); 1557 if (mContentText != null) { 1558 contentView.setTextViewText(R.id.text2, mContentText); 1559 contentView.setViewVisibility(R.id.text2, View.VISIBLE); 1560 showLine2 = true; 1561 } else { 1562 contentView.setViewVisibility(R.id.text2, View.GONE); 1563 } 1564 } else { 1565 contentView.setViewVisibility(R.id.text2, View.GONE); 1566 if (mProgressMax != 0 || mProgressIndeterminate) { 1567 contentView.setProgressBar( 1568 R.id.progress, mProgressMax, mProgress, mProgressIndeterminate); 1569 contentView.setViewVisibility(R.id.progress, View.VISIBLE); 1570 showLine2 = true; 1571 } else { 1572 contentView.setViewVisibility(R.id.progress, View.GONE); 1573 } 1574 } 1575 if (showLine2) { 1576 if (fitIn1U) { 1577 // need to shrink all the type to make sure everything fits 1578 final Resources res = mContext.getResources(); 1579 final float subTextSize = res.getDimensionPixelSize( 1580 R.dimen.notification_subtext_size); 1581 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize); 1582 } 1583 // vertical centering 1584 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 1585 } 1586 1587 if (mWhen != 0 && mShowWhen) { 1588 if (mUseChronometer) { 1589 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE); 1590 contentView.setLong(R.id.chronometer, "setBase", 1591 mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis())); 1592 contentView.setBoolean(R.id.chronometer, "setStarted", true); 1593 } else { 1594 contentView.setViewVisibility(R.id.time, View.VISIBLE); 1595 contentView.setLong(R.id.time, "setTime", mWhen); 1596 } 1597 } else { 1598 contentView.setViewVisibility(R.id.time, View.GONE); 1599 } 1600 1601 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE); 1602 contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE); 1603 return contentView; 1604 } 1605 1606 private RemoteViews applyStandardTemplateWithActions(int layoutId) { 1607 RemoteViews big = applyStandardTemplate(layoutId, false); 1608 1609 int N = mActions.size(); 1610 if (N > 0) { 1611 // Log.d("Notification", "has actions: " + mContentText); 1612 big.setViewVisibility(R.id.actions, View.VISIBLE); 1613 big.setViewVisibility(R.id.action_divider, View.VISIBLE); 1614 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS; 1615 big.removeAllViews(R.id.actions); 1616 for (int i=0; i<N; i++) { 1617 final RemoteViews button = generateActionButton(mActions.get(i)); 1618 //Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title); 1619 big.addView(R.id.actions, button); 1620 } 1621 } 1622 return big; 1623 } 1624 1625 private RemoteViews makeContentView() { 1626 if (mContentView != null) { 1627 return mContentView; 1628 } else { 1629 return applyStandardTemplate(R.layout.notification_template_base, true); // no more special large_icon flavor 1630 } 1631 } 1632 1633 private RemoteViews makeTickerView() { 1634 if (mTickerView != null) { 1635 return mTickerView; 1636 } else { 1637 if (mContentView == null) { 1638 return applyStandardTemplate(mLargeIcon == null 1639 ? R.layout.status_bar_latest_event_ticker 1640 : R.layout.status_bar_latest_event_ticker_large_icon, true); 1641 } else { 1642 return null; 1643 } 1644 } 1645 } 1646 1647 private RemoteViews makeBigContentView() { 1648 if (mActions.size() == 0) return null; 1649 1650 return applyStandardTemplateWithActions(R.layout.notification_template_big_base); 1651 } 1652 1653 private RemoteViews generateActionButton(Action action) { 1654 final boolean tombstone = (action.actionIntent == null); 1655 RemoteViews button = new RemoteViews(mContext.getPackageName(), 1656 tombstone ? R.layout.notification_action_tombstone 1657 : R.layout.notification_action); 1658 button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0); 1659 button.setTextViewText(R.id.action0, action.title); 1660 if (!tombstone) { 1661 button.setOnClickPendingIntent(R.id.action0, action.actionIntent); 1662 } 1663 button.setContentDescription(R.id.action0, action.title); 1664 return button; 1665 } 1666 1667 /** 1668 * Apply the unstyled operations and return a new {@link Notification} object. 1669 */ 1670 private Notification buildUnstyled() { 1671 Notification n = new Notification(); 1672 n.when = mWhen; 1673 n.icon = mSmallIcon; 1674 n.iconLevel = mSmallIconLevel; 1675 n.number = mNumber; 1676 n.contentView = makeContentView(); 1677 n.contentIntent = mContentIntent; 1678 n.deleteIntent = mDeleteIntent; 1679 n.fullScreenIntent = mFullScreenIntent; 1680 n.tickerText = mTickerText; 1681 n.tickerView = makeTickerView(); 1682 n.largeIcon = mLargeIcon; 1683 n.sound = mSound; 1684 n.audioStreamType = mAudioStreamType; 1685 n.vibrate = mVibrate; 1686 n.ledARGB = mLedArgb; 1687 n.ledOnMS = mLedOnMs; 1688 n.ledOffMS = mLedOffMs; 1689 n.defaults = mDefaults; 1690 n.flags = mFlags; 1691 n.bigContentView = makeBigContentView(); 1692 if (mLedOnMs != 0 || mLedOffMs != 0) { 1693 n.flags |= FLAG_SHOW_LIGHTS; 1694 } 1695 if ((mDefaults & DEFAULT_LIGHTS) != 0) { 1696 n.flags |= FLAG_SHOW_LIGHTS; 1697 } 1698 if (mKindList.size() > 0) { 1699 n.kind = new String[mKindList.size()]; 1700 mKindList.toArray(n.kind); 1701 } else { 1702 n.kind = null; 1703 } 1704 n.priority = mPriority; 1705 if (mActions.size() > 0) { 1706 n.actions = new Action[mActions.size()]; 1707 mActions.toArray(n.actions); 1708 } 1709 1710 return n; 1711 } 1712 1713 /** 1714 * Capture, in the provided bundle, semantic information used in the construction of 1715 * this Notification object. 1716 * @hide 1717 */ 1718 public void addExtras(Bundle extras) { 1719 // Store original information used in the construction of this object 1720 extras.putCharSequence(EXTRA_TITLE, mContentTitle); 1721 extras.putCharSequence(EXTRA_TEXT, mContentText); 1722 extras.putCharSequence(EXTRA_SUB_TEXT, mSubText); 1723 extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo); 1724 extras.putInt(EXTRA_SMALL_ICON, mSmallIcon); 1725 extras.putInt(EXTRA_PROGRESS, mProgress); 1726 extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax); 1727 extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate); 1728 extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer); 1729 extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen); 1730 if (mLargeIcon != null) { 1731 extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); 1732 } 1733 } 1734 1735 /** 1736 * @deprecated Use {@link #build()} instead. 1737 */ 1738 @Deprecated 1739 public Notification getNotification() { 1740 return build(); 1741 } 1742 1743 /** 1744 * Combine all of the options that have been set and return a new {@link Notification} 1745 * object. 1746 */ 1747 public Notification build() { 1748 final Notification n; 1749 1750 if (mStyle != null) { 1751 n = mStyle.build(); 1752 } else { 1753 n = buildUnstyled(); 1754 } 1755 1756 n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle(); 1757 1758 addExtras(n.extras); 1759 if (mStyle != null) { 1760 mStyle.addExtras(n.extras); 1761 } 1762 1763 return n; 1764 } 1765 1766 /** 1767 * Apply this Builder to an existing {@link Notification} object. 1768 * 1769 * @hide 1770 */ 1771 public Notification buildInto(Notification n) { 1772 build().cloneInto(n, true); 1773 return n; 1774 } 1775 } 1776 1777 /** 1778 * An object that can apply a rich notification style to a {@link Notification.Builder} 1779 * object. 1780 */ 1781 public static abstract class Style 1782 { 1783 private CharSequence mBigContentTitle; 1784 private CharSequence mSummaryText = null; 1785 private boolean mSummaryTextSet = false; 1786 1787 protected Builder mBuilder; 1788 1789 /** 1790 * Overrides ContentTitle in the big form of the template. 1791 * This defaults to the value passed to setContentTitle(). 1792 */ 1793 protected void internalSetBigContentTitle(CharSequence title) { 1794 mBigContentTitle = title; 1795 } 1796 1797 /** 1798 * Set the first line of text after the detail section in the big form of the template. 1799 */ 1800 protected void internalSetSummaryText(CharSequence cs) { 1801 mSummaryText = cs; 1802 mSummaryTextSet = true; 1803 } 1804 1805 public void setBuilder(Builder builder) { 1806 if (mBuilder != builder) { 1807 mBuilder = builder; 1808 if (mBuilder != null) { 1809 mBuilder.setStyle(this); 1810 } 1811 } 1812 } 1813 1814 protected void checkBuilder() { 1815 if (mBuilder == null) { 1816 throw new IllegalArgumentException("Style requires a valid Builder object"); 1817 } 1818 } 1819 1820 protected RemoteViews getStandardView(int layoutId) { 1821 checkBuilder(); 1822 1823 if (mBigContentTitle != null) { 1824 mBuilder.setContentTitle(mBigContentTitle); 1825 } 1826 1827 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId); 1828 1829 if (mBigContentTitle != null && mBigContentTitle.equals("")) { 1830 contentView.setViewVisibility(R.id.line1, View.GONE); 1831 } else { 1832 contentView.setViewVisibility(R.id.line1, View.VISIBLE); 1833 } 1834 1835 // The last line defaults to the subtext, but can be replaced by mSummaryText 1836 final CharSequence overflowText = 1837 mSummaryTextSet ? mSummaryText 1838 : mBuilder.mSubText; 1839 if (overflowText != null) { 1840 contentView.setTextViewText(R.id.text, overflowText); 1841 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE); 1842 contentView.setViewVisibility(R.id.line3, View.VISIBLE); 1843 } else { 1844 contentView.setViewVisibility(R.id.overflow_divider, View.GONE); 1845 contentView.setViewVisibility(R.id.line3, View.GONE); 1846 } 1847 1848 return contentView; 1849 } 1850 1851 /** 1852 * @hide 1853 */ 1854 public void addExtras(Bundle extras) { 1855 if (mSummaryTextSet) { 1856 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText); 1857 } 1858 if (mBigContentTitle != null) { 1859 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle); 1860 } 1861 } 1862 1863 public abstract Notification build(); 1864 } 1865 1866 /** 1867 * Helper class for generating large-format notifications that include a large image attachment. 1868 * 1869 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1870 * <pre class="prettyprint"> 1871 * Notification noti = new Notification.BigPictureStyle( 1872 * new Notification.Builder() 1873 * .setContentTitle("New photo from " + sender.toString()) 1874 * .setContentText(subject) 1875 * .setSmallIcon(R.drawable.new_post) 1876 * .setLargeIcon(aBitmap)) 1877 * .bigPicture(aBigBitmap) 1878 * .build(); 1879 * </pre> 1880 * 1881 * @see Notification#bigContentView 1882 */ 1883 public static class BigPictureStyle extends Style { 1884 private Bitmap mPicture; 1885 private Bitmap mBigLargeIcon; 1886 private boolean mBigLargeIconSet = false; 1887 1888 public BigPictureStyle() { 1889 } 1890 1891 public BigPictureStyle(Builder builder) { 1892 setBuilder(builder); 1893 } 1894 1895 /** 1896 * Overrides ContentTitle in the big form of the template. 1897 * This defaults to the value passed to setContentTitle(). 1898 */ 1899 public BigPictureStyle setBigContentTitle(CharSequence title) { 1900 internalSetBigContentTitle(safeCharSequence(title)); 1901 return this; 1902 } 1903 1904 /** 1905 * Set the first line of text after the detail section in the big form of the template. 1906 */ 1907 public BigPictureStyle setSummaryText(CharSequence cs) { 1908 internalSetSummaryText(safeCharSequence(cs)); 1909 return this; 1910 } 1911 1912 /** 1913 * Provide the bitmap to be used as the payload for the BigPicture notification. 1914 */ 1915 public BigPictureStyle bigPicture(Bitmap b) { 1916 mPicture = b; 1917 return this; 1918 } 1919 1920 /** 1921 * Override the large icon when the big notification is shown. 1922 */ 1923 public BigPictureStyle bigLargeIcon(Bitmap b) { 1924 mBigLargeIconSet = true; 1925 mBigLargeIcon = b; 1926 return this; 1927 } 1928 1929 private RemoteViews makeBigContentView() { 1930 RemoteViews contentView = getStandardView(R.layout.notification_template_big_picture); 1931 1932 contentView.setImageViewBitmap(R.id.big_picture, mPicture); 1933 1934 return contentView; 1935 } 1936 1937 /** 1938 * @hide 1939 */ 1940 public void addExtras(Bundle extras) { 1941 super.addExtras(extras); 1942 1943 if (mBigLargeIconSet) { 1944 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon); 1945 } 1946 extras.putParcelable(EXTRA_PICTURE, mPicture); 1947 } 1948 1949 @Override 1950 public Notification build() { 1951 checkBuilder(); 1952 Notification wip = mBuilder.buildUnstyled(); 1953 if (mBigLargeIconSet ) { 1954 mBuilder.mLargeIcon = mBigLargeIcon; 1955 } 1956 wip.bigContentView = makeBigContentView(); 1957 return wip; 1958 } 1959 } 1960 1961 /** 1962 * Helper class for generating large-format notifications that include a lot of text. 1963 * 1964 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1965 * <pre class="prettyprint"> 1966 * Notification noti = new Notification.BigTextStyle( 1967 * new Notification.Builder() 1968 * .setContentTitle("New mail from " + sender.toString()) 1969 * .setContentText(subject) 1970 * .setSmallIcon(R.drawable.new_mail) 1971 * .setLargeIcon(aBitmap)) 1972 * .bigText(aVeryLongString) 1973 * .build(); 1974 * </pre> 1975 * 1976 * @see Notification#bigContentView 1977 */ 1978 public static class BigTextStyle extends Style { 1979 private CharSequence mBigText; 1980 1981 public BigTextStyle() { 1982 } 1983 1984 public BigTextStyle(Builder builder) { 1985 setBuilder(builder); 1986 } 1987 1988 /** 1989 * Overrides ContentTitle in the big form of the template. 1990 * This defaults to the value passed to setContentTitle(). 1991 */ 1992 public BigTextStyle setBigContentTitle(CharSequence title) { 1993 internalSetBigContentTitle(safeCharSequence(title)); 1994 return this; 1995 } 1996 1997 /** 1998 * Set the first line of text after the detail section in the big form of the template. 1999 */ 2000 public BigTextStyle setSummaryText(CharSequence cs) { 2001 internalSetSummaryText(safeCharSequence(cs)); 2002 return this; 2003 } 2004 2005 /** 2006 * Provide the longer text to be displayed in the big form of the 2007 * template in place of the content text. 2008 */ 2009 public BigTextStyle bigText(CharSequence cs) { 2010 mBigText = safeCharSequence(cs); 2011 return this; 2012 } 2013 2014 /** 2015 * @hide 2016 */ 2017 public void addExtras(Bundle extras) { 2018 super.addExtras(extras); 2019 2020 extras.putCharSequence(EXTRA_TEXT, mBigText); 2021 } 2022 2023 private RemoteViews makeBigContentView() { 2024 // Remove the content text so line3 only shows if you have a summary 2025 final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null); 2026 mBuilder.mContentText = null; 2027 2028 RemoteViews contentView = getStandardView(R.layout.notification_template_big_text); 2029 2030 if (hadThreeLines) { 2031 // vertical centering 2032 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 2033 } 2034 2035 contentView.setTextViewText(R.id.big_text, mBigText); 2036 contentView.setViewVisibility(R.id.big_text, View.VISIBLE); 2037 contentView.setViewVisibility(R.id.text2, View.GONE); 2038 2039 return contentView; 2040 } 2041 2042 @Override 2043 public Notification build() { 2044 checkBuilder(); 2045 Notification wip = mBuilder.buildUnstyled(); 2046 wip.bigContentView = makeBigContentView(); 2047 2048 wip.extras.putCharSequence(EXTRA_TEXT, mBigText); 2049 2050 return wip; 2051 } 2052 } 2053 2054 /** 2055 * Helper class for generating large-format notifications that include a list of (up to 5) strings. 2056 * 2057 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 2058 * <pre class="prettyprint"> 2059 * Notification noti = new Notification.InboxStyle( 2060 * new Notification.Builder() 2061 * .setContentTitle("5 New mails from " + sender.toString()) 2062 * .setContentText(subject) 2063 * .setSmallIcon(R.drawable.new_mail) 2064 * .setLargeIcon(aBitmap)) 2065 * .addLine(str1) 2066 * .addLine(str2) 2067 * .setContentTitle("") 2068 * .setSummaryText("+3 more") 2069 * .build(); 2070 * </pre> 2071 * 2072 * @see Notification#bigContentView 2073 */ 2074 public static class InboxStyle extends Style { 2075 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5); 2076 2077 public InboxStyle() { 2078 } 2079 2080 public InboxStyle(Builder builder) { 2081 setBuilder(builder); 2082 } 2083 2084 /** 2085 * Overrides ContentTitle in the big form of the template. 2086 * This defaults to the value passed to setContentTitle(). 2087 */ 2088 public InboxStyle setBigContentTitle(CharSequence title) { 2089 internalSetBigContentTitle(safeCharSequence(title)); 2090 return this; 2091 } 2092 2093 /** 2094 * Set the first line of text after the detail section in the big form of the template. 2095 */ 2096 public InboxStyle setSummaryText(CharSequence cs) { 2097 internalSetSummaryText(safeCharSequence(cs)); 2098 return this; 2099 } 2100 2101 /** 2102 * Append a line to the digest section of the Inbox notification. 2103 */ 2104 public InboxStyle addLine(CharSequence cs) { 2105 mTexts.add(safeCharSequence(cs)); 2106 return this; 2107 } 2108 2109 /** 2110 * @hide 2111 */ 2112 public void addExtras(Bundle extras) { 2113 super.addExtras(extras); 2114 CharSequence[] a = new CharSequence[mTexts.size()]; 2115 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a)); 2116 } 2117 2118 private RemoteViews makeBigContentView() { 2119 // Remove the content text so line3 disappears unless you have a summary 2120 mBuilder.mContentText = null; 2121 RemoteViews contentView = getStandardView(R.layout.notification_template_inbox); 2122 2123 contentView.setViewVisibility(R.id.text2, View.GONE); 2124 2125 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3, 2126 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6}; 2127 2128 // Make sure all rows are gone in case we reuse a view. 2129 for (int rowId : rowIds) { 2130 contentView.setViewVisibility(rowId, View.GONE); 2131 } 2132 2133 2134 int i=0; 2135 while (i < mTexts.size() && i < rowIds.length) { 2136 CharSequence str = mTexts.get(i); 2137 if (str != null && !str.equals("")) { 2138 contentView.setViewVisibility(rowIds[i], View.VISIBLE); 2139 contentView.setTextViewText(rowIds[i], str); 2140 } 2141 i++; 2142 } 2143 2144 contentView.setViewVisibility(R.id.inbox_end_pad, 2145 mTexts.size() > 0 ? View.VISIBLE : View.GONE); 2146 2147 contentView.setViewVisibility(R.id.inbox_more, 2148 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE); 2149 2150 return contentView; 2151 } 2152 2153 @Override 2154 public Notification build() { 2155 checkBuilder(); 2156 Notification wip = mBuilder.buildUnstyled(); 2157 wip.bigContentView = makeBigContentView(); 2158 2159 return wip; 2160 } 2161 } 2162} 2163