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. On tablets, the 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 notification priority as described in 392 * MUMBLE MUMBLE. 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.vibrate != null) { 850 int N = this.vibrate.length-1; 851 sb.append("["); 852 for (int i=0; i<N; i++) { 853 sb.append(this.vibrate[i]); 854 sb.append(','); 855 } 856 if (N != -1) { 857 sb.append(this.vibrate[N]); 858 } 859 sb.append("]"); 860 } else if ((this.defaults & DEFAULT_VIBRATE) != 0) { 861 sb.append("default"); 862 } else { 863 sb.append("null"); 864 } 865 sb.append(" sound="); 866 if (this.sound != null) { 867 sb.append(this.sound.toString()); 868 } else if ((this.defaults & DEFAULT_SOUND) != 0) { 869 sb.append("default"); 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 * 1385 * @param icon Resource ID of a drawable that represents the action. 1386 * @param title Text describing the action. 1387 * @param intent PendingIntent to be fired when the action is invoked. 1388 */ 1389 public Builder addAction(int icon, CharSequence title, PendingIntent intent) { 1390 mActions.add(new Action(icon, title, intent)); 1391 return this; 1392 } 1393 1394 /** 1395 * Add a rich notification style to be applied at build time. 1396 * 1397 * @param style Object responsible for modifying the notification style. 1398 */ 1399 public Builder setStyle(Style style) { 1400 if (mStyle != style) { 1401 mStyle = style; 1402 if (mStyle != null) { 1403 mStyle.setBuilder(this); 1404 } 1405 } 1406 return this; 1407 } 1408 1409 private void setFlag(int mask, boolean value) { 1410 if (value) { 1411 mFlags |= mask; 1412 } else { 1413 mFlags &= ~mask; 1414 } 1415 } 1416 1417 private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) { 1418 RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId); 1419 boolean showLine3 = false; 1420 boolean showLine2 = false; 1421 int smallIconImageViewId = R.id.icon; 1422 if (mLargeIcon != null) { 1423 contentView.setImageViewBitmap(R.id.icon, mLargeIcon); 1424 smallIconImageViewId = R.id.right_icon; 1425 } 1426 if (mPriority < PRIORITY_LOW) { 1427 contentView.setInt(R.id.icon, 1428 "setBackgroundResource", R.drawable.notification_template_icon_low_bg); 1429 contentView.setInt(R.id.status_bar_latest_event_content, 1430 "setBackgroundResource", R.drawable.notification_bg_low); 1431 } 1432 if (mSmallIcon != 0) { 1433 contentView.setImageViewResource(smallIconImageViewId, mSmallIcon); 1434 contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE); 1435 } else { 1436 contentView.setViewVisibility(smallIconImageViewId, View.GONE); 1437 } 1438 if (mContentTitle != null) { 1439 contentView.setTextViewText(R.id.title, mContentTitle); 1440 } 1441 if (mContentText != null) { 1442 contentView.setTextViewText(R.id.text, mContentText); 1443 showLine3 = true; 1444 } 1445 if (mContentInfo != null) { 1446 contentView.setTextViewText(R.id.info, mContentInfo); 1447 contentView.setViewVisibility(R.id.info, View.VISIBLE); 1448 showLine3 = true; 1449 } else if (mNumber > 0) { 1450 final int tooBig = mContext.getResources().getInteger( 1451 R.integer.status_bar_notification_info_maxnum); 1452 if (mNumber > tooBig) { 1453 contentView.setTextViewText(R.id.info, mContext.getResources().getString( 1454 R.string.status_bar_notification_info_overflow)); 1455 } else { 1456 NumberFormat f = NumberFormat.getIntegerInstance(); 1457 contentView.setTextViewText(R.id.info, f.format(mNumber)); 1458 } 1459 contentView.setViewVisibility(R.id.info, View.VISIBLE); 1460 showLine3 = true; 1461 } else { 1462 contentView.setViewVisibility(R.id.info, View.GONE); 1463 } 1464 1465 // Need to show three lines? 1466 if (mSubText != null) { 1467 contentView.setTextViewText(R.id.text, mSubText); 1468 if (mContentText != null) { 1469 contentView.setTextViewText(R.id.text2, mContentText); 1470 contentView.setViewVisibility(R.id.text2, View.VISIBLE); 1471 showLine2 = true; 1472 } else { 1473 contentView.setViewVisibility(R.id.text2, View.GONE); 1474 } 1475 } else { 1476 contentView.setViewVisibility(R.id.text2, View.GONE); 1477 if (mProgressMax != 0 || mProgressIndeterminate) { 1478 contentView.setProgressBar( 1479 R.id.progress, mProgressMax, mProgress, mProgressIndeterminate); 1480 contentView.setViewVisibility(R.id.progress, View.VISIBLE); 1481 showLine2 = true; 1482 } else { 1483 contentView.setViewVisibility(R.id.progress, View.GONE); 1484 } 1485 } 1486 if (showLine2) { 1487 if (fitIn1U) { 1488 // need to shrink all the type to make sure everything fits 1489 final Resources res = mContext.getResources(); 1490 final float subTextSize = res.getDimensionPixelSize( 1491 R.dimen.notification_subtext_size); 1492 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize); 1493 } 1494 // vertical centering 1495 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 1496 } 1497 1498 if (mWhen != 0 && mShowWhen) { 1499 if (mUseChronometer) { 1500 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE); 1501 contentView.setLong(R.id.chronometer, "setBase", 1502 mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis())); 1503 contentView.setBoolean(R.id.chronometer, "setStarted", true); 1504 } else { 1505 contentView.setViewVisibility(R.id.time, View.VISIBLE); 1506 contentView.setLong(R.id.time, "setTime", mWhen); 1507 } 1508 } else { 1509 contentView.setViewVisibility(R.id.time, View.GONE); 1510 } 1511 1512 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE); 1513 contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE); 1514 return contentView; 1515 } 1516 1517 private RemoteViews applyStandardTemplateWithActions(int layoutId) { 1518 RemoteViews big = applyStandardTemplate(layoutId, false); 1519 1520 int N = mActions.size(); 1521 if (N > 0) { 1522 // Log.d("Notification", "has actions: " + mContentText); 1523 big.setViewVisibility(R.id.actions, View.VISIBLE); 1524 big.setViewVisibility(R.id.action_divider, View.VISIBLE); 1525 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS; 1526 big.removeAllViews(R.id.actions); 1527 for (int i=0; i<N; i++) { 1528 final RemoteViews button = generateActionButton(mActions.get(i)); 1529 //Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title); 1530 big.addView(R.id.actions, button); 1531 } 1532 } 1533 return big; 1534 } 1535 1536 private RemoteViews makeContentView() { 1537 if (mContentView != null) { 1538 return mContentView; 1539 } else { 1540 return applyStandardTemplate(R.layout.notification_template_base, true); // no more special large_icon flavor 1541 } 1542 } 1543 1544 private RemoteViews makeTickerView() { 1545 if (mTickerView != null) { 1546 return mTickerView; 1547 } else { 1548 if (mContentView == null) { 1549 return applyStandardTemplate(mLargeIcon == null 1550 ? R.layout.status_bar_latest_event_ticker 1551 : R.layout.status_bar_latest_event_ticker_large_icon, true); 1552 } else { 1553 return null; 1554 } 1555 } 1556 } 1557 1558 private RemoteViews makeBigContentView() { 1559 if (mActions.size() == 0) return null; 1560 1561 return applyStandardTemplateWithActions(R.layout.notification_template_big_base); 1562 } 1563 1564 private RemoteViews generateActionButton(Action action) { 1565 final boolean tombstone = (action.actionIntent == null); 1566 RemoteViews button = new RemoteViews(mContext.getPackageName(), 1567 tombstone ? R.layout.notification_action_tombstone 1568 : R.layout.notification_action); 1569 button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0); 1570 button.setTextViewText(R.id.action0, action.title); 1571 if (!tombstone) { 1572 button.setOnClickPendingIntent(R.id.action0, action.actionIntent); 1573 } 1574 button.setContentDescription(R.id.action0, action.title); 1575 return button; 1576 } 1577 1578 /** 1579 * Apply the unstyled operations and return a new {@link Notification} object. 1580 */ 1581 private Notification buildUnstyled() { 1582 Notification n = new Notification(); 1583 n.when = mWhen; 1584 n.icon = mSmallIcon; 1585 n.iconLevel = mSmallIconLevel; 1586 n.number = mNumber; 1587 n.contentView = makeContentView(); 1588 n.contentIntent = mContentIntent; 1589 n.deleteIntent = mDeleteIntent; 1590 n.fullScreenIntent = mFullScreenIntent; 1591 n.tickerText = mTickerText; 1592 n.tickerView = makeTickerView(); 1593 n.largeIcon = mLargeIcon; 1594 n.sound = mSound; 1595 n.audioStreamType = mAudioStreamType; 1596 n.vibrate = mVibrate; 1597 n.ledARGB = mLedArgb; 1598 n.ledOnMS = mLedOnMs; 1599 n.ledOffMS = mLedOffMs; 1600 n.defaults = mDefaults; 1601 n.flags = mFlags; 1602 n.bigContentView = makeBigContentView(); 1603 if (mLedOnMs != 0 && mLedOffMs != 0) { 1604 n.flags |= FLAG_SHOW_LIGHTS; 1605 } 1606 if ((mDefaults & DEFAULT_LIGHTS) != 0) { 1607 n.flags |= FLAG_SHOW_LIGHTS; 1608 } 1609 if (mKindList.size() > 0) { 1610 n.kind = new String[mKindList.size()]; 1611 mKindList.toArray(n.kind); 1612 } else { 1613 n.kind = null; 1614 } 1615 n.priority = mPriority; 1616 n.extras = mExtras != null ? new Bundle(mExtras) : null; 1617 if (mActions.size() > 0) { 1618 n.actions = new Action[mActions.size()]; 1619 mActions.toArray(n.actions); 1620 } 1621 return n; 1622 } 1623 1624 /** 1625 * @deprecated Use {@link #build()} instead. 1626 */ 1627 @Deprecated 1628 public Notification getNotification() { 1629 return build(); 1630 } 1631 1632 /** 1633 * Combine all of the options that have been set and return a new {@link Notification} 1634 * object. 1635 */ 1636 public Notification build() { 1637 if (mStyle != null) { 1638 return mStyle.build(); 1639 } else { 1640 return buildUnstyled(); 1641 } 1642 } 1643 } 1644 1645 1646 /** 1647 * An object that can apply a rich notification style to a {@link Notification.Builder} 1648 * object. 1649 */ 1650 public static abstract class Style 1651 { 1652 private CharSequence mBigContentTitle; 1653 private CharSequence mSummaryText = null; 1654 private boolean mSummaryTextSet = false; 1655 1656 protected Builder mBuilder; 1657 1658 /** 1659 * Overrides ContentTitle in the big form of the template. 1660 * This defaults to the value passed to setContentTitle(). 1661 */ 1662 protected void internalSetBigContentTitle(CharSequence title) { 1663 mBigContentTitle = title; 1664 } 1665 1666 /** 1667 * Set the first line of text after the detail section in the big form of the template. 1668 */ 1669 protected void internalSetSummaryText(CharSequence cs) { 1670 mSummaryText = cs; 1671 mSummaryTextSet = true; 1672 } 1673 1674 public void setBuilder(Builder builder) { 1675 if (mBuilder != builder) { 1676 mBuilder = builder; 1677 if (mBuilder != null) { 1678 mBuilder.setStyle(this); 1679 } 1680 } 1681 } 1682 1683 protected void checkBuilder() { 1684 if (mBuilder == null) { 1685 throw new IllegalArgumentException("Style requires a valid Builder object"); 1686 } 1687 } 1688 1689 protected RemoteViews getStandardView(int layoutId) { 1690 checkBuilder(); 1691 1692 if (mBigContentTitle != null) { 1693 mBuilder.setContentTitle(mBigContentTitle); 1694 } 1695 1696 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId); 1697 1698 if (mBigContentTitle != null && mBigContentTitle.equals("")) { 1699 contentView.setViewVisibility(R.id.line1, View.GONE); 1700 } else { 1701 contentView.setViewVisibility(R.id.line1, View.VISIBLE); 1702 } 1703 1704 // The last line defaults to the subtext, but can be replaced by mSummaryText 1705 final CharSequence overflowText = 1706 mSummaryTextSet ? mSummaryText 1707 : mBuilder.mSubText; 1708 if (overflowText != null) { 1709 contentView.setTextViewText(R.id.text, overflowText); 1710 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE); 1711 contentView.setViewVisibility(R.id.line3, View.VISIBLE); 1712 } else { 1713 contentView.setViewVisibility(R.id.overflow_divider, View.GONE); 1714 contentView.setViewVisibility(R.id.line3, View.GONE); 1715 } 1716 1717 return contentView; 1718 } 1719 1720 public abstract Notification build(); 1721 } 1722 1723 /** 1724 * Helper class for generating large-format notifications that include a large image attachment. 1725 * 1726 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1727 * <pre class="prettyprint"> 1728 * Notification noti = new Notification.BigPictureStyle( 1729 * new Notification.Builder() 1730 * .setContentTitle("New photo from " + sender.toString()) 1731 * .setContentText(subject) 1732 * .setSmallIcon(R.drawable.new_post) 1733 * .setLargeIcon(aBitmap)) 1734 * .bigPicture(aBigBitmap) 1735 * .build(); 1736 * </pre> 1737 * 1738 * @see Notification#bigContentView 1739 */ 1740 public static class BigPictureStyle extends Style { 1741 private Bitmap mPicture; 1742 private Bitmap mBigLargeIcon; 1743 private boolean mBigLargeIconSet = false; 1744 1745 public BigPictureStyle() { 1746 } 1747 1748 public BigPictureStyle(Builder builder) { 1749 setBuilder(builder); 1750 } 1751 1752 /** 1753 * Overrides ContentTitle in the big form of the template. 1754 * This defaults to the value passed to setContentTitle(). 1755 */ 1756 public BigPictureStyle setBigContentTitle(CharSequence title) { 1757 internalSetBigContentTitle(title); 1758 return this; 1759 } 1760 1761 /** 1762 * Set the first line of text after the detail section in the big form of the template. 1763 */ 1764 public BigPictureStyle setSummaryText(CharSequence cs) { 1765 internalSetSummaryText(cs); 1766 return this; 1767 } 1768 1769 /** 1770 * Provide the bitmap to be used as the payload for the BigPicture notification. 1771 */ 1772 public BigPictureStyle bigPicture(Bitmap b) { 1773 mPicture = b; 1774 return this; 1775 } 1776 1777 /** 1778 * Override the large icon when the big notification is shown. 1779 */ 1780 public BigPictureStyle bigLargeIcon(Bitmap b) { 1781 mBigLargeIconSet = true; 1782 mBigLargeIcon = b; 1783 return this; 1784 } 1785 1786 private RemoteViews makeBigContentView() { 1787 RemoteViews contentView = getStandardView(R.layout.notification_template_big_picture); 1788 1789 contentView.setImageViewBitmap(R.id.big_picture, mPicture); 1790 1791 return contentView; 1792 } 1793 1794 @Override 1795 public Notification build() { 1796 checkBuilder(); 1797 Notification wip = mBuilder.buildUnstyled(); 1798 if (mBigLargeIconSet ) { 1799 mBuilder.mLargeIcon = mBigLargeIcon; 1800 } 1801 wip.bigContentView = makeBigContentView(); 1802 return wip; 1803 } 1804 } 1805 1806 /** 1807 * Helper class for generating large-format notifications that include a lot of text. 1808 * 1809 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1810 * <pre class="prettyprint"> 1811 * Notification noti = new Notification.BigTextStyle( 1812 * new Notification.Builder() 1813 * .setContentTitle("New mail from " + sender.toString()) 1814 * .setContentText(subject) 1815 * .setSmallIcon(R.drawable.new_mail) 1816 * .setLargeIcon(aBitmap)) 1817 * .bigText(aVeryLongString) 1818 * .build(); 1819 * </pre> 1820 * 1821 * @see Notification#bigContentView 1822 */ 1823 public static class BigTextStyle extends Style { 1824 private CharSequence mBigText; 1825 1826 public BigTextStyle() { 1827 } 1828 1829 public BigTextStyle(Builder builder) { 1830 setBuilder(builder); 1831 } 1832 1833 /** 1834 * Overrides ContentTitle in the big form of the template. 1835 * This defaults to the value passed to setContentTitle(). 1836 */ 1837 public BigTextStyle setBigContentTitle(CharSequence title) { 1838 internalSetBigContentTitle(title); 1839 return this; 1840 } 1841 1842 /** 1843 * Set the first line of text after the detail section in the big form of the template. 1844 */ 1845 public BigTextStyle setSummaryText(CharSequence cs) { 1846 internalSetSummaryText(cs); 1847 return this; 1848 } 1849 1850 /** 1851 * Provide the longer text to be displayed in the big form of the 1852 * template in place of the content text. 1853 */ 1854 public BigTextStyle bigText(CharSequence cs) { 1855 mBigText = cs; 1856 return this; 1857 } 1858 1859 private RemoteViews makeBigContentView() { 1860 // Remove the content text so line3 only shows if you have a summary 1861 final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null); 1862 mBuilder.mContentText = null; 1863 1864 RemoteViews contentView = getStandardView(R.layout.notification_template_big_text); 1865 1866 if (hadThreeLines) { 1867 // vertical centering 1868 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 1869 } 1870 1871 contentView.setTextViewText(R.id.big_text, mBigText); 1872 contentView.setViewVisibility(R.id.big_text, View.VISIBLE); 1873 contentView.setViewVisibility(R.id.text2, View.GONE); 1874 1875 return contentView; 1876 } 1877 1878 @Override 1879 public Notification build() { 1880 checkBuilder(); 1881 Notification wip = mBuilder.buildUnstyled(); 1882 wip.bigContentView = makeBigContentView(); 1883 return wip; 1884 } 1885 } 1886 1887 /** 1888 * Helper class for generating large-format notifications that include a list of (up to 5) strings. 1889 * 1890 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1891 * <pre class="prettyprint"> 1892 * Notification noti = new Notification.InboxStyle( 1893 * new Notification.Builder() 1894 * .setContentTitle("5 New mails from " + sender.toString()) 1895 * .setContentText(subject) 1896 * .setSmallIcon(R.drawable.new_mail) 1897 * .setLargeIcon(aBitmap)) 1898 * .addLine(str1) 1899 * .addLine(str2) 1900 * .setContentTitle("") 1901 * .setSummaryText("+3 more") 1902 * .build(); 1903 * </pre> 1904 * 1905 * @see Notification#bigContentView 1906 */ 1907 public static class InboxStyle extends Style { 1908 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5); 1909 1910 public InboxStyle() { 1911 } 1912 1913 public InboxStyle(Builder builder) { 1914 setBuilder(builder); 1915 } 1916 1917 /** 1918 * Overrides ContentTitle in the big form of the template. 1919 * This defaults to the value passed to setContentTitle(). 1920 */ 1921 public InboxStyle setBigContentTitle(CharSequence title) { 1922 internalSetBigContentTitle(title); 1923 return this; 1924 } 1925 1926 /** 1927 * Set the first line of text after the detail section in the big form of the template. 1928 */ 1929 public InboxStyle setSummaryText(CharSequence cs) { 1930 internalSetSummaryText(cs); 1931 return this; 1932 } 1933 1934 /** 1935 * Append a line to the digest section of the Inbox notification. 1936 */ 1937 public InboxStyle addLine(CharSequence cs) { 1938 mTexts.add(cs); 1939 return this; 1940 } 1941 1942 private RemoteViews makeBigContentView() { 1943 // Remove the content text so line3 disappears unless you have a summary 1944 mBuilder.mContentText = null; 1945 RemoteViews contentView = getStandardView(R.layout.notification_template_inbox); 1946 1947 contentView.setViewVisibility(R.id.text2, View.GONE); 1948 1949 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3, 1950 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6}; 1951 1952 // Make sure all rows are gone in case we reuse a view. 1953 for (int rowId : rowIds) { 1954 contentView.setViewVisibility(rowId, View.GONE); 1955 } 1956 1957 1958 int i=0; 1959 while (i < mTexts.size() && i < rowIds.length) { 1960 CharSequence str = mTexts.get(i); 1961 if (str != null && !str.equals("")) { 1962 contentView.setViewVisibility(rowIds[i], View.VISIBLE); 1963 contentView.setTextViewText(rowIds[i], str); 1964 } 1965 i++; 1966 } 1967 1968 contentView.setViewVisibility(R.id.inbox_end_pad, 1969 mTexts.size() > 0 ? View.VISIBLE : View.GONE); 1970 1971 contentView.setViewVisibility(R.id.inbox_more, 1972 mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE); 1973 1974 return contentView; 1975 } 1976 1977 @Override 1978 public Notification build() { 1979 checkBuilder(); 1980 Notification wip = mBuilder.buildUnstyled(); 1981 wip.bigContentView = makeBigContentView(); 1982 return wip; 1983 } 1984 } 1985} 1986