Notification.java revision 51c7510e493680b4aca1ed7695b35c52d2cd63ff
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 * Notification extra to specify heads up display preference. 461 * @hide 462 */ 463 public static final String EXTRA_AS_HEADS_UP = "headsup"; 464 465 /** 466 * Value for {@link #EXTRA_AS_HEADS_UP} indicating that heads up display is not appropriate. 467 * @hide 468 */ 469 public static final int HEADS_UP_NEVER = 0; 470 471 /** 472 * Default value for {@link #EXTRA_AS_HEADS_UP} indicating that heads up display is appropriate. 473 * @hide 474 */ 475 public static final int HEADS_UP_ALLOWED = 1; 476 477 /** 478 * Value for {@link #EXTRA_AS_HEADS_UP} that advocates for heads up display. 479 * @hide 480 */ 481 public static final int HEADS_UP_REQUESTED = 2; 482 483 /** 484 * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification. 485 */ 486 public static class Action implements Parcelable { 487 public int icon; 488 public CharSequence title; 489 public PendingIntent actionIntent; 490 @SuppressWarnings("unused") 491 public Action() { } 492 private Action(Parcel in) { 493 icon = in.readInt(); 494 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 495 if (in.readInt() == 1) { 496 actionIntent = PendingIntent.CREATOR.createFromParcel(in); 497 } 498 } 499 public Action(int icon_, CharSequence title_, PendingIntent intent_) { 500 this.icon = icon_; 501 this.title = title_; 502 this.actionIntent = intent_; 503 } 504 @Override 505 public Action clone() { 506 return new Action( 507 this.icon, 508 this.title.toString(), 509 this.actionIntent // safe to alias 510 ); 511 } 512 @Override 513 public int describeContents() { 514 return 0; 515 } 516 @Override 517 public void writeToParcel(Parcel out, int flags) { 518 out.writeInt(icon); 519 TextUtils.writeToParcel(title, out, flags); 520 if (actionIntent != null) { 521 out.writeInt(1); 522 actionIntent.writeToParcel(out, flags); 523 } else { 524 out.writeInt(0); 525 } 526 } 527 public static final Parcelable.Creator<Action> CREATOR 528 = new Parcelable.Creator<Action>() { 529 public Action createFromParcel(Parcel in) { 530 return new Action(in); 531 } 532 public Action[] newArray(int size) { 533 return new Action[size]; 534 } 535 }; 536 } 537 538 public Action[] actions; 539 540 /** 541 * Constructs a Notification object with default values. 542 * You might want to consider using {@link Builder} instead. 543 */ 544 public Notification() 545 { 546 this.when = System.currentTimeMillis(); 547 this.priority = PRIORITY_DEFAULT; 548 } 549 550 /** 551 * @hide 552 */ 553 public Notification(Context context, int icon, CharSequence tickerText, long when, 554 CharSequence contentTitle, CharSequence contentText, Intent contentIntent) 555 { 556 this.when = when; 557 this.icon = icon; 558 this.tickerText = tickerText; 559 setLatestEventInfo(context, contentTitle, contentText, 560 PendingIntent.getActivity(context, 0, contentIntent, 0)); 561 } 562 563 /** 564 * Constructs a Notification object with the information needed to 565 * have a status bar icon without the standard expanded view. 566 * 567 * @param icon The resource id of the icon to put in the status bar. 568 * @param tickerText The text that flows by in the status bar when the notification first 569 * activates. 570 * @param when The time to show in the time field. In the System.currentTimeMillis 571 * timebase. 572 * 573 * @deprecated Use {@link Builder} instead. 574 */ 575 @Deprecated 576 public Notification(int icon, CharSequence tickerText, long when) 577 { 578 this.icon = icon; 579 this.tickerText = tickerText; 580 this.when = when; 581 } 582 583 /** 584 * Unflatten the notification from a parcel. 585 */ 586 public Notification(Parcel parcel) 587 { 588 int version = parcel.readInt(); 589 590 when = parcel.readLong(); 591 icon = parcel.readInt(); 592 number = parcel.readInt(); 593 if (parcel.readInt() != 0) { 594 contentIntent = PendingIntent.CREATOR.createFromParcel(parcel); 595 } 596 if (parcel.readInt() != 0) { 597 deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel); 598 } 599 if (parcel.readInt() != 0) { 600 tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 601 } 602 if (parcel.readInt() != 0) { 603 tickerView = RemoteViews.CREATOR.createFromParcel(parcel); 604 } 605 if (parcel.readInt() != 0) { 606 contentView = RemoteViews.CREATOR.createFromParcel(parcel); 607 } 608 if (parcel.readInt() != 0) { 609 largeIcon = Bitmap.CREATOR.createFromParcel(parcel); 610 } 611 defaults = parcel.readInt(); 612 flags = parcel.readInt(); 613 if (parcel.readInt() != 0) { 614 sound = Uri.CREATOR.createFromParcel(parcel); 615 } 616 617 audioStreamType = parcel.readInt(); 618 vibrate = parcel.createLongArray(); 619 ledARGB = parcel.readInt(); 620 ledOnMS = parcel.readInt(); 621 ledOffMS = parcel.readInt(); 622 iconLevel = parcel.readInt(); 623 624 if (parcel.readInt() != 0) { 625 fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel); 626 } 627 628 priority = parcel.readInt(); 629 630 kind = parcel.createStringArray(); // may set kind to null 631 632 extras = parcel.readBundle(); // may be null 633 634 actions = parcel.createTypedArray(Action.CREATOR); // may be null 635 636 if (parcel.readInt() != 0) { 637 bigContentView = RemoteViews.CREATOR.createFromParcel(parcel); 638 } 639 } 640 641 @Override 642 public Notification clone() { 643 Notification that = new Notification(); 644 cloneInto(that, true); 645 return that; 646 } 647 648 /** 649 * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members 650 * of this into that. 651 * @hide 652 */ 653 public void cloneInto(Notification that, boolean heavy) { 654 that.when = this.when; 655 that.icon = this.icon; 656 that.number = this.number; 657 658 // PendingIntents are global, so there's no reason (or way) to clone them. 659 that.contentIntent = this.contentIntent; 660 that.deleteIntent = this.deleteIntent; 661 that.fullScreenIntent = this.fullScreenIntent; 662 663 if (this.tickerText != null) { 664 that.tickerText = this.tickerText.toString(); 665 } 666 if (heavy && this.tickerView != null) { 667 that.tickerView = this.tickerView.clone(); 668 } 669 if (heavy && this.contentView != null) { 670 that.contentView = this.contentView.clone(); 671 } 672 if (heavy && this.largeIcon != null) { 673 that.largeIcon = Bitmap.createBitmap(this.largeIcon); 674 } 675 that.iconLevel = this.iconLevel; 676 that.sound = this.sound; // android.net.Uri is immutable 677 that.audioStreamType = this.audioStreamType; 678 679 final long[] vibrate = this.vibrate; 680 if (vibrate != null) { 681 final int N = vibrate.length; 682 final long[] vib = that.vibrate = new long[N]; 683 System.arraycopy(vibrate, 0, vib, 0, N); 684 } 685 686 that.ledARGB = this.ledARGB; 687 that.ledOnMS = this.ledOnMS; 688 that.ledOffMS = this.ledOffMS; 689 that.defaults = this.defaults; 690 691 that.flags = this.flags; 692 693 that.priority = this.priority; 694 695 final String[] thiskind = this.kind; 696 if (thiskind != null) { 697 final int N = thiskind.length; 698 final String[] thatkind = that.kind = new String[N]; 699 System.arraycopy(thiskind, 0, thatkind, 0, N); 700 } 701 702 if (this.extras != null) { 703 try { 704 that.extras = new Bundle(this.extras); 705 // will unparcel 706 that.extras.size(); 707 } catch (BadParcelableException e) { 708 Log.e(TAG, "could not unparcel extras from notification: " + this, e); 709 that.extras = null; 710 } 711 } 712 713 if (this.actions != null) { 714 that.actions = new Action[this.actions.length]; 715 for(int i=0; i<this.actions.length; i++) { 716 that.actions[i] = this.actions[i].clone(); 717 } 718 } 719 720 if (heavy && this.bigContentView != null) { 721 that.bigContentView = this.bigContentView.clone(); 722 } 723 724 if (!heavy) { 725 that.lightenPayload(); // will clean out extras 726 } 727 } 728 729 /** 730 * Removes heavyweight parts of the Notification object for archival or for sending to 731 * listeners when the full contents are not necessary. 732 * @hide 733 */ 734 public final void lightenPayload() { 735 tickerView = null; 736 contentView = null; 737 bigContentView = null; 738 largeIcon = null; 739 if (extras != null) { 740 extras.remove(Notification.EXTRA_LARGE_ICON); 741 extras.remove(Notification.EXTRA_LARGE_ICON_BIG); 742 extras.remove(Notification.EXTRA_PICTURE); 743 } 744 } 745 746 /** 747 * Make sure this CharSequence is safe to put into a bundle, which basically 748 * means it had better not be some custom Parcelable implementation. 749 * @hide 750 */ 751 public static CharSequence safeCharSequence(CharSequence cs) { 752 if (cs instanceof Parcelable) { 753 Log.e(TAG, "warning: " + cs.getClass().getCanonicalName() 754 + " instance is a custom Parcelable and not allowed in Notification"); 755 return cs.toString(); 756 } 757 758 return cs; 759 } 760 761 public int describeContents() { 762 return 0; 763 } 764 765 /** 766 * Flatten this notification from a parcel. 767 */ 768 public void writeToParcel(Parcel parcel, int flags) 769 { 770 parcel.writeInt(1); 771 772 parcel.writeLong(when); 773 parcel.writeInt(icon); 774 parcel.writeInt(number); 775 if (contentIntent != null) { 776 parcel.writeInt(1); 777 contentIntent.writeToParcel(parcel, 0); 778 } else { 779 parcel.writeInt(0); 780 } 781 if (deleteIntent != null) { 782 parcel.writeInt(1); 783 deleteIntent.writeToParcel(parcel, 0); 784 } else { 785 parcel.writeInt(0); 786 } 787 if (tickerText != null) { 788 parcel.writeInt(1); 789 TextUtils.writeToParcel(tickerText, parcel, flags); 790 } else { 791 parcel.writeInt(0); 792 } 793 if (tickerView != null) { 794 parcel.writeInt(1); 795 tickerView.writeToParcel(parcel, 0); 796 } else { 797 parcel.writeInt(0); 798 } 799 if (contentView != null) { 800 parcel.writeInt(1); 801 contentView.writeToParcel(parcel, 0); 802 } else { 803 parcel.writeInt(0); 804 } 805 if (largeIcon != null) { 806 parcel.writeInt(1); 807 largeIcon.writeToParcel(parcel, 0); 808 } else { 809 parcel.writeInt(0); 810 } 811 812 parcel.writeInt(defaults); 813 parcel.writeInt(this.flags); 814 815 if (sound != null) { 816 parcel.writeInt(1); 817 sound.writeToParcel(parcel, 0); 818 } else { 819 parcel.writeInt(0); 820 } 821 parcel.writeInt(audioStreamType); 822 parcel.writeLongArray(vibrate); 823 parcel.writeInt(ledARGB); 824 parcel.writeInt(ledOnMS); 825 parcel.writeInt(ledOffMS); 826 parcel.writeInt(iconLevel); 827 828 if (fullScreenIntent != null) { 829 parcel.writeInt(1); 830 fullScreenIntent.writeToParcel(parcel, 0); 831 } else { 832 parcel.writeInt(0); 833 } 834 835 parcel.writeInt(priority); 836 837 parcel.writeStringArray(kind); // ok for null 838 839 parcel.writeBundle(extras); // null ok 840 841 parcel.writeTypedArray(actions, 0); // null ok 842 843 if (bigContentView != null) { 844 parcel.writeInt(1); 845 bigContentView.writeToParcel(parcel, 0); 846 } else { 847 parcel.writeInt(0); 848 } 849 } 850 851 /** 852 * Parcelable.Creator that instantiates Notification objects 853 */ 854 public static final Parcelable.Creator<Notification> CREATOR 855 = new Parcelable.Creator<Notification>() 856 { 857 public Notification createFromParcel(Parcel parcel) 858 { 859 return new Notification(parcel); 860 } 861 862 public Notification[] newArray(int size) 863 { 864 return new Notification[size]; 865 } 866 }; 867 868 /** 869 * Sets the {@link #contentView} field to be a view with the standard "Latest Event" 870 * layout. 871 * 872 * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields 873 * in the view.</p> 874 * @param context The context for your application / activity. 875 * @param contentTitle The title that goes in the expanded entry. 876 * @param contentText The text that goes in the expanded entry. 877 * @param contentIntent The intent to launch when the user clicks the expanded notification. 878 * If this is an activity, it must include the 879 * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires 880 * that you take care of task management as described in the 881 * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back 882 * Stack</a> document. 883 * 884 * @deprecated Use {@link Builder} instead. 885 */ 886 @Deprecated 887 public void setLatestEventInfo(Context context, 888 CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { 889 Notification.Builder builder = new Notification.Builder(context); 890 891 // First, ensure that key pieces of information that may have been set directly 892 // are preserved 893 builder.setWhen(this.when); 894 builder.setSmallIcon(this.icon); 895 builder.setPriority(this.priority); 896 builder.setTicker(this.tickerText); 897 builder.setNumber(this.number); 898 builder.mFlags = this.flags; 899 builder.setSound(this.sound, this.audioStreamType); 900 builder.setDefaults(this.defaults); 901 builder.setVibrate(this.vibrate); 902 903 // now apply the latestEventInfo fields 904 if (contentTitle != null) { 905 builder.setContentTitle(contentTitle); 906 } 907 if (contentText != null) { 908 builder.setContentText(contentText); 909 } 910 builder.setContentIntent(contentIntent); 911 builder.buildInto(this); 912 } 913 914 @Override 915 public String toString() { 916 StringBuilder sb = new StringBuilder(); 917 sb.append("Notification(pri="); 918 sb.append(priority); 919 sb.append(" contentView="); 920 if (contentView != null) { 921 sb.append(contentView.getPackage()); 922 sb.append("/0x"); 923 sb.append(Integer.toHexString(contentView.getLayoutId())); 924 } else { 925 sb.append("null"); 926 } 927 // TODO(dsandler): defaults take precedence over local values, so reorder the branches below 928 sb.append(" vibrate="); 929 if ((this.defaults & DEFAULT_VIBRATE) != 0) { 930 sb.append("default"); 931 } else if (this.vibrate != null) { 932 int N = this.vibrate.length-1; 933 sb.append("["); 934 for (int i=0; i<N; i++) { 935 sb.append(this.vibrate[i]); 936 sb.append(','); 937 } 938 if (N != -1) { 939 sb.append(this.vibrate[N]); 940 } 941 sb.append("]"); 942 } else { 943 sb.append("null"); 944 } 945 sb.append(" sound="); 946 if ((this.defaults & DEFAULT_SOUND) != 0) { 947 sb.append("default"); 948 } else if (this.sound != null) { 949 sb.append(this.sound.toString()); 950 } else { 951 sb.append("null"); 952 } 953 sb.append(" defaults=0x"); 954 sb.append(Integer.toHexString(this.defaults)); 955 sb.append(" flags=0x"); 956 sb.append(Integer.toHexString(this.flags)); 957 sb.append(" kind=["); 958 if (this.kind == null) { 959 sb.append("null"); 960 } else { 961 for (int i=0; i<this.kind.length; i++) { 962 if (i>0) sb.append(","); 963 sb.append(this.kind[i]); 964 } 965 } 966 sb.append("]"); 967 if (actions != null) { 968 sb.append(" "); 969 sb.append(actions.length); 970 sb.append(" action"); 971 if (actions.length > 1) sb.append("s"); 972 } 973 sb.append(")"); 974 return sb.toString(); 975 } 976 977 /** {@hide} */ 978 public void setUser(UserHandle user) { 979 if (user.getIdentifier() == UserHandle.USER_ALL) { 980 user = UserHandle.OWNER; 981 } 982 if (tickerView != null) { 983 tickerView.setUser(user); 984 } 985 if (contentView != null) { 986 contentView.setUser(user); 987 } 988 if (bigContentView != null) { 989 bigContentView.setUser(user); 990 } 991 } 992 993 /** 994 * Builder class for {@link Notification} objects. 995 * 996 * Provides a convenient way to set the various fields of a {@link Notification} and generate 997 * content views using the platform's notification layout template. If your app supports 998 * versions of Android as old as API level 4, you can instead use 999 * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}, 1000 * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support 1001 * library</a>. 1002 * 1003 * <p>Example: 1004 * 1005 * <pre class="prettyprint"> 1006 * Notification noti = new Notification.Builder(mContext) 1007 * .setContentTitle("New mail from " + sender.toString()) 1008 * .setContentText(subject) 1009 * .setSmallIcon(R.drawable.new_mail) 1010 * .setLargeIcon(aBitmap) 1011 * .build(); 1012 * </pre> 1013 */ 1014 public static class Builder { 1015 private static final int MAX_ACTION_BUTTONS = 3; 1016 1017 private Context mContext; 1018 1019 private long mWhen; 1020 private int mSmallIcon; 1021 private int mSmallIconLevel; 1022 private int mNumber; 1023 private CharSequence mContentTitle; 1024 private CharSequence mContentText; 1025 private CharSequence mContentInfo; 1026 private CharSequence mSubText; 1027 private PendingIntent mContentIntent; 1028 private RemoteViews mContentView; 1029 private PendingIntent mDeleteIntent; 1030 private PendingIntent mFullScreenIntent; 1031 private CharSequence mTickerText; 1032 private RemoteViews mTickerView; 1033 private Bitmap mLargeIcon; 1034 private Uri mSound; 1035 private int mAudioStreamType; 1036 private long[] mVibrate; 1037 private int mLedArgb; 1038 private int mLedOnMs; 1039 private int mLedOffMs; 1040 private int mDefaults; 1041 private int mFlags; 1042 private int mProgressMax; 1043 private int mProgress; 1044 private boolean mProgressIndeterminate; 1045 private ArrayList<String> mKindList = new ArrayList<String>(1); 1046 private Bundle mExtras; 1047 private int mPriority; 1048 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS); 1049 private boolean mUseChronometer; 1050 private Style mStyle; 1051 private boolean mShowWhen = true; 1052 1053 /** 1054 * Constructs a new Builder with the defaults: 1055 * 1056 1057 * <table> 1058 * <tr><th align=right>priority</th> 1059 * <td>{@link #PRIORITY_DEFAULT}</td></tr> 1060 * <tr><th align=right>when</th> 1061 * <td>now ({@link System#currentTimeMillis()})</td></tr> 1062 * <tr><th align=right>audio stream</th> 1063 * <td>{@link #STREAM_DEFAULT}</td></tr> 1064 * </table> 1065 * 1066 1067 * @param context 1068 * A {@link Context} that will be used by the Builder to construct the 1069 * RemoteViews. The Context will not be held past the lifetime of this Builder 1070 * object. 1071 */ 1072 public Builder(Context context) { 1073 mContext = context; 1074 1075 // Set defaults to match the defaults of a Notification 1076 mWhen = System.currentTimeMillis(); 1077 mAudioStreamType = STREAM_DEFAULT; 1078 mPriority = PRIORITY_DEFAULT; 1079 } 1080 1081 /** 1082 * Add a timestamp pertaining to the notification (usually the time the event occurred). 1083 * It will be shown in the notification content view by default; use 1084 * {@link Builder#setShowWhen(boolean) setShowWhen} to control this. 1085 * 1086 * @see Notification#when 1087 */ 1088 public Builder setWhen(long when) { 1089 mWhen = when; 1090 return this; 1091 } 1092 1093 /** 1094 * Control whether the timestamp set with {@link Builder#setWhen(long) setWhen} is shown 1095 * in the content view. 1096 */ 1097 public Builder setShowWhen(boolean show) { 1098 mShowWhen = show; 1099 return this; 1100 } 1101 1102 /** 1103 * Show the {@link Notification#when} field as a stopwatch. 1104 * 1105 * Instead of presenting <code>when</code> as a timestamp, the notification will show an 1106 * automatically updating display of the minutes and seconds since <code>when</code>. 1107 * 1108 * Useful when showing an elapsed time (like an ongoing phone call). 1109 * 1110 * @see android.widget.Chronometer 1111 * @see Notification#when 1112 */ 1113 public Builder setUsesChronometer(boolean b) { 1114 mUseChronometer = b; 1115 return this; 1116 } 1117 1118 /** 1119 * Set the small icon resource, which will be used to represent the notification in the 1120 * status bar. 1121 * 1122 1123 * The platform template for the expanded view will draw this icon in the left, unless a 1124 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small 1125 * icon will be moved to the right-hand side. 1126 * 1127 1128 * @param icon 1129 * A resource ID in the application's package of the drawable to use. 1130 * @see Notification#icon 1131 */ 1132 public Builder setSmallIcon(int icon) { 1133 mSmallIcon = icon; 1134 return this; 1135 } 1136 1137 /** 1138 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional 1139 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable 1140 * LevelListDrawable}. 1141 * 1142 * @param icon A resource ID in the application's package of the drawable to use. 1143 * @param level The level to use for the icon. 1144 * 1145 * @see Notification#icon 1146 * @see Notification#iconLevel 1147 */ 1148 public Builder setSmallIcon(int icon, int level) { 1149 mSmallIcon = icon; 1150 mSmallIconLevel = level; 1151 return this; 1152 } 1153 1154 /** 1155 * Set the first line of text in the platform notification template. 1156 */ 1157 public Builder setContentTitle(CharSequence title) { 1158 mContentTitle = safeCharSequence(title); 1159 return this; 1160 } 1161 1162 /** 1163 * Set the second line of text in the platform notification template. 1164 */ 1165 public Builder setContentText(CharSequence text) { 1166 mContentText = safeCharSequence(text); 1167 return this; 1168 } 1169 1170 /** 1171 * Set the third line of text in the platform notification template. 1172 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the 1173 * same location in the standard template. 1174 */ 1175 public Builder setSubText(CharSequence text) { 1176 mSubText = safeCharSequence(text); 1177 return this; 1178 } 1179 1180 /** 1181 * Set the large number at the right-hand side of the notification. This is 1182 * equivalent to setContentInfo, although it might show the number in a different 1183 * font size for readability. 1184 */ 1185 public Builder setNumber(int number) { 1186 mNumber = number; 1187 return this; 1188 } 1189 1190 /** 1191 * A small piece of additional information pertaining to this notification. 1192 * 1193 * The platform template will draw this on the last line of the notification, at the far 1194 * right (to the right of a smallIcon if it has been placed there). 1195 */ 1196 public Builder setContentInfo(CharSequence info) { 1197 mContentInfo = safeCharSequence(info); 1198 return this; 1199 } 1200 1201 /** 1202 * Set the progress this notification represents. 1203 * 1204 * The platform template will represent this using a {@link ProgressBar}. 1205 */ 1206 public Builder setProgress(int max, int progress, boolean indeterminate) { 1207 mProgressMax = max; 1208 mProgress = progress; 1209 mProgressIndeterminate = indeterminate; 1210 return this; 1211 } 1212 1213 /** 1214 * Supply a custom RemoteViews to use instead of the platform template. 1215 * 1216 * @see Notification#contentView 1217 */ 1218 public Builder setContent(RemoteViews views) { 1219 mContentView = views; 1220 return this; 1221 } 1222 1223 /** 1224 * Supply a {@link PendingIntent} to be sent when the notification is clicked. 1225 * 1226 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you 1227 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use 1228 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)} 1229 * to assign PendingIntents to individual views in that custom layout (i.e., to create 1230 * clickable buttons inside the notification view). 1231 * 1232 * @see Notification#contentIntent Notification.contentIntent 1233 */ 1234 public Builder setContentIntent(PendingIntent intent) { 1235 mContentIntent = intent; 1236 return this; 1237 } 1238 1239 /** 1240 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user. 1241 * 1242 * @see Notification#deleteIntent 1243 */ 1244 public Builder setDeleteIntent(PendingIntent intent) { 1245 mDeleteIntent = intent; 1246 return this; 1247 } 1248 1249 /** 1250 * An intent to launch instead of posting the notification to the status bar. 1251 * Only for use with extremely high-priority notifications demanding the user's 1252 * <strong>immediate</strong> attention, such as an incoming phone call or 1253 * alarm clock that the user has explicitly set to a particular time. 1254 * If this facility is used for something else, please give the user an option 1255 * to turn it off and use a normal notification, as this can be extremely 1256 * disruptive. 1257 * 1258 * @param intent The pending intent to launch. 1259 * @param highPriority Passing true will cause this notification to be sent 1260 * even if other notifications are suppressed. 1261 * 1262 * @see Notification#fullScreenIntent 1263 */ 1264 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) { 1265 mFullScreenIntent = intent; 1266 setFlag(FLAG_HIGH_PRIORITY, highPriority); 1267 return this; 1268 } 1269 1270 /** 1271 * Set the "ticker" text which is displayed in the status bar when the notification first 1272 * arrives. 1273 * 1274 * @see Notification#tickerText 1275 */ 1276 public Builder setTicker(CharSequence tickerText) { 1277 mTickerText = safeCharSequence(tickerText); 1278 return this; 1279 } 1280 1281 /** 1282 * Set the text that is displayed in the status bar when the notification first 1283 * arrives, and also a RemoteViews object that may be displayed instead on some 1284 * devices. 1285 * 1286 * @see Notification#tickerText 1287 * @see Notification#tickerView 1288 */ 1289 public Builder setTicker(CharSequence tickerText, RemoteViews views) { 1290 mTickerText = safeCharSequence(tickerText); 1291 mTickerView = views; 1292 return this; 1293 } 1294 1295 /** 1296 * Add a large icon to the notification (and the ticker on some devices). 1297 * 1298 * In the platform template, this image will be shown on the left of the notification view 1299 * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side). 1300 * 1301 * @see Notification#largeIcon 1302 */ 1303 public Builder setLargeIcon(Bitmap icon) { 1304 mLargeIcon = icon; 1305 return this; 1306 } 1307 1308 /** 1309 * Set the sound to play. 1310 * 1311 * It will be played on the {@link #STREAM_DEFAULT default stream} for notifications. 1312 * 1313 * @see Notification#sound 1314 */ 1315 public Builder setSound(Uri sound) { 1316 mSound = sound; 1317 mAudioStreamType = STREAM_DEFAULT; 1318 return this; 1319 } 1320 1321 /** 1322 * Set the sound to play, along with a specific stream on which to play it. 1323 * 1324 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants. 1325 * 1326 * @see Notification#sound 1327 */ 1328 public Builder setSound(Uri sound, int streamType) { 1329 mSound = sound; 1330 mAudioStreamType = streamType; 1331 return this; 1332 } 1333 1334 /** 1335 * Set the vibration pattern to use. 1336 * 1337 1338 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the 1339 * <code>pattern</code> parameter. 1340 * 1341 1342 * @see Notification#vibrate 1343 */ 1344 public Builder setVibrate(long[] pattern) { 1345 mVibrate = pattern; 1346 return this; 1347 } 1348 1349 /** 1350 * Set the desired color for the indicator LED on the device, as well as the 1351 * blink duty cycle (specified in milliseconds). 1352 * 1353 1354 * Not all devices will honor all (or even any) of these values. 1355 * 1356 1357 * @see Notification#ledARGB 1358 * @see Notification#ledOnMS 1359 * @see Notification#ledOffMS 1360 */ 1361 public Builder setLights(int argb, int onMs, int offMs) { 1362 mLedArgb = argb; 1363 mLedOnMs = onMs; 1364 mLedOffMs = offMs; 1365 return this; 1366 } 1367 1368 /** 1369 * Set whether this is an "ongoing" notification. 1370 * 1371 1372 * Ongoing notifications cannot be dismissed by the user, so your application or service 1373 * must take care of canceling them. 1374 * 1375 1376 * They are typically used to indicate a background task that the user is actively engaged 1377 * with (e.g., playing music) or is pending in some way and therefore occupying the device 1378 * (e.g., a file download, sync operation, active network connection). 1379 * 1380 1381 * @see Notification#FLAG_ONGOING_EVENT 1382 * @see Service#setForeground(boolean) 1383 */ 1384 public Builder setOngoing(boolean ongoing) { 1385 setFlag(FLAG_ONGOING_EVENT, ongoing); 1386 return this; 1387 } 1388 1389 /** 1390 * Set this flag if you would only like the sound, vibrate 1391 * and ticker to be played if the notification is not already showing. 1392 * 1393 * @see Notification#FLAG_ONLY_ALERT_ONCE 1394 */ 1395 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) { 1396 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce); 1397 return this; 1398 } 1399 1400 /** 1401 * Make this notification automatically dismissed when the user touches it. The 1402 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens. 1403 * 1404 * @see Notification#FLAG_AUTO_CANCEL 1405 */ 1406 public Builder setAutoCancel(boolean autoCancel) { 1407 setFlag(FLAG_AUTO_CANCEL, autoCancel); 1408 return this; 1409 } 1410 1411 /** 1412 * Set which notification properties will be inherited from system defaults. 1413 * <p> 1414 * The value should be one or more of the following fields combined with 1415 * bitwise-or: 1416 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. 1417 * <p> 1418 * For all default values, use {@link #DEFAULT_ALL}. 1419 */ 1420 public Builder setDefaults(int defaults) { 1421 mDefaults = defaults; 1422 return this; 1423 } 1424 1425 /** 1426 * Set the priority of this notification. 1427 * 1428 * @see Notification#priority 1429 */ 1430 public Builder setPriority(int pri) { 1431 mPriority = pri; 1432 return this; 1433 } 1434 1435 /** 1436 * @hide 1437 * 1438 * Add a kind (category) to this notification. Optional. 1439 * 1440 * @see Notification#kind 1441 */ 1442 public Builder addKind(String k) { 1443 mKindList.add(k); 1444 return this; 1445 } 1446 1447 /** 1448 * Add metadata to this notification. 1449 * 1450 * A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's 1451 * current contents are copied into the Notification each time {@link #build()} is 1452 * called. 1453 * 1454 * @see Notification#extras 1455 */ 1456 public Builder setExtras(Bundle bag) { 1457 mExtras = bag; 1458 return this; 1459 } 1460 1461 /** 1462 * Add an action to this notification. Actions are typically displayed by 1463 * the system as a button adjacent to the notification content. 1464 * <br> 1465 * A notification displays up to 3 actions, from left to right in the order they were added. 1466 * 1467 * @param icon Resource ID of a drawable that represents the action. 1468 * @param title Text describing the action. 1469 * @param intent PendingIntent to be fired when the action is invoked. 1470 */ 1471 public Builder addAction(int icon, CharSequence title, PendingIntent intent) { 1472 mActions.add(new Action(icon, safeCharSequence(title), intent)); 1473 return this; 1474 } 1475 1476 /** 1477 * Add a rich notification style to be applied at build time. 1478 * 1479 * @param style Object responsible for modifying the notification style. 1480 */ 1481 public Builder setStyle(Style style) { 1482 if (mStyle != style) { 1483 mStyle = style; 1484 if (mStyle != null) { 1485 mStyle.setBuilder(this); 1486 } 1487 } 1488 return this; 1489 } 1490 1491 private void setFlag(int mask, boolean value) { 1492 if (value) { 1493 mFlags |= mask; 1494 } else { 1495 mFlags &= ~mask; 1496 } 1497 } 1498 1499 private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) { 1500 RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId); 1501 boolean showLine3 = false; 1502 boolean showLine2 = false; 1503 int smallIconImageViewId = R.id.icon; 1504 if (mLargeIcon != null) { 1505 contentView.setImageViewBitmap(R.id.icon, mLargeIcon); 1506 smallIconImageViewId = R.id.right_icon; 1507 } 1508 if (mPriority < PRIORITY_LOW) { 1509 contentView.setInt(R.id.icon, 1510 "setBackgroundResource", R.drawable.notification_template_icon_low_bg); 1511 contentView.setInt(R.id.status_bar_latest_event_content, 1512 "setBackgroundResource", R.drawable.notification_bg_low); 1513 } 1514 if (mSmallIcon != 0) { 1515 contentView.setImageViewResource(smallIconImageViewId, mSmallIcon); 1516 contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE); 1517 } else { 1518 contentView.setViewVisibility(smallIconImageViewId, View.GONE); 1519 } 1520 if (mContentTitle != null) { 1521 contentView.setTextViewText(R.id.title, mContentTitle); 1522 } 1523 if (mContentText != null) { 1524 contentView.setTextViewText(R.id.text, mContentText); 1525 showLine3 = true; 1526 } 1527 if (mContentInfo != null) { 1528 contentView.setTextViewText(R.id.info, mContentInfo); 1529 contentView.setViewVisibility(R.id.info, View.VISIBLE); 1530 showLine3 = true; 1531 } else if (mNumber > 0) { 1532 final int tooBig = mContext.getResources().getInteger( 1533 R.integer.status_bar_notification_info_maxnum); 1534 if (mNumber > tooBig) { 1535 contentView.setTextViewText(R.id.info, mContext.getResources().getString( 1536 R.string.status_bar_notification_info_overflow)); 1537 } else { 1538 NumberFormat f = NumberFormat.getIntegerInstance(); 1539 contentView.setTextViewText(R.id.info, f.format(mNumber)); 1540 } 1541 contentView.setViewVisibility(R.id.info, View.VISIBLE); 1542 showLine3 = true; 1543 } else { 1544 contentView.setViewVisibility(R.id.info, View.GONE); 1545 } 1546 1547 // Need to show three lines? 1548 if (mSubText != null) { 1549 contentView.setTextViewText(R.id.text, mSubText); 1550 if (mContentText != null) { 1551 contentView.setTextViewText(R.id.text2, mContentText); 1552 contentView.setViewVisibility(R.id.text2, View.VISIBLE); 1553 showLine2 = true; 1554 } else { 1555 contentView.setViewVisibility(R.id.text2, View.GONE); 1556 } 1557 } else { 1558 contentView.setViewVisibility(R.id.text2, View.GONE); 1559 if (mProgressMax != 0 || mProgressIndeterminate) { 1560 contentView.setProgressBar( 1561 R.id.progress, mProgressMax, mProgress, mProgressIndeterminate); 1562 contentView.setViewVisibility(R.id.progress, View.VISIBLE); 1563 showLine2 = true; 1564 } else { 1565 contentView.setViewVisibility(R.id.progress, View.GONE); 1566 } 1567 } 1568 if (showLine2) { 1569 if (fitIn1U) { 1570 // need to shrink all the type to make sure everything fits 1571 final Resources res = mContext.getResources(); 1572 final float subTextSize = res.getDimensionPixelSize( 1573 R.dimen.notification_subtext_size); 1574 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize); 1575 } 1576 // vertical centering 1577 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 1578 } 1579 1580 if (mWhen != 0 && mShowWhen) { 1581 if (mUseChronometer) { 1582 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE); 1583 contentView.setLong(R.id.chronometer, "setBase", 1584 mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis())); 1585 contentView.setBoolean(R.id.chronometer, "setStarted", true); 1586 } else { 1587 contentView.setViewVisibility(R.id.time, View.VISIBLE); 1588 contentView.setLong(R.id.time, "setTime", mWhen); 1589 } 1590 } else { 1591 contentView.setViewVisibility(R.id.time, View.GONE); 1592 } 1593 1594 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE); 1595 contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE); 1596 return contentView; 1597 } 1598 1599 private RemoteViews applyStandardTemplateWithActions(int layoutId) { 1600 RemoteViews big = applyStandardTemplate(layoutId, false); 1601 1602 int N = mActions.size(); 1603 if (N > 0) { 1604 // Log.d("Notification", "has actions: " + mContentText); 1605 big.setViewVisibility(R.id.actions, View.VISIBLE); 1606 big.setViewVisibility(R.id.action_divider, View.VISIBLE); 1607 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS; 1608 big.removeAllViews(R.id.actions); 1609 for (int i=0; i<N; i++) { 1610 final RemoteViews button = generateActionButton(mActions.get(i)); 1611 //Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title); 1612 big.addView(R.id.actions, button); 1613 } 1614 } 1615 return big; 1616 } 1617 1618 private RemoteViews makeContentView() { 1619 if (mContentView != null) { 1620 return mContentView; 1621 } else { 1622 return applyStandardTemplate(R.layout.notification_template_base, true); // no more special large_icon flavor 1623 } 1624 } 1625 1626 private RemoteViews makeTickerView() { 1627 if (mTickerView != null) { 1628 return mTickerView; 1629 } else { 1630 if (mContentView == null) { 1631 return applyStandardTemplate(mLargeIcon == null 1632 ? R.layout.status_bar_latest_event_ticker 1633 : R.layout.status_bar_latest_event_ticker_large_icon, true); 1634 } else { 1635 return null; 1636 } 1637 } 1638 } 1639 1640 private RemoteViews makeBigContentView() { 1641 if (mActions.size() == 0) return null; 1642 1643 return applyStandardTemplateWithActions(R.layout.notification_template_big_base); 1644 } 1645 1646 private RemoteViews generateActionButton(Action action) { 1647 final boolean tombstone = (action.actionIntent == null); 1648 RemoteViews button = new RemoteViews(mContext.getPackageName(), 1649 tombstone ? R.layout.notification_action_tombstone 1650 : R.layout.notification_action); 1651 button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0); 1652 button.setTextViewText(R.id.action0, action.title); 1653 if (!tombstone) { 1654 button.setOnClickPendingIntent(R.id.action0, action.actionIntent); 1655 } 1656 button.setContentDescription(R.id.action0, action.title); 1657 return button; 1658 } 1659 1660 /** 1661 * Apply the unstyled operations and return a new {@link Notification} object. 1662 */ 1663 private Notification buildUnstyled() { 1664 Notification n = new Notification(); 1665 n.when = mWhen; 1666 n.icon = mSmallIcon; 1667 n.iconLevel = mSmallIconLevel; 1668 n.number = mNumber; 1669 n.contentView = makeContentView(); 1670 n.contentIntent = mContentIntent; 1671 n.deleteIntent = mDeleteIntent; 1672 n.fullScreenIntent = mFullScreenIntent; 1673 n.tickerText = mTickerText; 1674 n.tickerView = makeTickerView(); 1675 n.largeIcon = mLargeIcon; 1676 n.sound = mSound; 1677 n.audioStreamType = mAudioStreamType; 1678 n.vibrate = mVibrate; 1679 n.ledARGB = mLedArgb; 1680 n.ledOnMS = mLedOnMs; 1681 n.ledOffMS = mLedOffMs; 1682 n.defaults = mDefaults; 1683 n.flags = mFlags; 1684 n.bigContentView = makeBigContentView(); 1685 if (mLedOnMs != 0 || mLedOffMs != 0) { 1686 n.flags |= FLAG_SHOW_LIGHTS; 1687 } 1688 if ((mDefaults & DEFAULT_LIGHTS) != 0) { 1689 n.flags |= FLAG_SHOW_LIGHTS; 1690 } 1691 if (mKindList.size() > 0) { 1692 n.kind = new String[mKindList.size()]; 1693 mKindList.toArray(n.kind); 1694 } else { 1695 n.kind = null; 1696 } 1697 n.priority = mPriority; 1698 if (mActions.size() > 0) { 1699 n.actions = new Action[mActions.size()]; 1700 mActions.toArray(n.actions); 1701 } 1702 1703 return n; 1704 } 1705 1706 /** 1707 * Capture, in the provided bundle, semantic information used in the construction of 1708 * this Notification object. 1709 * @hide 1710 */ 1711 public void addExtras(Bundle extras) { 1712 // Store original information used in the construction of this object 1713 extras.putCharSequence(EXTRA_TITLE, mContentTitle); 1714 extras.putCharSequence(EXTRA_TEXT, mContentText); 1715 extras.putCharSequence(EXTRA_SUB_TEXT, mSubText); 1716 extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo); 1717 extras.putInt(EXTRA_SMALL_ICON, mSmallIcon); 1718 extras.putInt(EXTRA_PROGRESS, mProgress); 1719 extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax); 1720 extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate); 1721 extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer); 1722 extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen); 1723 if (mLargeIcon != null) { 1724 extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); 1725 } 1726 } 1727 1728 /** 1729 * @deprecated Use {@link #build()} instead. 1730 */ 1731 @Deprecated 1732 public Notification getNotification() { 1733 return build(); 1734 } 1735 1736 /** 1737 * Combine all of the options that have been set and return a new {@link Notification} 1738 * object. 1739 */ 1740 public Notification build() { 1741 final Notification n; 1742 1743 if (mStyle != null) { 1744 n = mStyle.build(); 1745 } else { 1746 n = buildUnstyled(); 1747 } 1748 1749 n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle(); 1750 1751 addExtras(n.extras); 1752 if (mStyle != null) { 1753 mStyle.addExtras(n.extras); 1754 } 1755 1756 return n; 1757 } 1758 1759 /** 1760 * Apply this Builder to an existing {@link Notification} object. 1761 * 1762 * @hide 1763 */ 1764 public Notification buildInto(Notification n) { 1765 build().cloneInto(n, true); 1766 return n; 1767 } 1768 } 1769 1770 /** 1771 * An object that can apply a rich notification style to a {@link Notification.Builder} 1772 * object. 1773 */ 1774 public static abstract class Style 1775 { 1776 private CharSequence mBigContentTitle; 1777 private CharSequence mSummaryText = null; 1778 private boolean mSummaryTextSet = false; 1779 1780 protected Builder mBuilder; 1781 1782 /** 1783 * Overrides ContentTitle in the big form of the template. 1784 * This defaults to the value passed to setContentTitle(). 1785 */ 1786 protected void internalSetBigContentTitle(CharSequence title) { 1787 mBigContentTitle = title; 1788 } 1789 1790 /** 1791 * Set the first line of text after the detail section in the big form of the template. 1792 */ 1793 protected void internalSetSummaryText(CharSequence cs) { 1794 mSummaryText = cs; 1795 mSummaryTextSet = true; 1796 } 1797 1798 public void setBuilder(Builder builder) { 1799 if (mBuilder != builder) { 1800 mBuilder = builder; 1801 if (mBuilder != null) { 1802 mBuilder.setStyle(this); 1803 } 1804 } 1805 } 1806 1807 protected void checkBuilder() { 1808 if (mBuilder == null) { 1809 throw new IllegalArgumentException("Style requires a valid Builder object"); 1810 } 1811 } 1812 1813 protected RemoteViews getStandardView(int layoutId) { 1814 checkBuilder(); 1815 1816 if (mBigContentTitle != null) { 1817 mBuilder.setContentTitle(mBigContentTitle); 1818 } 1819 1820 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId); 1821 1822 if (mBigContentTitle != null && mBigContentTitle.equals("")) { 1823 contentView.setViewVisibility(R.id.line1, View.GONE); 1824 } else { 1825 contentView.setViewVisibility(R.id.line1, View.VISIBLE); 1826 } 1827 1828 // The last line defaults to the subtext, but can be replaced by mSummaryText 1829 final CharSequence overflowText = 1830 mSummaryTextSet ? mSummaryText 1831 : mBuilder.mSubText; 1832 if (overflowText != null) { 1833 contentView.setTextViewText(R.id.text, overflowText); 1834 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE); 1835 contentView.setViewVisibility(R.id.line3, View.VISIBLE); 1836 } else { 1837 contentView.setViewVisibility(R.id.overflow_divider, View.GONE); 1838 contentView.setViewVisibility(R.id.line3, View.GONE); 1839 } 1840 1841 return contentView; 1842 } 1843 1844 /** 1845 * @hide 1846 */ 1847 public void addExtras(Bundle extras) { 1848 if (mSummaryTextSet) { 1849 extras.putCharSequence(EXTRA_SUMMARY_TEXT, mSummaryText); 1850 } 1851 if (mBigContentTitle != null) { 1852 extras.putCharSequence(EXTRA_TITLE_BIG, mBigContentTitle); 1853 } 1854 } 1855 1856 public abstract Notification build(); 1857 } 1858 1859 /** 1860 * Helper class for generating large-format notifications that include a large image attachment. 1861 * 1862 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1863 * <pre class="prettyprint"> 1864 * Notification noti = new Notification.BigPictureStyle( 1865 * new Notification.Builder() 1866 * .setContentTitle("New photo from " + sender.toString()) 1867 * .setContentText(subject) 1868 * .setSmallIcon(R.drawable.new_post) 1869 * .setLargeIcon(aBitmap)) 1870 * .bigPicture(aBigBitmap) 1871 * .build(); 1872 * </pre> 1873 * 1874 * @see Notification#bigContentView 1875 */ 1876 public static class BigPictureStyle extends Style { 1877 private Bitmap mPicture; 1878 private Bitmap mBigLargeIcon; 1879 private boolean mBigLargeIconSet = false; 1880 1881 public BigPictureStyle() { 1882 } 1883 1884 public BigPictureStyle(Builder builder) { 1885 setBuilder(builder); 1886 } 1887 1888 /** 1889 * Overrides ContentTitle in the big form of the template. 1890 * This defaults to the value passed to setContentTitle(). 1891 */ 1892 public BigPictureStyle setBigContentTitle(CharSequence title) { 1893 internalSetBigContentTitle(safeCharSequence(title)); 1894 return this; 1895 } 1896 1897 /** 1898 * Set the first line of text after the detail section in the big form of the template. 1899 */ 1900 public BigPictureStyle setSummaryText(CharSequence cs) { 1901 internalSetSummaryText(safeCharSequence(cs)); 1902 return this; 1903 } 1904 1905 /** 1906 * Provide the bitmap to be used as the payload for the BigPicture notification. 1907 */ 1908 public BigPictureStyle bigPicture(Bitmap b) { 1909 mPicture = b; 1910 return this; 1911 } 1912 1913 /** 1914 * Override the large icon when the big notification is shown. 1915 */ 1916 public BigPictureStyle bigLargeIcon(Bitmap b) { 1917 mBigLargeIconSet = true; 1918 mBigLargeIcon = b; 1919 return this; 1920 } 1921 1922 private RemoteViews makeBigContentView() { 1923 RemoteViews contentView = getStandardView(R.layout.notification_template_big_picture); 1924 1925 contentView.setImageViewBitmap(R.id.big_picture, mPicture); 1926 1927 return contentView; 1928 } 1929 1930 /** 1931 * @hide 1932 */ 1933 public void addExtras(Bundle extras) { 1934 super.addExtras(extras); 1935 1936 if (mBigLargeIconSet) { 1937 extras.putParcelable(EXTRA_LARGE_ICON_BIG, mBigLargeIcon); 1938 } 1939 extras.putParcelable(EXTRA_PICTURE, mPicture); 1940 } 1941 1942 @Override 1943 public Notification build() { 1944 checkBuilder(); 1945 Notification wip = mBuilder.buildUnstyled(); 1946 if (mBigLargeIconSet ) { 1947 mBuilder.mLargeIcon = mBigLargeIcon; 1948 } 1949 wip.bigContentView = makeBigContentView(); 1950 return wip; 1951 } 1952 } 1953 1954 /** 1955 * Helper class for generating large-format notifications that include a lot of text. 1956 * 1957 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1958 * <pre class="prettyprint"> 1959 * Notification noti = new Notification.BigTextStyle( 1960 * new Notification.Builder() 1961 * .setContentTitle("New mail from " + sender.toString()) 1962 * .setContentText(subject) 1963 * .setSmallIcon(R.drawable.new_mail) 1964 * .setLargeIcon(aBitmap)) 1965 * .bigText(aVeryLongString) 1966 * .build(); 1967 * </pre> 1968 * 1969 * @see Notification#bigContentView 1970 */ 1971 public static class BigTextStyle extends Style { 1972 private CharSequence mBigText; 1973 1974 public BigTextStyle() { 1975 } 1976 1977 public BigTextStyle(Builder builder) { 1978 setBuilder(builder); 1979 } 1980 1981 /** 1982 * Overrides ContentTitle in the big form of the template. 1983 * This defaults to the value passed to setContentTitle(). 1984 */ 1985 public BigTextStyle setBigContentTitle(CharSequence title) { 1986 internalSetBigContentTitle(safeCharSequence(title)); 1987 return this; 1988 } 1989 1990 /** 1991 * Set the first line of text after the detail section in the big form of the template. 1992 */ 1993 public BigTextStyle setSummaryText(CharSequence cs) { 1994 internalSetSummaryText(safeCharSequence(cs)); 1995 return this; 1996 } 1997 1998 /** 1999 * Provide the longer text to be displayed in the big form of the 2000 * template in place of the content text. 2001 */ 2002 public BigTextStyle bigText(CharSequence cs) { 2003 mBigText = safeCharSequence(cs); 2004 return this; 2005 } 2006 2007 /** 2008 * @hide 2009 */ 2010 public void addExtras(Bundle extras) { 2011 super.addExtras(extras); 2012 2013 extras.putCharSequence(EXTRA_TEXT, mBigText); 2014 } 2015 2016 private RemoteViews makeBigContentView() { 2017 // Remove the content text so line3 only shows if you have a summary 2018 final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null); 2019 mBuilder.mContentText = null; 2020 2021 RemoteViews contentView = getStandardView(R.layout.notification_template_big_text); 2022 2023 if (hadThreeLines) { 2024 // vertical centering 2025 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 2026 } 2027 2028 contentView.setTextViewText(R.id.big_text, mBigText); 2029 contentView.setViewVisibility(R.id.big_text, View.VISIBLE); 2030 contentView.setViewVisibility(R.id.text2, View.GONE); 2031 2032 return contentView; 2033 } 2034 2035 @Override 2036 public Notification build() { 2037 checkBuilder(); 2038 Notification wip = mBuilder.buildUnstyled(); 2039 wip.bigContentView = makeBigContentView(); 2040 2041 wip.extras.putCharSequence(EXTRA_TEXT, mBigText); 2042 2043 return wip; 2044 } 2045 } 2046 2047 /** 2048 * Helper class for generating large-format notifications that include a list of (up to 5) strings. 2049 * 2050 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 2051 * <pre class="prettyprint"> 2052 * Notification noti = new Notification.InboxStyle( 2053 * new Notification.Builder() 2054 * .setContentTitle("5 New mails from " + sender.toString()) 2055 * .setContentText(subject) 2056 * .setSmallIcon(R.drawable.new_mail) 2057 * .setLargeIcon(aBitmap)) 2058 * .addLine(str1) 2059 * .addLine(str2) 2060 * .setContentTitle("") 2061 * .setSummaryText("+3 more") 2062 * .build(); 2063 * </pre> 2064 * 2065 * @see Notification#bigContentView 2066 */ 2067 public static class InboxStyle extends Style { 2068 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5); 2069 2070 public InboxStyle() { 2071 } 2072 2073 public InboxStyle(Builder builder) { 2074 setBuilder(builder); 2075 } 2076 2077 /** 2078 * Overrides ContentTitle in the big form of the template. 2079 * This defaults to the value passed to setContentTitle(). 2080 */ 2081 public InboxStyle setBigContentTitle(CharSequence title) { 2082 internalSetBigContentTitle(safeCharSequence(title)); 2083 return this; 2084 } 2085 2086 /** 2087 * Set the first line of text after the detail section in the big form of the template. 2088 */ 2089 public InboxStyle setSummaryText(CharSequence cs) { 2090 internalSetSummaryText(safeCharSequence(cs)); 2091 return this; 2092 } 2093 2094 /** 2095 * Append a line to the digest section of the Inbox notification. 2096 */ 2097 public InboxStyle addLine(CharSequence cs) { 2098 mTexts.add(safeCharSequence(cs)); 2099 return this; 2100 } 2101 2102 /** 2103 * @hide 2104 */ 2105 public void addExtras(Bundle extras) { 2106 super.addExtras(extras); 2107 CharSequence[] a = new CharSequence[mTexts.size()]; 2108 extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a)); 2109 } 2110 2111 private RemoteViews makeBigContentView() { 2112 // Remove the content text so line3 disappears unless you have a summary 2113 mBuilder.mContentText = null; 2114 RemoteViews contentView = getStandardView(R.layout.notification_template_inbox); 2115 2116 contentView.setViewVisibility(R.id.text2, View.GONE); 2117 2118 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3, 2119 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6}; 2120 2121 // Make sure all rows are gone in case we reuse a view. 2122 for (int rowId : rowIds) { 2123 contentView.setViewVisibility(rowId, View.GONE); 2124 } 2125 2126 2127 int i=0; 2128 while (i < mTexts.size() && i < rowIds.length) { 2129 CharSequence str = mTexts.get(i); 2130 if (str != null && !str.equals("")) { 2131 contentView.setViewVisibility(rowIds[i], View.VISIBLE); 2132 contentView.setTextViewText(rowIds[i], str); 2133 } 2134 i++; 2135 } 2136 2137 contentView.setViewVisibility(R.id.inbox_end_pad, 2138 mTexts.size() > 0 ? View.VISIBLE : View.GONE); 2139 2140 contentView.setViewVisibility(R.id.inbox_more, 2141 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE); 2142 2143 return contentView; 2144 } 2145 2146 @Override 2147 public Notification build() { 2148 checkBuilder(); 2149 Notification wip = mBuilder.buildUnstyled(); 2150 wip.bigContentView = makeBigContentView(); 2151 2152 return wip; 2153 } 2154 } 2155} 2156