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. 901 * 902 * Example: 903 * 904 * <pre class="prettyprint"> 905 * Notification noti = new Notification.Builder() 906 * .setContentTitle("New mail from " + sender.toString()) 907 * .setContentText(subject) 908 * .setSmallIcon(R.drawable.new_mail) 909 * .setLargeIcon(aBitmap) 910 * .build(); 911 * </pre> 912 */ 913 public static class Builder { 914 private static final int MAX_ACTION_BUTTONS = 3; 915 916 private Context mContext; 917 918 private long mWhen; 919 private int mSmallIcon; 920 private int mSmallIconLevel; 921 private int mNumber; 922 private CharSequence mContentTitle; 923 private CharSequence mContentText; 924 private CharSequence mContentInfo; 925 private CharSequence mSubText; 926 private PendingIntent mContentIntent; 927 private RemoteViews mContentView; 928 private PendingIntent mDeleteIntent; 929 private PendingIntent mFullScreenIntent; 930 private CharSequence mTickerText; 931 private RemoteViews mTickerView; 932 private Bitmap mLargeIcon; 933 private Uri mSound; 934 private int mAudioStreamType; 935 private long[] mVibrate; 936 private int mLedArgb; 937 private int mLedOnMs; 938 private int mLedOffMs; 939 private int mDefaults; 940 private int mFlags; 941 private int mProgressMax; 942 private int mProgress; 943 private boolean mProgressIndeterminate; 944 private ArrayList<String> mKindList = new ArrayList<String>(1); 945 private Bundle mExtras; 946 private int mPriority; 947 private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS); 948 private boolean mUseChronometer; 949 private Style mStyle; 950 951 /** 952 * Constructs a new Builder with the defaults: 953 * 954 955 * <table> 956 * <tr><th align=right>priority</th> 957 * <td>{@link #PRIORITY_DEFAULT}</td></tr> 958 * <tr><th align=right>when</th> 959 * <td>now ({@link System#currentTimeMillis()})</td></tr> 960 * <tr><th align=right>audio stream</th> 961 * <td>{@link #STREAM_DEFAULT}</td></tr> 962 * </table> 963 * 964 965 * @param context 966 * A {@link Context} that will be used by the Builder to construct the 967 * RemoteViews. The Context will not be held past the lifetime of this Builder 968 * object. 969 */ 970 public Builder(Context context) { 971 mContext = context; 972 973 // Set defaults to match the defaults of a Notification 974 mWhen = System.currentTimeMillis(); 975 mAudioStreamType = STREAM_DEFAULT; 976 mPriority = PRIORITY_DEFAULT; 977 } 978 979 /** 980 * Add a timestamp pertaining to the notification (usually the time the event occurred). 981 * 982 983 * @see Notification#when 984 */ 985 public Builder setWhen(long when) { 986 mWhen = when; 987 return this; 988 } 989 990 /** 991 * Show the {@link Notification#when} field as a stopwatch. 992 * 993 * Instead of presenting <code>when</code> as a timestamp, the notification will show an 994 * automatically updating display of the minutes and seconds since <code>when</code>. 995 * 996 * Useful when showing an elapsed time (like an ongoing phone call). 997 * 998 * @see android.widget.Chronometer 999 * @see Notification#when 1000 */ 1001 public Builder setUsesChronometer(boolean b) { 1002 mUseChronometer = b; 1003 return this; 1004 } 1005 1006 /** 1007 * Set the small icon resource, which will be used to represent the notification in the 1008 * status bar. 1009 * 1010 1011 * The platform template for the expanded view will draw this icon in the left, unless a 1012 * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small 1013 * icon will be moved to the right-hand side. 1014 * 1015 1016 * @param icon 1017 * A resource ID in the application's package of the drawable to use. 1018 * @see Notification#icon 1019 */ 1020 public Builder setSmallIcon(int icon) { 1021 mSmallIcon = icon; 1022 return this; 1023 } 1024 1025 /** 1026 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional 1027 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable 1028 * LevelListDrawable}. 1029 * 1030 * @param icon A resource ID in the application's package of the drawable to use. 1031 * @param level The level to use for the icon. 1032 * 1033 * @see Notification#icon 1034 * @see Notification#iconLevel 1035 */ 1036 public Builder setSmallIcon(int icon, int level) { 1037 mSmallIcon = icon; 1038 mSmallIconLevel = level; 1039 return this; 1040 } 1041 1042 /** 1043 * Set the first line of text in the platform notification template. 1044 */ 1045 public Builder setContentTitle(CharSequence title) { 1046 mContentTitle = title; 1047 return this; 1048 } 1049 1050 /** 1051 * Set the second line of text in the platform notification template. 1052 */ 1053 public Builder setContentText(CharSequence text) { 1054 mContentText = text; 1055 return this; 1056 } 1057 1058 /** 1059 * Set the third line of text in the platform notification template. 1060 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the same location in the standard template. 1061 */ 1062 public Builder setSubText(CharSequence text) { 1063 mSubText = text; 1064 return this; 1065 } 1066 1067 /** 1068 * Set the large number at the right-hand side of the notification. This is 1069 * equivalent to setContentInfo, although it might show the number in a different 1070 * font size for readability. 1071 */ 1072 public Builder setNumber(int number) { 1073 mNumber = number; 1074 return this; 1075 } 1076 1077 /** 1078 * A small piece of additional information pertaining to this notification. 1079 * 1080 * The platform template will draw this on the last line of the notification, at the far 1081 * right (to the right of a smallIcon if it has been placed there). 1082 */ 1083 public Builder setContentInfo(CharSequence info) { 1084 mContentInfo = info; 1085 return this; 1086 } 1087 1088 /** 1089 * Set the progress this notification represents. 1090 * 1091 * The platform template will represent this using a {@link ProgressBar}. 1092 */ 1093 public Builder setProgress(int max, int progress, boolean indeterminate) { 1094 mProgressMax = max; 1095 mProgress = progress; 1096 mProgressIndeterminate = indeterminate; 1097 return this; 1098 } 1099 1100 /** 1101 * Supply a custom RemoteViews to use instead of the platform template. 1102 * 1103 * @see Notification#contentView 1104 */ 1105 public Builder setContent(RemoteViews views) { 1106 mContentView = views; 1107 return this; 1108 } 1109 1110 /** 1111 * Supply a {@link PendingIntent} to be sent when the notification is clicked. 1112 * 1113 * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you 1114 * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use 1115 * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)} 1116 * to assign PendingIntents to individual views in that custom layout (i.e., to create 1117 * clickable buttons inside the notification view). 1118 * 1119 * @see Notification#contentIntent Notification.contentIntent 1120 */ 1121 public Builder setContentIntent(PendingIntent intent) { 1122 mContentIntent = intent; 1123 return this; 1124 } 1125 1126 /** 1127 * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user. 1128 * 1129 * @see Notification#deleteIntent 1130 */ 1131 public Builder setDeleteIntent(PendingIntent intent) { 1132 mDeleteIntent = intent; 1133 return this; 1134 } 1135 1136 /** 1137 * An intent to launch instead of posting the notification to the status bar. 1138 * Only for use with extremely high-priority notifications demanding the user's 1139 * <strong>immediate</strong> attention, such as an incoming phone call or 1140 * alarm clock that the user has explicitly set to a particular time. 1141 * If this facility is used for something else, please give the user an option 1142 * to turn it off and use a normal notification, as this can be extremely 1143 * disruptive. 1144 * 1145 * @param intent The pending intent to launch. 1146 * @param highPriority Passing true will cause this notification to be sent 1147 * even if other notifications are suppressed. 1148 * 1149 * @see Notification#fullScreenIntent 1150 */ 1151 public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) { 1152 mFullScreenIntent = intent; 1153 setFlag(FLAG_HIGH_PRIORITY, highPriority); 1154 return this; 1155 } 1156 1157 /** 1158 * Set the "ticker" text which is displayed in the status bar when the notification first 1159 * arrives. 1160 * 1161 * @see Notification#tickerText 1162 */ 1163 public Builder setTicker(CharSequence tickerText) { 1164 mTickerText = tickerText; 1165 return this; 1166 } 1167 1168 /** 1169 * Set the text that is displayed in the status bar when the notification first 1170 * arrives, and also a RemoteViews object that may be displayed instead on some 1171 * devices. 1172 * 1173 * @see Notification#tickerText 1174 * @see Notification#tickerView 1175 */ 1176 public Builder setTicker(CharSequence tickerText, RemoteViews views) { 1177 mTickerText = tickerText; 1178 mTickerView = views; 1179 return this; 1180 } 1181 1182 /** 1183 * Add a large icon to the notification (and the ticker on some devices). 1184 * 1185 * In the platform template, this image will be shown on the left of the notification view 1186 * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side). 1187 * 1188 * @see Notification#largeIcon 1189 */ 1190 public Builder setLargeIcon(Bitmap icon) { 1191 mLargeIcon = icon; 1192 return this; 1193 } 1194 1195 /** 1196 * Set the sound to play. 1197 * 1198 * It will be played on the {@link #STREAM_DEFAULT default stream} for notifications. 1199 * 1200 * @see Notification#sound 1201 */ 1202 public Builder setSound(Uri sound) { 1203 mSound = sound; 1204 mAudioStreamType = STREAM_DEFAULT; 1205 return this; 1206 } 1207 1208 /** 1209 * Set the sound to play, along with a specific stream on which to play it. 1210 * 1211 * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants. 1212 * 1213 * @see Notification#sound 1214 */ 1215 public Builder setSound(Uri sound, int streamType) { 1216 mSound = sound; 1217 mAudioStreamType = streamType; 1218 return this; 1219 } 1220 1221 /** 1222 * Set the vibration pattern to use. 1223 * 1224 1225 * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the 1226 * <code>pattern</code> parameter. 1227 * 1228 1229 * @see Notification#vibrate 1230 */ 1231 public Builder setVibrate(long[] pattern) { 1232 mVibrate = pattern; 1233 return this; 1234 } 1235 1236 /** 1237 * Set the desired color for the indicator LED on the device, as well as the 1238 * blink duty cycle (specified in milliseconds). 1239 * 1240 1241 * Not all devices will honor all (or even any) of these values. 1242 * 1243 1244 * @see Notification#ledARGB 1245 * @see Notification#ledOnMS 1246 * @see Notification#ledOffMS 1247 */ 1248 public Builder setLights(int argb, int onMs, int offMs) { 1249 mLedArgb = argb; 1250 mLedOnMs = onMs; 1251 mLedOffMs = offMs; 1252 return this; 1253 } 1254 1255 /** 1256 * Set whether this is an "ongoing" notification. 1257 * 1258 1259 * Ongoing notifications cannot be dismissed by the user, so your application or service 1260 * must take care of canceling them. 1261 * 1262 1263 * They are typically used to indicate a background task that the user is actively engaged 1264 * with (e.g., playing music) or is pending in some way and therefore occupying the device 1265 * (e.g., a file download, sync operation, active network connection). 1266 * 1267 1268 * @see Notification#FLAG_ONGOING_EVENT 1269 * @see Service#setForeground(boolean) 1270 */ 1271 public Builder setOngoing(boolean ongoing) { 1272 setFlag(FLAG_ONGOING_EVENT, ongoing); 1273 return this; 1274 } 1275 1276 /** 1277 * Set this flag if you would only like the sound, vibrate 1278 * and ticker to be played if the notification is not already showing. 1279 * 1280 * @see Notification#FLAG_ONLY_ALERT_ONCE 1281 */ 1282 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) { 1283 setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce); 1284 return this; 1285 } 1286 1287 /** 1288 * Make this notification automatically dismissed when the user touches it. The 1289 * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens. 1290 * 1291 * @see Notification#FLAG_AUTO_CANCEL 1292 */ 1293 public Builder setAutoCancel(boolean autoCancel) { 1294 setFlag(FLAG_AUTO_CANCEL, autoCancel); 1295 return this; 1296 } 1297 1298 /** 1299 * Set which notification properties will be inherited from system defaults. 1300 * <p> 1301 * The value should be one or more of the following fields combined with 1302 * bitwise-or: 1303 * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. 1304 * <p> 1305 * For all default values, use {@link #DEFAULT_ALL}. 1306 */ 1307 public Builder setDefaults(int defaults) { 1308 mDefaults = defaults; 1309 return this; 1310 } 1311 1312 /** 1313 * Set the priority of this notification. 1314 * 1315 * @see Notification#priority 1316 */ 1317 public Builder setPriority(int pri) { 1318 mPriority = pri; 1319 return this; 1320 } 1321 1322 /** 1323 * @hide 1324 * 1325 * Add a kind (category) to this notification. Optional. 1326 * 1327 * @see Notification#kind 1328 */ 1329 public Builder addKind(String k) { 1330 mKindList.add(k); 1331 return this; 1332 } 1333 1334 /** 1335 * Add metadata to this notification. 1336 * 1337 * A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's 1338 * current contents are copied into the Notification each time {@link #build()} is 1339 * called. 1340 * 1341 * @see Notification#extras 1342 * @hide 1343 */ 1344 public Builder setExtras(Bundle bag) { 1345 mExtras = bag; 1346 return this; 1347 } 1348 1349 /** 1350 * Add an action to this notification. Actions are typically displayed by 1351 * the system as a button adjacent to the notification content. 1352 * 1353 * @param icon Resource ID of a drawable that represents the action. 1354 * @param title Text describing the action. 1355 * @param intent PendingIntent to be fired when the action is invoked. 1356 */ 1357 public Builder addAction(int icon, CharSequence title, PendingIntent intent) { 1358 mActions.add(new Action(icon, title, intent)); 1359 return this; 1360 } 1361 1362 /** 1363 * Add a rich notification style to be applied at build time. 1364 * 1365 * @param style Object responsible for modifying the notification style. 1366 */ 1367 public Builder setStyle(Style style) { 1368 if (mStyle != style) { 1369 mStyle = style; 1370 if (mStyle != null) { 1371 mStyle.setBuilder(this); 1372 } 1373 } 1374 return this; 1375 } 1376 1377 private void setFlag(int mask, boolean value) { 1378 if (value) { 1379 mFlags |= mask; 1380 } else { 1381 mFlags &= ~mask; 1382 } 1383 } 1384 1385 private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) { 1386 RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId); 1387 boolean showLine3 = false; 1388 boolean showLine2 = false; 1389 int smallIconImageViewId = R.id.icon; 1390 if (mLargeIcon != null) { 1391 contentView.setImageViewBitmap(R.id.icon, mLargeIcon); 1392 smallIconImageViewId = R.id.right_icon; 1393 } 1394 if (mPriority < PRIORITY_LOW) { 1395 contentView.setInt(R.id.icon, 1396 "setBackgroundResource", R.drawable.notification_template_icon_low_bg); 1397 contentView.setInt(R.id.status_bar_latest_event_content, 1398 "setBackgroundResource", R.drawable.notification_bg_low); 1399 } 1400 if (mSmallIcon != 0) { 1401 contentView.setImageViewResource(smallIconImageViewId, mSmallIcon); 1402 contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE); 1403 } else { 1404 contentView.setViewVisibility(smallIconImageViewId, View.GONE); 1405 } 1406 if (mContentTitle != null) { 1407 contentView.setTextViewText(R.id.title, mContentTitle); 1408 } 1409 if (mContentText != null) { 1410 contentView.setTextViewText(R.id.text, mContentText); 1411 showLine3 = true; 1412 } 1413 if (mContentInfo != null) { 1414 contentView.setTextViewText(R.id.info, mContentInfo); 1415 contentView.setViewVisibility(R.id.info, View.VISIBLE); 1416 showLine3 = true; 1417 } else if (mNumber > 0) { 1418 final int tooBig = mContext.getResources().getInteger( 1419 R.integer.status_bar_notification_info_maxnum); 1420 if (mNumber > tooBig) { 1421 contentView.setTextViewText(R.id.info, mContext.getResources().getString( 1422 R.string.status_bar_notification_info_overflow)); 1423 } else { 1424 NumberFormat f = NumberFormat.getIntegerInstance(); 1425 contentView.setTextViewText(R.id.info, f.format(mNumber)); 1426 } 1427 contentView.setViewVisibility(R.id.info, View.VISIBLE); 1428 showLine3 = true; 1429 } else { 1430 contentView.setViewVisibility(R.id.info, View.GONE); 1431 } 1432 1433 // Need to show three lines? 1434 if (mSubText != null) { 1435 contentView.setTextViewText(R.id.text, mSubText); 1436 if (mContentText != null) { 1437 contentView.setTextViewText(R.id.text2, mContentText); 1438 contentView.setViewVisibility(R.id.text2, View.VISIBLE); 1439 showLine2 = true; 1440 } else { 1441 contentView.setViewVisibility(R.id.text2, View.GONE); 1442 } 1443 } else { 1444 contentView.setViewVisibility(R.id.text2, View.GONE); 1445 if (mProgressMax != 0 || mProgressIndeterminate) { 1446 contentView.setProgressBar( 1447 R.id.progress, mProgressMax, mProgress, mProgressIndeterminate); 1448 contentView.setViewVisibility(R.id.progress, View.VISIBLE); 1449 showLine2 = true; 1450 } else { 1451 contentView.setViewVisibility(R.id.progress, View.GONE); 1452 } 1453 } 1454 if (showLine2) { 1455 if (fitIn1U) { 1456 // need to shrink all the type to make sure everything fits 1457 final Resources res = mContext.getResources(); 1458 final float subTextSize = res.getDimensionPixelSize( 1459 R.dimen.notification_subtext_size); 1460 contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize); 1461 } 1462 // vertical centering 1463 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 1464 } 1465 1466 if (mWhen != 0) { 1467 if (mUseChronometer) { 1468 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE); 1469 contentView.setLong(R.id.chronometer, "setBase", 1470 mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis())); 1471 contentView.setBoolean(R.id.chronometer, "setStarted", true); 1472 } else { 1473 contentView.setViewVisibility(R.id.time, View.VISIBLE); 1474 contentView.setLong(R.id.time, "setTime", mWhen); 1475 } 1476 } 1477 contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE); 1478 contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE); 1479 return contentView; 1480 } 1481 1482 private RemoteViews applyStandardTemplateWithActions(int layoutId) { 1483 RemoteViews big = applyStandardTemplate(layoutId, false); 1484 1485 int N = mActions.size(); 1486 if (N > 0) { 1487 // Log.d("Notification", "has actions: " + mContentText); 1488 big.setViewVisibility(R.id.actions, View.VISIBLE); 1489 big.setViewVisibility(R.id.action_divider, View.VISIBLE); 1490 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS; 1491 big.removeAllViews(R.id.actions); 1492 for (int i=0; i<N; i++) { 1493 final RemoteViews button = generateActionButton(mActions.get(i)); 1494 //Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title); 1495 big.addView(R.id.actions, button); 1496 } 1497 } 1498 return big; 1499 } 1500 1501 private RemoteViews makeContentView() { 1502 if (mContentView != null) { 1503 return mContentView; 1504 } else { 1505 return applyStandardTemplate(R.layout.notification_template_base, true); // no more special large_icon flavor 1506 } 1507 } 1508 1509 private RemoteViews makeTickerView() { 1510 if (mTickerView != null) { 1511 return mTickerView; 1512 } else { 1513 if (mContentView == null) { 1514 return applyStandardTemplate(mLargeIcon == null 1515 ? R.layout.status_bar_latest_event_ticker 1516 : R.layout.status_bar_latest_event_ticker_large_icon, true); 1517 } else { 1518 return null; 1519 } 1520 } 1521 } 1522 1523 private RemoteViews makeBigContentView() { 1524 if (mActions.size() == 0) return null; 1525 1526 return applyStandardTemplateWithActions(R.layout.notification_template_big_base); 1527 } 1528 1529 private RemoteViews generateActionButton(Action action) { 1530 final boolean tombstone = (action.actionIntent == null); 1531 RemoteViews button = new RemoteViews(mContext.getPackageName(), 1532 tombstone ? R.layout.notification_action_tombstone 1533 : R.layout.notification_action); 1534 button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0); 1535 button.setTextViewText(R.id.action0, action.title); 1536 if (!tombstone) { 1537 button.setOnClickPendingIntent(R.id.action0, action.actionIntent); 1538 } 1539 button.setContentDescription(R.id.action0, action.title); 1540 return button; 1541 } 1542 1543 /** 1544 * Apply the unstyled operations and return a new {@link Notification} object. 1545 */ 1546 private Notification buildUnstyled() { 1547 Notification n = new Notification(); 1548 n.when = mWhen; 1549 n.icon = mSmallIcon; 1550 n.iconLevel = mSmallIconLevel; 1551 n.number = mNumber; 1552 n.contentView = makeContentView(); 1553 n.contentIntent = mContentIntent; 1554 n.deleteIntent = mDeleteIntent; 1555 n.fullScreenIntent = mFullScreenIntent; 1556 n.tickerText = mTickerText; 1557 n.tickerView = makeTickerView(); 1558 n.largeIcon = mLargeIcon; 1559 n.sound = mSound; 1560 n.audioStreamType = mAudioStreamType; 1561 n.vibrate = mVibrate; 1562 n.ledARGB = mLedArgb; 1563 n.ledOnMS = mLedOnMs; 1564 n.ledOffMS = mLedOffMs; 1565 n.defaults = mDefaults; 1566 n.flags = mFlags; 1567 n.bigContentView = makeBigContentView(); 1568 if (mLedOnMs != 0 && mLedOffMs != 0) { 1569 n.flags |= FLAG_SHOW_LIGHTS; 1570 } 1571 if ((mDefaults & DEFAULT_LIGHTS) != 0) { 1572 n.flags |= FLAG_SHOW_LIGHTS; 1573 } 1574 if (mKindList.size() > 0) { 1575 n.kind = new String[mKindList.size()]; 1576 mKindList.toArray(n.kind); 1577 } else { 1578 n.kind = null; 1579 } 1580 n.priority = mPriority; 1581 n.extras = mExtras != null ? new Bundle(mExtras) : null; 1582 if (mActions.size() > 0) { 1583 n.actions = new Action[mActions.size()]; 1584 mActions.toArray(n.actions); 1585 } 1586 return n; 1587 } 1588 1589 /** 1590 * @deprecated Use {@link #build()} instead. 1591 */ 1592 @Deprecated 1593 public Notification getNotification() { 1594 return build(); 1595 } 1596 1597 /** 1598 * Combine all of the options that have been set and return a new {@link Notification} 1599 * object. 1600 */ 1601 public Notification build() { 1602 if (mStyle != null) { 1603 return mStyle.build(); 1604 } else { 1605 return buildUnstyled(); 1606 } 1607 } 1608 } 1609 1610 1611 /** 1612 * An object that can apply a rich notification style to a {@link Notification.Builder} 1613 * object. 1614 */ 1615 public static abstract class Style 1616 { 1617 private CharSequence mBigContentTitle; 1618 private CharSequence mSummaryText = null; 1619 private boolean mSummaryTextSet = false; 1620 1621 protected Builder mBuilder; 1622 1623 /** 1624 * Overrides ContentTitle in the big form of the template. 1625 * This defaults to the value passed to setContentTitle(). 1626 */ 1627 protected void internalSetBigContentTitle(CharSequence title) { 1628 mBigContentTitle = title; 1629 } 1630 1631 /** 1632 * Set the first line of text after the detail section in the big form of the template. 1633 */ 1634 protected void internalSetSummaryText(CharSequence cs) { 1635 mSummaryText = cs; 1636 mSummaryTextSet = true; 1637 } 1638 1639 public void setBuilder(Builder builder) { 1640 if (mBuilder != builder) { 1641 mBuilder = builder; 1642 if (mBuilder != null) { 1643 mBuilder.setStyle(this); 1644 } 1645 } 1646 } 1647 1648 protected void checkBuilder() { 1649 if (mBuilder == null) { 1650 throw new IllegalArgumentException("Style requires a valid Builder object"); 1651 } 1652 } 1653 1654 protected RemoteViews getStandardView(int layoutId) { 1655 checkBuilder(); 1656 1657 if (mBigContentTitle != null) { 1658 mBuilder.setContentTitle(mBigContentTitle); 1659 } 1660 1661 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId); 1662 1663 if (mBigContentTitle != null && mBigContentTitle.equals("")) { 1664 contentView.setViewVisibility(R.id.line1, View.GONE); 1665 } else { 1666 contentView.setViewVisibility(R.id.line1, View.VISIBLE); 1667 } 1668 1669 // The last line defaults to the subtext, but can be replaced by mSummaryText 1670 final CharSequence overflowText = 1671 mSummaryTextSet ? mSummaryText 1672 : mBuilder.mSubText; 1673 if (overflowText != null) { 1674 contentView.setTextViewText(R.id.text, overflowText); 1675 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE); 1676 contentView.setViewVisibility(R.id.line3, View.VISIBLE); 1677 } else { 1678 contentView.setViewVisibility(R.id.overflow_divider, View.GONE); 1679 contentView.setViewVisibility(R.id.line3, View.GONE); 1680 } 1681 1682 return contentView; 1683 } 1684 1685 public abstract Notification build(); 1686 } 1687 1688 /** 1689 * Helper class for generating large-format notifications that include a large image attachment. 1690 * 1691 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1692 * <pre class="prettyprint"> 1693 * Notification noti = new Notification.BigPictureStyle( 1694 * new Notification.Builder() 1695 * .setContentTitle("New photo from " + sender.toString()) 1696 * .setContentText(subject) 1697 * .setSmallIcon(R.drawable.new_post) 1698 * .setLargeIcon(aBitmap)) 1699 * .bigPicture(aBigBitmap) 1700 * .build(); 1701 * </pre> 1702 * 1703 * @see Notification#bigContentView 1704 */ 1705 public static class BigPictureStyle extends Style { 1706 private Bitmap mPicture; 1707 private Bitmap mBigLargeIcon; 1708 private boolean mBigLargeIconSet = false; 1709 1710 public BigPictureStyle() { 1711 } 1712 1713 public BigPictureStyle(Builder builder) { 1714 setBuilder(builder); 1715 } 1716 1717 /** 1718 * Overrides ContentTitle in the big form of the template. 1719 * This defaults to the value passed to setContentTitle(). 1720 */ 1721 public BigPictureStyle setBigContentTitle(CharSequence title) { 1722 internalSetBigContentTitle(title); 1723 return this; 1724 } 1725 1726 /** 1727 * Set the first line of text after the detail section in the big form of the template. 1728 */ 1729 public BigPictureStyle setSummaryText(CharSequence cs) { 1730 internalSetSummaryText(cs); 1731 return this; 1732 } 1733 1734 public BigPictureStyle bigPicture(Bitmap b) { 1735 mPicture = b; 1736 return this; 1737 } 1738 1739 /** 1740 * Override the large icon when the big notification is shown. 1741 */ 1742 public BigPictureStyle bigLargeIcon(Bitmap b) { 1743 mBigLargeIconSet = true; 1744 mBigLargeIcon = b; 1745 return this; 1746 } 1747 1748 private RemoteViews makeBigContentView() { 1749 RemoteViews contentView = getStandardView(R.layout.notification_template_big_picture); 1750 1751 contentView.setImageViewBitmap(R.id.big_picture, mPicture); 1752 1753 return contentView; 1754 } 1755 1756 @Override 1757 public Notification build() { 1758 checkBuilder(); 1759 Notification wip = mBuilder.buildUnstyled(); 1760 if (mBigLargeIconSet ) { 1761 mBuilder.mLargeIcon = mBigLargeIcon; 1762 } 1763 wip.bigContentView = makeBigContentView(); 1764 return wip; 1765 } 1766 } 1767 1768 /** 1769 * Helper class for generating large-format notifications that include a lot of text. 1770 * 1771 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1772 * <pre class="prettyprint"> 1773 * Notification noti = new Notification.BigPictureStyle( 1774 * new Notification.Builder() 1775 * .setContentTitle("New mail from " + sender.toString()) 1776 * .setContentText(subject) 1777 * .setSmallIcon(R.drawable.new_mail) 1778 * .setLargeIcon(aBitmap)) 1779 * .bigText(aVeryLongString) 1780 * .build(); 1781 * </pre> 1782 * 1783 * @see Notification#bigContentView 1784 */ 1785 public static class BigTextStyle extends Style { 1786 private CharSequence mBigText; 1787 1788 public BigTextStyle() { 1789 } 1790 1791 public BigTextStyle(Builder builder) { 1792 setBuilder(builder); 1793 } 1794 1795 /** 1796 * Overrides ContentTitle in the big form of the template. 1797 * This defaults to the value passed to setContentTitle(). 1798 */ 1799 public BigTextStyle setBigContentTitle(CharSequence title) { 1800 internalSetBigContentTitle(title); 1801 return this; 1802 } 1803 1804 /** 1805 * Set the first line of text after the detail section in the big form of the template. 1806 */ 1807 public BigTextStyle setSummaryText(CharSequence cs) { 1808 internalSetSummaryText(cs); 1809 return this; 1810 } 1811 1812 public BigTextStyle bigText(CharSequence cs) { 1813 mBigText = cs; 1814 return this; 1815 } 1816 1817 private RemoteViews makeBigContentView() { 1818 // Remove the content text so line3 only shows if you have a summary 1819 final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null); 1820 mBuilder.mContentText = null; 1821 1822 RemoteViews contentView = getStandardView(R.layout.notification_template_big_text); 1823 1824 if (hadThreeLines) { 1825 // vertical centering 1826 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); 1827 } 1828 1829 contentView.setTextViewText(R.id.big_text, mBigText); 1830 contentView.setViewVisibility(R.id.big_text, View.VISIBLE); 1831 contentView.setViewVisibility(R.id.text2, View.GONE); 1832 1833 return contentView; 1834 } 1835 1836 @Override 1837 public Notification build() { 1838 checkBuilder(); 1839 Notification wip = mBuilder.buildUnstyled(); 1840 wip.bigContentView = makeBigContentView(); 1841 return wip; 1842 } 1843 } 1844 1845 /** 1846 * Helper class for generating large-format notifications that include a list of (up to 5) strings. 1847 * 1848 * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: 1849 * <pre class="prettyprint"> 1850 * Notification noti = new Notification.InboxStyle( 1851 * new Notification.Builder() 1852 * .setContentTitle("5 New mails from " + sender.toString()) 1853 * .setContentText(subject) 1854 * .setSmallIcon(R.drawable.new_mail) 1855 * .setLargeIcon(aBitmap)) 1856 * .addLine(str1) 1857 * .addLine(str2) 1858 * .setContentTitle("") 1859 * .setSummaryText("+3 more") 1860 * .build(); 1861 * </pre> 1862 * 1863 * @see Notification#bigContentView 1864 */ 1865 public static class InboxStyle extends Style { 1866 private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5); 1867 1868 public InboxStyle() { 1869 } 1870 1871 public InboxStyle(Builder builder) { 1872 setBuilder(builder); 1873 } 1874 1875 /** 1876 * Overrides ContentTitle in the big form of the template. 1877 * This defaults to the value passed to setContentTitle(). 1878 */ 1879 public InboxStyle setBigContentTitle(CharSequence title) { 1880 internalSetBigContentTitle(title); 1881 return this; 1882 } 1883 1884 /** 1885 * Set the first line of text after the detail section in the big form of the template. 1886 */ 1887 public InboxStyle setSummaryText(CharSequence cs) { 1888 internalSetSummaryText(cs); 1889 return this; 1890 } 1891 1892 public InboxStyle addLine(CharSequence cs) { 1893 mTexts.add(cs); 1894 return this; 1895 } 1896 1897 private RemoteViews makeBigContentView() { 1898 // Remove the content text so line3 disappears unless you have a summary 1899 mBuilder.mContentText = null; 1900 RemoteViews contentView = getStandardView(R.layout.notification_template_inbox); 1901 1902 contentView.setViewVisibility(R.id.text2, View.GONE); 1903 1904 int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3, 1905 R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6}; 1906 1907 // Make sure all rows are gone in case we reuse a view. 1908 for (int rowId : rowIds) { 1909 contentView.setViewVisibility(rowId, View.GONE); 1910 } 1911 1912 int i=0; 1913 while (i < mTexts.size() && i < rowIds.length) { 1914 CharSequence str = mTexts.get(i); 1915 if (str != null && !str.equals("")) { 1916 contentView.setViewVisibility(rowIds[i], View.VISIBLE); 1917 contentView.setTextViewText(rowIds[i], str); 1918 } 1919 i++; 1920 } 1921 1922 if (mTexts.size() > rowIds.length) { 1923 contentView.setViewVisibility(R.id.inbox_more, View.VISIBLE); 1924 } else { 1925 contentView.setViewVisibility(R.id.inbox_more, View.GONE); 1926 } 1927 1928 return contentView; 1929 } 1930 1931 @Override 1932 public Notification build() { 1933 checkBuilder(); 1934 Notification wip = mBuilder.buildUnstyled(); 1935 wip.bigContentView = makeBigContentView(); 1936 return wip; 1937 } 1938 } 1939 } 1940