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