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