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