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