NotificationCompat.java revision c39d9c75590eca86a5e7e32a8824ba04a0d42e9b
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v4.app; 18 19import android.app.Activity; 20import android.app.Notification; 21import android.app.PendingIntent; 22import android.content.Context; 23import android.graphics.Bitmap; 24import android.graphics.Color; 25import android.media.AudioManager; 26import android.net.Uri; 27import android.os.Build; 28import android.os.Bundle; 29import android.os.Parcelable; 30import android.support.annotation.ColorInt; 31import android.support.annotation.RestrictTo; 32import android.support.v4.os.BuildCompat; 33import android.support.v4.view.GravityCompat; 34import android.view.Gravity; 35import android.widget.RemoteViews; 36 37import java.util.ArrayList; 38import java.util.Collections; 39import java.util.List; 40 41import static android.support.annotation.RestrictTo.Scope.GROUP_ID; 42 43/** 44 * Helper for accessing features in {@link android.app.Notification} 45 * introduced after API level 4 in a backwards compatible fashion. 46 */ 47public class NotificationCompat { 48 49 /** 50 * Use all default values (where applicable). 51 */ 52 public static final int DEFAULT_ALL = ~0; 53 54 /** 55 * Use the default notification sound. This will ignore any sound set using 56 * {@link Builder#setSound} 57 * 58 * <p> 59 * A notification that is noisy is more likely to be presented as a heads-up notification, 60 * on some platforms. 61 * </p> 62 * 63 * @see Builder#setDefaults 64 */ 65 public static final int DEFAULT_SOUND = 1; 66 67 /** 68 * Use the default notification vibrate. This will ignore any vibrate set using 69 * {@link Builder#setVibrate}. Using phone vibration requires the 70 * {@link android.Manifest.permission#VIBRATE VIBRATE} permission. 71 * 72 * <p> 73 * A notification that vibrates is more likely to be presented as a heads-up notification, 74 * on some platforms. 75 * </p> 76 * 77 * @see Builder#setDefaults 78 */ 79 public static final int DEFAULT_VIBRATE = 2; 80 81 /** 82 * Use the default notification lights. This will ignore the 83 * {@link #FLAG_SHOW_LIGHTS} bit, and values set with {@link Builder#setLights}. 84 * 85 * @see Builder#setDefaults 86 */ 87 public static final int DEFAULT_LIGHTS = 4; 88 89 /** 90 * Use this constant as the value for audioStreamType to request that 91 * the default stream type for notifications be used. Currently the 92 * default stream type is {@link AudioManager#STREAM_NOTIFICATION}. 93 */ 94 public static final int STREAM_DEFAULT = -1; 95 96 /** 97 * Bit set in the Notification flags field when LEDs should be turned on 98 * for this notification. 99 */ 100 public static final int FLAG_SHOW_LIGHTS = 0x00000001; 101 102 /** 103 * Bit set in the Notification flags field if this notification is in 104 * reference to something that is ongoing, like a phone call. It should 105 * not be set if this notification is in reference to something that 106 * happened at a particular point in time, like a missed phone call. 107 */ 108 public static final int FLAG_ONGOING_EVENT = 0x00000002; 109 110 /** 111 * Bit set in the Notification flags field if 112 * the audio will be repeated until the notification is 113 * cancelled or the notification window is opened. 114 */ 115 public static final int FLAG_INSISTENT = 0x00000004; 116 117 /** 118 * Bit set in the Notification flags field if the notification's sound, 119 * vibrate and ticker should only be played if the notification is not already showing. 120 */ 121 public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008; 122 123 /** 124 * Bit set in the Notification flags field if the notification should be canceled when 125 * it is clicked by the user. 126 */ 127 public static final int FLAG_AUTO_CANCEL = 0x00000010; 128 129 /** 130 * Bit set in the Notification flags field if the notification should not be canceled 131 * when the user clicks the Clear all button. 132 */ 133 public static final int FLAG_NO_CLEAR = 0x00000020; 134 135 /** 136 * Bit set in the Notification flags field if this notification represents a currently 137 * running service. This will normally be set for you by 138 * {@link android.app.Service#startForeground}. 139 */ 140 public static final int FLAG_FOREGROUND_SERVICE = 0x00000040; 141 142 /** 143 * Obsolete flag indicating high-priority notifications; use the priority field instead. 144 * 145 * @deprecated Use {@link NotificationCompat.Builder#setPriority(int)} with a positive value. 146 */ 147 @Deprecated 148 public static final int FLAG_HIGH_PRIORITY = 0x00000080; 149 150 /** 151 * Bit set in the Notification flags field if this notification is relevant to the current 152 * device only and it is not recommended that it bridge to other devices. 153 */ 154 public static final int FLAG_LOCAL_ONLY = 0x00000100; 155 156 /** 157 * Bit set in the Notification flags field if this notification is the group summary for a 158 * group of notifications. Grouped notifications may display in a cluster or stack on devices 159 * which support such rendering. Requires a group key also be set using 160 * {@link Builder#setGroup}. 161 */ 162 public static final int FLAG_GROUP_SUMMARY = 0x00000200; 163 164 /** 165 * Default notification priority for {@link NotificationCompat.Builder#setPriority(int)}. 166 * If your application does not prioritize its own notifications, 167 * use this value for all notifications. 168 */ 169 public static final int PRIORITY_DEFAULT = 0; 170 171 /** 172 * Lower notification priority for {@link NotificationCompat.Builder#setPriority(int)}, 173 * for items that are less important. The UI may choose to show 174 * these items smaller, or at a different position in the list, 175 * compared with your app's {@link #PRIORITY_DEFAULT} items. 176 */ 177 public static final int PRIORITY_LOW = -1; 178 179 /** 180 * Lowest notification priority for {@link NotificationCompat.Builder#setPriority(int)}; 181 * these items might not be shown to the user except under 182 * special circumstances, such as detailed notification logs. 183 */ 184 public static final int PRIORITY_MIN = -2; 185 186 /** 187 * Higher notification priority for {@link NotificationCompat.Builder#setPriority(int)}, 188 * for more important notifications or alerts. The UI may choose 189 * to show these items larger, or at a different position in 190 * notification lists, compared with your app's {@link #PRIORITY_DEFAULT} items. 191 */ 192 public static final int PRIORITY_HIGH = 1; 193 194 /** 195 * Highest notification priority for {@link NotificationCompat.Builder#setPriority(int)}, 196 * for your application's most important items that require the user's 197 * prompt attention or input. 198 */ 199 public static final int PRIORITY_MAX = 2; 200 201 /** 202 * Notification extras key: this is the title of the notification, 203 * as supplied to {@link Builder#setContentTitle(CharSequence)}. 204 */ 205 public static final String EXTRA_TITLE = "android.title"; 206 207 /** 208 * Notification extras key: this is the title of the notification when shown in expanded form, 209 * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}. 210 */ 211 public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big"; 212 213 /** 214 * Notification extras key: this is the main text payload, as supplied to 215 * {@link Builder#setContentText(CharSequence)}. 216 */ 217 public static final String EXTRA_TEXT = "android.text"; 218 219 /** 220 * Notification extras key: this is a third line of text, as supplied to 221 * {@link Builder#setSubText(CharSequence)}. 222 */ 223 public static final String EXTRA_SUB_TEXT = "android.subText"; 224 225 /** 226 * Notification extras key: this is the remote input history, as supplied to 227 * {@link Builder#setRemoteInputHistory(CharSequence[])}. 228 * 229 * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])} 230 * with the most recent inputs that have been sent through a {@link RemoteInput} of this 231 * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat 232 * notifications once the other party has responded). 233 * 234 * The extra with this key is of type CharSequence[] and contains the most recent entry at 235 * the 0 index, the second most recent at the 1 index, etc. 236 * 237 * @see Builder#setRemoteInputHistory(CharSequence[]) 238 */ 239 public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory"; 240 241 /** 242 * Notification extras key: this is a small piece of additional text as supplied to 243 * {@link Builder#setContentInfo(CharSequence)}. 244 */ 245 public static final String EXTRA_INFO_TEXT = "android.infoText"; 246 247 /** 248 * Notification extras key: this is a line of summary information intended to be shown 249 * alongside expanded notifications, as supplied to (e.g.) 250 * {@link BigTextStyle#setSummaryText(CharSequence)}. 251 */ 252 public static final String EXTRA_SUMMARY_TEXT = "android.summaryText"; 253 254 /** 255 * Notification extras key: this is the longer text shown in the big form of a 256 * {@link BigTextStyle} notification, as supplied to 257 * {@link BigTextStyle#bigText(CharSequence)}. 258 */ 259 public static final String EXTRA_BIG_TEXT = "android.bigText"; 260 261 /** 262 * Notification extras key: this is the resource ID of the notification's main small icon, as 263 * supplied to {@link Builder#setSmallIcon(int)}. 264 */ 265 public static final String EXTRA_SMALL_ICON = "android.icon"; 266 267 /** 268 * Notification extras key: this is a bitmap to be used instead of the small icon when showing the 269 * notification payload, as 270 * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}. 271 */ 272 public static final String EXTRA_LARGE_ICON = "android.largeIcon"; 273 274 /** 275 * Notification extras key: this is a bitmap to be used instead of the one from 276 * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is 277 * shown in its expanded form, as supplied to 278 * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}. 279 */ 280 public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big"; 281 282 /** 283 * Notification extras key: this is the progress value supplied to 284 * {@link Builder#setProgress(int, int, boolean)}. 285 */ 286 public static final String EXTRA_PROGRESS = "android.progress"; 287 288 /** 289 * Notification extras key: this is the maximum value supplied to 290 * {@link Builder#setProgress(int, int, boolean)}. 291 */ 292 public static final String EXTRA_PROGRESS_MAX = "android.progressMax"; 293 294 /** 295 * Notification extras key: whether the progress bar is indeterminate, supplied to 296 * {@link Builder#setProgress(int, int, boolean)}. 297 */ 298 public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate"; 299 300 /** 301 * Notification extras key: whether the when field set using {@link Builder#setWhen} should 302 * be shown as a count-up timer (specifically a {@link android.widget.Chronometer}) instead 303 * of a timestamp, as supplied to {@link Builder#setUsesChronometer(boolean)}. 304 */ 305 public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer"; 306 307 /** 308 * Notification extras key: whether the when field set using {@link Builder#setWhen} should 309 * be shown, as supplied to {@link Builder#setShowWhen(boolean)}. 310 */ 311 public static final String EXTRA_SHOW_WHEN = "android.showWhen"; 312 313 /** 314 * Notification extras key: this is a bitmap to be shown in {@link BigPictureStyle} expanded 315 * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}. 316 */ 317 public static final String EXTRA_PICTURE = "android.picture"; 318 319 /** 320 * Notification extras key: An array of CharSequences to show in {@link InboxStyle} expanded 321 * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}. 322 */ 323 public static final String EXTRA_TEXT_LINES = "android.textLines"; 324 325 /** 326 * Notification extras key: A string representing the name of the specific 327 * {@link android.app.Notification.Style} used to create this notification. 328 */ 329 public static final String EXTRA_TEMPLATE = "android.template"; 330 331 /** 332 * Notification extras key: A String array containing the people that this 333 * notification relates to, each of which was supplied to 334 * {@link Builder#addPerson(String)}. 335 */ 336 public static final String EXTRA_PEOPLE = "android.people"; 337 338 /** 339 * Notification extras key: A 340 * {@link android.content.ContentUris content URI} pointing to an image that can be displayed 341 * in the background when the notification is selected. The URI must point to an image stream 342 * suitable for passing into 343 * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream) 344 * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider 345 * URI used for this purpose must require no permissions to read the image data. 346 */ 347 public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; 348 349 /** 350 * Notification key: A 351 * {@link android.media.session.MediaSession.Token} associated with a 352 * {@link android.app.Notification.MediaStyle} notification. 353 */ 354 public static final String EXTRA_MEDIA_SESSION = "android.mediaSession"; 355 356 /** 357 * Notification extras key: the indices of actions to be shown in the compact view, 358 * as supplied to (e.g.) {@link Notification.MediaStyle#setShowActionsInCompactView(int...)}. 359 */ 360 public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions"; 361 362 /** 363 * Notification key: the username to be displayed for all messages sent by the user 364 * including 365 * direct replies 366 * {@link MessagingStyle} notification. 367 */ 368 public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName"; 369 370 /** 371 * Notification key: a {@link String} to be displayed as the title to a conversation 372 * represented by a {@link MessagingStyle} 373 */ 374 public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle"; 375 376 /** 377 * Notification key: an array of {@link Bundle} objects representing 378 * {@link MessagingStyle.Message} objects for a {@link MessagingStyle} notification. 379 */ 380 public static final String EXTRA_MESSAGES = "android.messages"; 381 382 /** 383 * Value of {@link Notification#color} equal to 0 (also known as 384 * {@link android.graphics.Color#TRANSPARENT Color.TRANSPARENT}), 385 * telling the system not to decorate this notification with any special color but instead use 386 * default colors when presenting this notification. 387 */ 388 @ColorInt 389 public static final int COLOR_DEFAULT = Color.TRANSPARENT; 390 391 /** 392 * Notification visibility: Show this notification in its entirety on all lockscreens. 393 * 394 * {@see android.app.Notification#visibility} 395 */ 396 public static final int VISIBILITY_PUBLIC = 1; 397 398 /** 399 * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or 400 * private information on secure lockscreens. 401 * 402 * {@see android.app.Notification#visibility} 403 */ 404 public static final int VISIBILITY_PRIVATE = 0; 405 406 /** 407 * Notification visibility: Do not reveal any part of this notification on a secure lockscreen. 408 * 409 * {@see android.app.Notification#visibility} 410 */ 411 public static final int VISIBILITY_SECRET = -1; 412 413 /** 414 * Notification category: incoming call (voice or video) or similar synchronous communication request. 415 */ 416 public static final String CATEGORY_CALL = NotificationCompatApi21.CATEGORY_CALL; 417 418 /** 419 * Notification category: incoming direct message (SMS, instant message, etc.). 420 */ 421 public static final String CATEGORY_MESSAGE = NotificationCompatApi21.CATEGORY_MESSAGE; 422 423 /** 424 * Notification category: asynchronous bulk message (email). 425 */ 426 public static final String CATEGORY_EMAIL = NotificationCompatApi21.CATEGORY_EMAIL; 427 428 /** 429 * Notification category: calendar event. 430 */ 431 public static final String CATEGORY_EVENT = NotificationCompatApi21.CATEGORY_EVENT; 432 433 /** 434 * Notification category: promotion or advertisement. 435 */ 436 public static final String CATEGORY_PROMO = NotificationCompatApi21.CATEGORY_PROMO; 437 438 /** 439 * Notification category: alarm or timer. 440 */ 441 public static final String CATEGORY_ALARM = NotificationCompatApi21.CATEGORY_ALARM; 442 443 /** 444 * Notification category: progress of a long-running background operation. 445 */ 446 public static final String CATEGORY_PROGRESS = NotificationCompatApi21.CATEGORY_PROGRESS; 447 448 /** 449 * Notification category: social network or sharing update. 450 */ 451 public static final String CATEGORY_SOCIAL = NotificationCompatApi21.CATEGORY_SOCIAL; 452 453 /** 454 * Notification category: error in background operation or authentication status. 455 */ 456 public static final String CATEGORY_ERROR = NotificationCompatApi21.CATEGORY_ERROR; 457 458 /** 459 * Notification category: media transport control for playback. 460 */ 461 public static final String CATEGORY_TRANSPORT = NotificationCompatApi21.CATEGORY_TRANSPORT; 462 463 /** 464 * Notification category: system or device status update. Reserved for system use. 465 */ 466 public static final String CATEGORY_SYSTEM = NotificationCompatApi21.CATEGORY_SYSTEM; 467 468 /** 469 * Notification category: indication of running background service. 470 */ 471 public static final String CATEGORY_SERVICE = NotificationCompatApi21.CATEGORY_SERVICE; 472 473 /** 474 * Notification category: user-scheduled reminder. 475 */ 476 public static final String CATEGORY_REMINDER = NotificationCompatApi23.CATEGORY_REMINDER; 477 478 /** 479 * Notification category: a specific, timely recommendation for a single thing. 480 * For example, a news app might want to recommend a news story it believes the user will 481 * want to read next. 482 */ 483 public static final String CATEGORY_RECOMMENDATION = 484 NotificationCompatApi21.CATEGORY_RECOMMENDATION; 485 486 /** 487 * Notification category: ongoing information about device or contextual status. 488 */ 489 public static final String CATEGORY_STATUS = NotificationCompatApi21.CATEGORY_STATUS; 490 491 static final NotificationCompatImpl IMPL; 492 493 interface NotificationCompatImpl { 494 public Notification build(Builder b, BuilderExtender extender); 495 public Bundle getExtras(Notification n); 496 public int getActionCount(Notification n); 497 public Action getAction(Notification n, int actionIndex); 498 public Action[] getActionsFromParcelableArrayList(ArrayList<Parcelable> parcelables); 499 public ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions); 500 public String getCategory(Notification n); 501 public boolean getLocalOnly(Notification n); 502 public String getGroup(Notification n); 503 public boolean isGroupSummary(Notification n); 504 public String getSortKey(Notification n); 505 Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc); 506 NotificationCompatBase.UnreadConversation getUnreadConversationFromBundle( 507 Bundle b, NotificationCompatBase.UnreadConversation.Factory factory, 508 RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory); 509 } 510 511 /** 512 * Interface for appcompat to extend v4 builder with media style. 513 * 514 * @hide 515 */ 516 @RestrictTo(GROUP_ID) 517 protected static class BuilderExtender { 518 public Notification build(Builder b, NotificationBuilderWithBuilderAccessor builder) { 519 Notification n = builder.build(); 520 if (b.mContentView != null) { 521 n.contentView = b.mContentView; 522 } 523 return n; 524 } 525 } 526 527 static class NotificationCompatImplBase implements NotificationCompatImpl { 528 @Override 529 public Notification build(Builder b, BuilderExtender extender) { 530 Notification result = b.mNotification; 531 result = NotificationCompatBase.add(result, b.mContext, 532 b.resolveTitle(), b.resolveText(), b.mContentIntent, b.mFullScreenIntent); 533 // translate high priority requests into legacy flag 534 if (b.mPriority > PRIORITY_DEFAULT) { 535 result.flags |= FLAG_HIGH_PRIORITY; 536 } 537 if (b.mContentView != null) { 538 result.contentView = b.mContentView; 539 } 540 return result; 541 } 542 543 @Override 544 public Bundle getExtras(Notification n) { 545 return null; 546 } 547 548 @Override 549 public int getActionCount(Notification n) { 550 return 0; 551 } 552 553 @Override 554 public Action getAction(Notification n, int actionIndex) { 555 return null; 556 } 557 558 @Override 559 public Action[] getActionsFromParcelableArrayList( 560 ArrayList<Parcelable> parcelables) { 561 return null; 562 } 563 564 @Override 565 public ArrayList<Parcelable> getParcelableArrayListForActions(Action[] actions) { 566 return null; 567 } 568 569 @Override 570 public String getCategory(Notification n) { 571 return null; 572 } 573 574 @Override 575 public boolean getLocalOnly(Notification n) { 576 return false; 577 } 578 579 @Override 580 public String getGroup(Notification n) { 581 return null; 582 } 583 584 @Override 585 public boolean isGroupSummary(Notification n) { 586 return false; 587 } 588 589 @Override 590 public String getSortKey(Notification n) { 591 return null; 592 } 593 594 @Override 595 public Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) { 596 return null; 597 } 598 599 @Override 600 public NotificationCompatBase.UnreadConversation getUnreadConversationFromBundle( 601 Bundle b, NotificationCompatBase.UnreadConversation.Factory factory, 602 RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) { 603 return null; 604 } 605 } 606 607 static class NotificationCompatImplHoneycomb extends NotificationCompatImplBase { 608 @Override 609 public Notification build(Builder b, BuilderExtender extender) { 610 Notification notification = NotificationCompatHoneycomb.add(b.mContext, b.mNotification, 611 b.resolveTitle(), b.resolveText(), b.mContentInfo, b.mTickerView, 612 b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon); 613 if (b.mContentView != null) { 614 notification.contentView = b.mContentView; 615 } 616 return notification; 617 } 618 } 619 620 static class NotificationCompatImplIceCreamSandwich extends NotificationCompatImplBase { 621 @Override 622 public Notification build(Builder b, BuilderExtender extender) { 623 NotificationCompatIceCreamSandwich.Builder builder = 624 new NotificationCompatIceCreamSandwich.Builder(b.mContext, b.mNotification, 625 b.resolveTitle(), b.resolveText(), b.mContentInfo, b.mTickerView, 626 b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon, 627 b.mProgressMax, b.mProgress, b.mProgressIndeterminate); 628 return extender.build(b, builder); 629 } 630 } 631 632 static class NotificationCompatImplJellybean extends NotificationCompatImplBase { 633 @Override 634 public Notification build(Builder b, BuilderExtender extender) { 635 NotificationCompatJellybean.Builder builder = new NotificationCompatJellybean.Builder( 636 b.mContext, b.mNotification, b.resolveTitle(), b.resolveText(), b.mContentInfo, 637 b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon, 638 b.mProgressMax, b.mProgress, b.mProgressIndeterminate, 639 b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mExtras, 640 b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mContentView, b.mBigContentView); 641 addActionsToBuilder(builder, b.mActions); 642 addStyleToBuilderJellybean(builder, b.mStyle); 643 Notification notification = extender.build(b, builder); 644 if (b.mStyle != null) { 645 Bundle extras = getExtras(notification); 646 if (extras != null) { 647 b.mStyle.addCompatExtras(extras); 648 } 649 } 650 return notification; 651 } 652 653 @Override 654 public Bundle getExtras(Notification n) { 655 return NotificationCompatJellybean.getExtras(n); 656 } 657 658 @Override 659 public int getActionCount(Notification n) { 660 return NotificationCompatJellybean.getActionCount(n); 661 } 662 663 @Override 664 public Action getAction(Notification n, int actionIndex) { 665 return (Action) NotificationCompatJellybean.getAction(n, actionIndex, Action.FACTORY, 666 RemoteInput.FACTORY); 667 } 668 669 @Override 670 public Action[] getActionsFromParcelableArrayList( 671 ArrayList<Parcelable> parcelables) { 672 return (Action[]) NotificationCompatJellybean.getActionsFromParcelableArrayList( 673 parcelables, Action.FACTORY, RemoteInput.FACTORY); 674 } 675 676 @Override 677 public ArrayList<Parcelable> getParcelableArrayListForActions( 678 Action[] actions) { 679 return NotificationCompatJellybean.getParcelableArrayListForActions(actions); 680 } 681 682 @Override 683 public boolean getLocalOnly(Notification n) { 684 return NotificationCompatJellybean.getLocalOnly(n); 685 } 686 687 @Override 688 public String getGroup(Notification n) { 689 return NotificationCompatJellybean.getGroup(n); 690 } 691 692 @Override 693 public boolean isGroupSummary(Notification n) { 694 return NotificationCompatJellybean.isGroupSummary(n); 695 } 696 697 @Override 698 public String getSortKey(Notification n) { 699 return NotificationCompatJellybean.getSortKey(n); 700 } 701 } 702 703 static class NotificationCompatImplKitKat extends NotificationCompatImplJellybean { 704 @Override 705 public Notification build(Builder b, BuilderExtender extender) { 706 NotificationCompatKitKat.Builder builder = new NotificationCompatKitKat.Builder( 707 b.mContext, b.mNotification, b.resolveTitle(), b.resolveText(), b.mContentInfo, 708 b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon, 709 b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen, 710 b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, 711 b.mPeople, b.mExtras, b.mGroupKey, b.mGroupSummary, b.mSortKey, 712 b.mContentView, b.mBigContentView); 713 addActionsToBuilder(builder, b.mActions); 714 addStyleToBuilderJellybean(builder, b.mStyle); 715 return extender.build(b, builder); 716 } 717 718 @Override 719 public Bundle getExtras(Notification n) { 720 return NotificationCompatKitKat.getExtras(n); 721 } 722 723 @Override 724 public int getActionCount(Notification n) { 725 return NotificationCompatKitKat.getActionCount(n); 726 } 727 728 @Override 729 public Action getAction(Notification n, int actionIndex) { 730 return (Action) NotificationCompatKitKat.getAction(n, actionIndex, Action.FACTORY, 731 RemoteInput.FACTORY); 732 } 733 734 @Override 735 public boolean getLocalOnly(Notification n) { 736 return NotificationCompatKitKat.getLocalOnly(n); 737 } 738 739 @Override 740 public String getGroup(Notification n) { 741 return NotificationCompatKitKat.getGroup(n); 742 } 743 744 @Override 745 public boolean isGroupSummary(Notification n) { 746 return NotificationCompatKitKat.isGroupSummary(n); 747 } 748 749 @Override 750 public String getSortKey(Notification n) { 751 return NotificationCompatKitKat.getSortKey(n); 752 } 753 } 754 755 static class NotificationCompatImplApi20 extends NotificationCompatImplKitKat { 756 @Override 757 public Notification build(Builder b, BuilderExtender extender) { 758 NotificationCompatApi20.Builder builder = new NotificationCompatApi20.Builder( 759 b.mContext, b.mNotification, b.resolveTitle(), b.resolveText(), b.mContentInfo, 760 b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon, 761 b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen, 762 b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mPeople, b.mExtras, 763 b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mContentView, b.mBigContentView); 764 addActionsToBuilder(builder, b.mActions); 765 addStyleToBuilderJellybean(builder, b.mStyle); 766 Notification notification = extender.build(b, builder); 767 if (b.mStyle != null) { 768 b.mStyle.addCompatExtras(getExtras(notification)); 769 } 770 return notification; 771 } 772 773 @Override 774 public Action getAction(Notification n, int actionIndex) { 775 return (Action) NotificationCompatApi20.getAction(n, actionIndex, Action.FACTORY, 776 RemoteInput.FACTORY); 777 } 778 779 @Override 780 public Action[] getActionsFromParcelableArrayList( 781 ArrayList<Parcelable> parcelables) { 782 return (Action[]) NotificationCompatApi20.getActionsFromParcelableArrayList( 783 parcelables, Action.FACTORY, RemoteInput.FACTORY); 784 } 785 786 @Override 787 public ArrayList<Parcelable> getParcelableArrayListForActions( 788 Action[] actions) { 789 return NotificationCompatApi20.getParcelableArrayListForActions(actions); 790 } 791 792 @Override 793 public boolean getLocalOnly(Notification n) { 794 return NotificationCompatApi20.getLocalOnly(n); 795 } 796 797 @Override 798 public String getGroup(Notification n) { 799 return NotificationCompatApi20.getGroup(n); 800 } 801 802 @Override 803 public boolean isGroupSummary(Notification n) { 804 return NotificationCompatApi20.isGroupSummary(n); 805 } 806 807 @Override 808 public String getSortKey(Notification n) { 809 return NotificationCompatApi20.getSortKey(n); 810 } 811 } 812 813 static class NotificationCompatImplApi21 extends NotificationCompatImplApi20 { 814 @Override 815 public Notification build(Builder b, BuilderExtender extender) { 816 NotificationCompatApi21.Builder builder = new NotificationCompatApi21.Builder( 817 b.mContext, b.mNotification, b.resolveTitle(), b.resolveText(), b.mContentInfo, 818 b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon, 819 b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen, 820 b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory, 821 b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion, 822 b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mContentView, b.mBigContentView, 823 b.mHeadsUpContentView); 824 addActionsToBuilder(builder, b.mActions); 825 addStyleToBuilderJellybean(builder, b.mStyle); 826 Notification notification = extender.build(b, builder); 827 if (b.mStyle != null) { 828 b.mStyle.addCompatExtras(getExtras(notification)); 829 } 830 return notification; 831 } 832 833 @Override 834 public String getCategory(Notification notif) { 835 return NotificationCompatApi21.getCategory(notif); 836 } 837 838 @Override 839 public Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) { 840 return NotificationCompatApi21.getBundleForUnreadConversation(uc); 841 } 842 843 @Override 844 public NotificationCompatBase.UnreadConversation getUnreadConversationFromBundle( 845 Bundle b, NotificationCompatBase.UnreadConversation.Factory factory, 846 RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) { 847 return NotificationCompatApi21.getUnreadConversationFromBundle( 848 b, factory, remoteInputFactory); 849 } 850 } 851 852 static class NotificationCompatImplApi24 extends NotificationCompatImplApi21 { 853 @Override 854 public Notification build(Builder b, 855 BuilderExtender extender) { 856 NotificationCompatApi24.Builder builder = new NotificationCompatApi24.Builder( 857 b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo, 858 b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon, 859 b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen, 860 b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory, 861 b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion, 862 b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mRemoteInputHistory, b.mContentView, 863 b.mBigContentView, b.mHeadsUpContentView); 864 addActionsToBuilder(builder, b.mActions); 865 addStyleToBuilderApi24(builder, b.mStyle); 866 Notification notification = extender.build(b, builder); 867 if (b.mStyle != null) { 868 b.mStyle.addCompatExtras(getExtras(notification)); 869 } 870 return notification; 871 } 872 } 873 874 static void addActionsToBuilder(NotificationBuilderWithActions builder, 875 ArrayList<Action> actions) { 876 for (Action action : actions) { 877 builder.addAction(action); 878 } 879 } 880 881 static void addStyleToBuilderJellybean(NotificationBuilderWithBuilderAccessor builder, 882 Style style) { 883 if (style != null) { 884 if (style instanceof BigTextStyle) { 885 BigTextStyle bigTextStyle = (BigTextStyle) style; 886 NotificationCompatJellybean.addBigTextStyle(builder, 887 bigTextStyle.mBigContentTitle, 888 bigTextStyle.mSummaryTextSet, 889 bigTextStyle.mSummaryText, 890 bigTextStyle.mBigText); 891 } else if (style instanceof InboxStyle) { 892 InboxStyle inboxStyle = (InboxStyle) style; 893 NotificationCompatJellybean.addInboxStyle(builder, 894 inboxStyle.mBigContentTitle, 895 inboxStyle.mSummaryTextSet, 896 inboxStyle.mSummaryText, 897 inboxStyle.mTexts); 898 } else if (style instanceof BigPictureStyle) { 899 BigPictureStyle bigPictureStyle = (BigPictureStyle) style; 900 NotificationCompatJellybean.addBigPictureStyle(builder, 901 bigPictureStyle.mBigContentTitle, 902 bigPictureStyle.mSummaryTextSet, 903 bigPictureStyle.mSummaryText, 904 bigPictureStyle.mPicture, 905 bigPictureStyle.mBigLargeIcon, 906 bigPictureStyle.mBigLargeIconSet); 907 } 908 } 909 } 910 911 static void addStyleToBuilderApi24(NotificationBuilderWithBuilderAccessor builder, 912 Style style) { 913 if (style != null) { 914 if (style instanceof MessagingStyle) { 915 MessagingStyle messagingStyle = (MessagingStyle) style; 916 List<CharSequence> texts = new ArrayList<>(); 917 List<Long> timestamps = new ArrayList<>(); 918 List<CharSequence> senders = new ArrayList<>(); 919 List<String> dataMimeTypes = new ArrayList<>(); 920 List<Uri> dataUris = new ArrayList<>(); 921 922 for (MessagingStyle.Message message : messagingStyle.mMessages) { 923 texts.add(message.getText()); 924 timestamps.add(message.getTimestamp()); 925 senders.add(message.getSender()); 926 dataMimeTypes.add(message.getDataMimeType()); 927 dataUris.add(message.getDataUri()); 928 } 929 NotificationCompatApi24.addMessagingStyle(builder, messagingStyle.mUserDisplayName, 930 messagingStyle.mConversationTitle, texts, timestamps, senders, 931 dataMimeTypes, dataUris); 932 } else { 933 addStyleToBuilderJellybean(builder, style); 934 } 935 } 936 } 937 938 static { 939 if (BuildCompat.isAtLeastN()) { 940 IMPL = new NotificationCompatImplApi24(); 941 } else if (Build.VERSION.SDK_INT >= 21) { 942 IMPL = new NotificationCompatImplApi21(); 943 } else if (Build.VERSION.SDK_INT >= 20) { 944 IMPL = new NotificationCompatImplApi20(); 945 } else if (Build.VERSION.SDK_INT >= 19) { 946 IMPL = new NotificationCompatImplKitKat(); 947 } else if (Build.VERSION.SDK_INT >= 16) { 948 IMPL = new NotificationCompatImplJellybean(); 949 } else if (Build.VERSION.SDK_INT >= 14) { 950 IMPL = new NotificationCompatImplIceCreamSandwich(); 951 } else if (Build.VERSION.SDK_INT >= 11) { 952 IMPL = new NotificationCompatImplHoneycomb(); 953 } else { 954 IMPL = new NotificationCompatImplBase(); 955 } 956 } 957 958 /** 959 * Builder class for {@link NotificationCompat} objects. Allows easier control over 960 * all the flags, as well as help constructing the typical notification layouts. 961 * <p> 962 * On platform versions that don't offer expanded notifications, methods that depend on 963 * expanded notifications have no effect. 964 * </p> 965 * <p> 966 * For example, action buttons won't appear on platforms prior to Android 4.1. Action 967 * buttons depend on expanded notifications, which are only available in Android 4.1 968 * and later. 969 * <p> 970 * For this reason, you should always ensure that UI controls in a notification are also 971 * available in an {@link android.app.Activity} in your app, and you should always start that 972 * {@link android.app.Activity} when users click the notification. To do this, use the 973 * {@link NotificationCompat.Builder#setContentIntent setContentIntent()} 974 * method. 975 * </p> 976 * 977 */ 978 public static class Builder { 979 /** 980 * Maximum length of CharSequences accepted by Builder and friends. 981 * 982 * <p> 983 * Avoids spamming the system with overly large strings such as full e-mails. 984 */ 985 private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024; 986 987 // All these variables are declared public/hidden so they can be accessed by a builder 988 // extender. 989 990 /** @hide */ 991 @RestrictTo(GROUP_ID) 992 public Context mContext; 993 994 /** @hide */ 995 @RestrictTo(GROUP_ID) 996 public CharSequence mContentTitle; 997 /** @hide */ 998 @RestrictTo(GROUP_ID) 999 public CharSequence mContentText; 1000 PendingIntent mContentIntent; 1001 PendingIntent mFullScreenIntent; 1002 RemoteViews mTickerView; 1003 /** @hide */ 1004 @RestrictTo(GROUP_ID) 1005 public Bitmap mLargeIcon; 1006 /** @hide */ 1007 @RestrictTo(GROUP_ID) 1008 public CharSequence mContentInfo; 1009 /** @hide */ 1010 @RestrictTo(GROUP_ID) 1011 public int mNumber; 1012 int mPriority; 1013 boolean mShowWhen = true; 1014 /** @hide */ 1015 @RestrictTo(GROUP_ID) 1016 public boolean mUseChronometer; 1017 /** @hide */ 1018 @RestrictTo(GROUP_ID) 1019 public Style mStyle; 1020 /** @hide */ 1021 @RestrictTo(GROUP_ID) 1022 public CharSequence mSubText; 1023 /** @hide */ 1024 @RestrictTo(GROUP_ID) 1025 public CharSequence[] mRemoteInputHistory; 1026 int mProgressMax; 1027 int mProgress; 1028 boolean mProgressIndeterminate; 1029 String mGroupKey; 1030 boolean mGroupSummary; 1031 String mSortKey; 1032 /** @hide */ 1033 @RestrictTo(GROUP_ID) 1034 public ArrayList<Action> mActions = new ArrayList<Action>(); 1035 boolean mLocalOnly = false; 1036 String mCategory; 1037 Bundle mExtras; 1038 int mColor = COLOR_DEFAULT; 1039 int mVisibility = VISIBILITY_PRIVATE; 1040 Notification mPublicVersion; 1041 RemoteViews mContentView; 1042 RemoteViews mBigContentView; 1043 RemoteViews mHeadsUpContentView; 1044 1045 /** @hide */ 1046 @RestrictTo(GROUP_ID) 1047 public Notification mNotification = new Notification(); 1048 public ArrayList<String> mPeople; 1049 1050 /** 1051 * Constructor. 1052 * 1053 * Automatically sets the when field to {@link System#currentTimeMillis() 1054 * System.currentTimeMillis()} and the audio stream to the 1055 * {@link Notification#STREAM_DEFAULT}. 1056 * 1057 * @param context A {@link Context} that will be used to construct the 1058 * RemoteViews. The Context will not be held past the lifetime of this 1059 * Builder object. 1060 */ 1061 public Builder(Context context) { 1062 mContext = context; 1063 1064 // Set defaults to match the defaults of a Notification 1065 mNotification.when = System.currentTimeMillis(); 1066 mNotification.audioStreamType = Notification.STREAM_DEFAULT; 1067 mPriority = PRIORITY_DEFAULT; 1068 mPeople = new ArrayList<String>(); 1069 } 1070 1071 /** 1072 * Set the time that the event occurred. Notifications in the panel are 1073 * sorted by this time. 1074 */ 1075 public Builder setWhen(long when) { 1076 mNotification.when = when; 1077 return this; 1078 } 1079 1080 /** 1081 * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown 1082 * in the content view. 1083 */ 1084 public Builder setShowWhen(boolean show) { 1085 mShowWhen = show; 1086 return this; 1087 } 1088 1089 /** 1090 * Show the {@link Notification#when} field as a stopwatch. 1091 * 1092 * Instead of presenting <code>when</code> as a timestamp, the notification will show an 1093 * automatically updating display of the minutes and seconds since <code>when</code>. 1094 * 1095 * Useful when showing an elapsed time (like an ongoing phone call). 1096 * 1097 * @see android.widget.Chronometer 1098 * @see Notification#when 1099 */ 1100 public Builder setUsesChronometer(boolean b) { 1101 mUseChronometer = b; 1102 return this; 1103 } 1104 1105 /** 1106 * Set the small icon to use in the notification layouts. Different classes of devices 1107 * may return different sizes. See the UX guidelines for more information on how to 1108 * design these icons. 1109 * 1110 * @param icon A resource ID in the application's package of the drawble to use. 1111 */ 1112 public Builder setSmallIcon(int icon) { 1113 mNotification.icon = icon; 1114 return this; 1115 } 1116 1117 /** 1118 * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional 1119 * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable 1120 * LevelListDrawable}. 1121 * 1122 * @param icon A resource ID in the application's package of the drawble to use. 1123 * @param level The level to use for the icon. 1124 * 1125 * @see android.graphics.drawable.LevelListDrawable 1126 */ 1127 public Builder setSmallIcon(int icon, int level) { 1128 mNotification.icon = icon; 1129 mNotification.iconLevel = level; 1130 return this; 1131 } 1132 1133 /** 1134 * Set the title (first row) of the notification, in a standard notification. 1135 */ 1136 public Builder setContentTitle(CharSequence title) { 1137 mContentTitle = limitCharSequenceLength(title); 1138 return this; 1139 } 1140 1141 /** 1142 * Set the text (second row) of the notification, in a standard notification. 1143 */ 1144 public Builder setContentText(CharSequence text) { 1145 mContentText = limitCharSequenceLength(text); 1146 return this; 1147 } 1148 1149 /** 1150 * Set the third line of text in the platform notification template. 1151 * Don't use if you're also using {@link #setProgress(int, int, boolean)}; 1152 * they occupy the same location in the standard template. 1153 * <br> 1154 * If the platform does not provide large-format notifications, this method has no effect. 1155 * The third line of text only appears in expanded view. 1156 * <br> 1157 */ 1158 public Builder setSubText(CharSequence text) { 1159 mSubText = limitCharSequenceLength(text); 1160 return this; 1161 } 1162 1163 /** 1164 * Set the remote input history. 1165 * 1166 * This should be set to the most recent inputs that have been sent 1167 * through a {@link RemoteInput} of this Notification and cleared once the it is no 1168 * longer relevant (e.g. for chat notifications once the other party has responded). 1169 * 1170 * The most recent input must be stored at the 0 index, the second most recent at the 1171 * 1 index, etc. Note that the system will limit both how far back the inputs will be shown 1172 * and how much of each individual input is shown. 1173 * 1174 * <p>Note: The reply text will only be shown on notifications that have least one action 1175 * with a {@code RemoteInput}.</p> 1176 */ 1177 public Builder setRemoteInputHistory(CharSequence[] text) { 1178 mRemoteInputHistory = text; 1179 return this; 1180 } 1181 1182 /** 1183 * Set the large number at the right-hand side of the notification. This is 1184 * equivalent to setContentInfo, although it might show the number in a different 1185 * font size for readability. 1186 */ 1187 public Builder setNumber(int number) { 1188 mNumber = number; 1189 return this; 1190 } 1191 1192 /** 1193 * Set the large text at the right-hand side of the notification. 1194 */ 1195 public Builder setContentInfo(CharSequence info) { 1196 mContentInfo = limitCharSequenceLength(info); 1197 return this; 1198 } 1199 1200 /** 1201 * Set the progress this notification represents, which may be 1202 * represented as a {@link android.widget.ProgressBar}. 1203 */ 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 standard one. 1213 */ 1214 public Builder setContent(RemoteViews views) { 1215 mNotification.contentView = views; 1216 return this; 1217 } 1218 1219 /** 1220 * Supply a {@link PendingIntent} to send when the notification is clicked. 1221 * If you do not supply an intent, you can now add PendingIntents to individual 1222 * views to be launched when clicked by calling {@link RemoteViews#setOnClickPendingIntent 1223 * RemoteViews.setOnClickPendingIntent(int,PendingIntent)}. Be sure to 1224 * read {@link Notification#contentIntent Notification.contentIntent} for 1225 * how to correctly use this. 1226 */ 1227 public Builder setContentIntent(PendingIntent intent) { 1228 mContentIntent = intent; 1229 return this; 1230 } 1231 1232 /** 1233 * Supply a {@link PendingIntent} to send when the notification is cleared by the user 1234 * directly from the notification panel. For example, this intent is sent when the user 1235 * clicks the "Clear all" button, or the individual "X" buttons on notifications. This 1236 * intent is not sent when the application calls 1237 * {@link android.app.NotificationManager#cancel NotificationManager.cancel(int)}. 1238 */ 1239 public Builder setDeleteIntent(PendingIntent intent) { 1240 mNotification.deleteIntent = intent; 1241 return this; 1242 } 1243 1244 /** 1245 * An intent to launch instead of posting the notification to the status bar. 1246 * Only for use with extremely high-priority notifications demanding the user's 1247 * <strong>immediate</strong> attention, such as an incoming phone call or 1248 * alarm clock that the user has explicitly set to a particular time. 1249 * If this facility is used for something else, please give the user an option 1250 * to turn it off and use a normal notification, as this can be extremely 1251 * disruptive. 1252 * 1253 * <p> 1254 * On some platforms, the system UI may choose to display a heads-up notification, 1255 * instead of launching this intent, while the user is using the device. 1256 * </p> 1257 * 1258 * @param intent The pending intent to launch. 1259 * @param highPriority Passing true will cause this notification to be sent 1260 * even if other notifications are suppressed. 1261 */ 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 text that is displayed in the status bar when the notification first 1270 * arrives. 1271 */ 1272 public Builder setTicker(CharSequence tickerText) { 1273 mNotification.tickerText = limitCharSequenceLength(tickerText); 1274 return this; 1275 } 1276 1277 /** 1278 * Set the text that is displayed in the status bar when the notification first 1279 * arrives, and also a RemoteViews object that may be displayed instead on some 1280 * devices. 1281 */ 1282 public Builder setTicker(CharSequence tickerText, RemoteViews views) { 1283 mNotification.tickerText = limitCharSequenceLength(tickerText); 1284 mTickerView = views; 1285 return this; 1286 } 1287 1288 /** 1289 * Set the large icon that is shown in the ticker and notification. 1290 */ 1291 public Builder setLargeIcon(Bitmap icon) { 1292 mLargeIcon = icon; 1293 return this; 1294 } 1295 1296 /** 1297 * Set the sound to play. It will play on the default stream. 1298 * 1299 * <p> 1300 * On some platforms, a notification that is noisy is more likely to be presented 1301 * as a heads-up notification. 1302 * </p> 1303 */ 1304 public Builder setSound(Uri sound) { 1305 mNotification.sound = sound; 1306 mNotification.audioStreamType = Notification.STREAM_DEFAULT; 1307 return this; 1308 } 1309 1310 /** 1311 * Set the sound to play. It will play on the stream you supply. 1312 * 1313 * <p> 1314 * On some platforms, a notification that is noisy is more likely to be presented 1315 * as a heads-up notification. 1316 * </p> 1317 * 1318 * @see Notification#STREAM_DEFAULT 1319 * @see AudioManager for the <code>STREAM_</code> constants. 1320 */ 1321 public Builder setSound(Uri sound, int streamType) { 1322 mNotification.sound = sound; 1323 mNotification.audioStreamType = streamType; 1324 return this; 1325 } 1326 1327 /** 1328 * Set the vibration pattern to use. 1329 * 1330 * <p> 1331 * On some platforms, a notification that vibrates is more likely to be presented 1332 * as a heads-up notification. 1333 * </p> 1334 * 1335 * @see android.os.Vibrator for a discussion of the <code>pattern</code> 1336 * parameter. 1337 */ 1338 public Builder setVibrate(long[] pattern) { 1339 mNotification.vibrate = pattern; 1340 return this; 1341 } 1342 1343 /** 1344 * Set the argb value that you would like the LED on the device to blink, as well as the 1345 * rate. The rate is specified in terms of the number of milliseconds to be on 1346 * and then the number of milliseconds to be off. 1347 */ 1348 public Builder setLights(@ColorInt int argb, int onMs, int offMs) { 1349 mNotification.ledARGB = argb; 1350 mNotification.ledOnMS = onMs; 1351 mNotification.ledOffMS = offMs; 1352 boolean showLights = mNotification.ledOnMS != 0 && mNotification.ledOffMS != 0; 1353 mNotification.flags = (mNotification.flags & ~Notification.FLAG_SHOW_LIGHTS) | 1354 (showLights ? Notification.FLAG_SHOW_LIGHTS : 0); 1355 return this; 1356 } 1357 1358 /** 1359 * Set whether this is an ongoing notification. 1360 * 1361 * <p>Ongoing notifications differ from regular notifications in the following ways: 1362 * <ul> 1363 * <li>Ongoing notifications are sorted above the regular notifications in the 1364 * notification panel.</li> 1365 * <li>Ongoing notifications do not have an 'X' close button, and are not affected 1366 * by the "Clear all" button. 1367 * </ul> 1368 */ 1369 public Builder setOngoing(boolean ongoing) { 1370 setFlag(Notification.FLAG_ONGOING_EVENT, ongoing); 1371 return this; 1372 } 1373 1374 /** 1375 * Set this flag if you would only like the sound, vibrate 1376 * and ticker to be played if the notification is not already showing. 1377 */ 1378 public Builder setOnlyAlertOnce(boolean onlyAlertOnce) { 1379 setFlag(Notification.FLAG_ONLY_ALERT_ONCE, onlyAlertOnce); 1380 return this; 1381 } 1382 1383 /** 1384 * Setting this flag will make it so the notification is automatically 1385 * canceled when the user clicks it in the panel. The PendingIntent 1386 * set with {@link #setDeleteIntent} will be broadcast when the notification 1387 * is canceled. 1388 */ 1389 public Builder setAutoCancel(boolean autoCancel) { 1390 setFlag(Notification.FLAG_AUTO_CANCEL, autoCancel); 1391 return this; 1392 } 1393 1394 /** 1395 * Set whether or not this notification is only relevant to the current device. 1396 * 1397 * <p>Some notifications can be bridged to other devices for remote display. 1398 * This hint can be set to recommend this notification not be bridged. 1399 */ 1400 public Builder setLocalOnly(boolean b) { 1401 mLocalOnly = b; 1402 return this; 1403 } 1404 1405 /** 1406 * Set the notification category. 1407 * 1408 * <p>Must be one of the predefined notification categories (see the <code>CATEGORY_*</code> 1409 * constants in {@link Notification}) that best describes this notification. 1410 * May be used by the system for ranking and filtering. 1411 */ 1412 public Builder setCategory(String category) { 1413 mCategory = category; 1414 return this; 1415 } 1416 1417 /** 1418 * Set the default notification options that will be used. 1419 * <p> 1420 * The value should be one or more of the following fields combined with 1421 * bitwise-or: 1422 * {@link Notification#DEFAULT_SOUND}, {@link Notification#DEFAULT_VIBRATE}, 1423 * {@link Notification#DEFAULT_LIGHTS}. 1424 * <p> 1425 * For all default values, use {@link Notification#DEFAULT_ALL}. 1426 */ 1427 public Builder setDefaults(int defaults) { 1428 mNotification.defaults = defaults; 1429 if ((defaults & Notification.DEFAULT_LIGHTS) != 0) { 1430 mNotification.flags |= Notification.FLAG_SHOW_LIGHTS; 1431 } 1432 return this; 1433 } 1434 1435 private void setFlag(int mask, boolean value) { 1436 if (value) { 1437 mNotification.flags |= mask; 1438 } else { 1439 mNotification.flags &= ~mask; 1440 } 1441 } 1442 1443 /** 1444 * Set the relative priority for this notification. 1445 * 1446 * Priority is an indication of how much of the user's 1447 * valuable attention should be consumed by this 1448 * notification. Low-priority notifications may be hidden from 1449 * the user in certain situations, while the user might be 1450 * interrupted for a higher-priority notification. 1451 * The system sets a notification's priority based on various factors including the 1452 * setPriority value. The effect may differ slightly on different platforms. 1453 * 1454 * @param pri Relative priority for this notification. Must be one of 1455 * the priority constants defined by {@link NotificationCompat}. 1456 * Acceptable values range from {@link 1457 * NotificationCompat#PRIORITY_MIN} (-2) to {@link 1458 * NotificationCompat#PRIORITY_MAX} (2). 1459 */ 1460 public Builder setPriority(int pri) { 1461 mPriority = pri; 1462 return this; 1463 } 1464 1465 /** 1466 * Add a person that is relevant to this notification. 1467 * 1468 * <P> 1469 * Depending on user preferences, this annotation may allow the notification to pass 1470 * through interruption filters, and to appear more prominently in the user interface. 1471 * </P> 1472 * 1473 * <P> 1474 * The person should be specified by the {@code String} representation of a 1475 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}. 1476 * </P> 1477 * 1478 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema 1479 * URIs. The path part of these URIs must exist in the contacts database, in the 1480 * appropriate column, or the reference will be discarded as invalid. Telephone schema 1481 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}. 1482 * </P> 1483 * 1484 * @param uri A URI for the person. 1485 * @see Notification#EXTRA_PEOPLE 1486 */ 1487 public Builder addPerson(String uri) { 1488 mPeople.add(uri); 1489 return this; 1490 } 1491 1492 /** 1493 * Set this notification to be part of a group of notifications sharing the same key. 1494 * Grouped notifications may display in a cluster or stack on devices which 1495 * support such rendering. 1496 * 1497 * <p>To make this notification the summary for its group, also call 1498 * {@link #setGroupSummary}. A sort order can be specified for group members by using 1499 * {@link #setSortKey}. 1500 * @param groupKey The group key of the group. 1501 * @return this object for method chaining 1502 */ 1503 public Builder setGroup(String groupKey) { 1504 mGroupKey = groupKey; 1505 return this; 1506 } 1507 1508 /** 1509 * Set this notification to be the group summary for a group of notifications. 1510 * Grouped notifications may display in a cluster or stack on devices which 1511 * support such rendering. Requires a group key also be set using {@link #setGroup}. 1512 * @param isGroupSummary Whether this notification should be a group summary. 1513 * @return this object for method chaining 1514 */ 1515 public Builder setGroupSummary(boolean isGroupSummary) { 1516 mGroupSummary = isGroupSummary; 1517 return this; 1518 } 1519 1520 /** 1521 * Set a sort key that orders this notification among other notifications from the 1522 * same package. This can be useful if an external sort was already applied and an app 1523 * would like to preserve this. Notifications will be sorted lexicographically using this 1524 * value, although providing different priorities in addition to providing sort key may 1525 * cause this value to be ignored. 1526 * 1527 * <p>This sort key can also be used to order members of a notification group. See 1528 * {@link Builder#setGroup}. 1529 * 1530 * @see String#compareTo(String) 1531 */ 1532 public Builder setSortKey(String sortKey) { 1533 mSortKey = sortKey; 1534 return this; 1535 } 1536 1537 /** 1538 * Merge additional metadata into this notification. 1539 * 1540 * <p>Values within the Bundle will replace existing extras values in this Builder. 1541 * 1542 * @see Notification#extras 1543 */ 1544 public Builder addExtras(Bundle extras) { 1545 if (extras != null) { 1546 if (mExtras == null) { 1547 mExtras = new Bundle(extras); 1548 } else { 1549 mExtras.putAll(extras); 1550 } 1551 } 1552 return this; 1553 } 1554 1555 /** 1556 * Set metadata for this notification. 1557 * 1558 * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's 1559 * current contents are copied into the Notification each time {@link #build()} is 1560 * called. 1561 * 1562 * <p>Replaces any existing extras values with those from the provided Bundle. 1563 * Use {@link #addExtras} to merge in metadata instead. 1564 * 1565 * @see Notification#extras 1566 */ 1567 public Builder setExtras(Bundle extras) { 1568 mExtras = extras; 1569 return this; 1570 } 1571 1572 /** 1573 * Get the current metadata Bundle used by this notification Builder. 1574 * 1575 * <p>The returned Bundle is shared with this Builder. 1576 * 1577 * <p>The current contents of this Bundle are copied into the Notification each time 1578 * {@link #build()} is called. 1579 * 1580 * @see Notification#extras 1581 */ 1582 public Bundle getExtras() { 1583 if (mExtras == null) { 1584 mExtras = new Bundle(); 1585 } 1586 return mExtras; 1587 } 1588 1589 /** 1590 * Add an action to this notification. Actions are typically displayed by 1591 * the system as a button adjacent to the notification content. 1592 * <br> 1593 * Action buttons won't appear on platforms prior to Android 4.1. Action 1594 * buttons depend on expanded notifications, which are only available in Android 4.1 1595 * and later. To ensure that an action button's functionality is always available, first 1596 * implement the functionality in the {@link android.app.Activity} that starts when a user 1597 * clicks the notification (see {@link #setContentIntent setContentIntent()}), and then 1598 * enhance the notification by implementing the same functionality with 1599 * {@link #addAction addAction()}. 1600 * 1601 * @param icon Resource ID of a drawable that represents the action. 1602 * @param title Text describing the action. 1603 * @param intent {@link android.app.PendingIntent} to be fired when the action is invoked. 1604 */ 1605 public Builder addAction(int icon, CharSequence title, PendingIntent intent) { 1606 mActions.add(new Action(icon, title, intent)); 1607 return this; 1608 } 1609 1610 /** 1611 * Add an action to this notification. Actions are typically displayed by 1612 * the system as a button adjacent to the notification content. 1613 * <br> 1614 * Action buttons won't appear on platforms prior to Android 4.1. Action 1615 * buttons depend on expanded notifications, which are only available in Android 4.1 1616 * and later. To ensure that an action button's functionality is always available, first 1617 * implement the functionality in the {@link android.app.Activity} that starts when a user 1618 * clicks the notification (see {@link #setContentIntent setContentIntent()}), and then 1619 * enhance the notification by implementing the same functionality with 1620 * {@link #addAction addAction()}. 1621 * 1622 * @param action The action to add. 1623 */ 1624 public Builder addAction(Action action) { 1625 mActions.add(action); 1626 return this; 1627 } 1628 1629 /** 1630 * Add a rich notification style to be applied at build time. 1631 * <br> 1632 * If the platform does not provide rich notification styles, this method has no effect. The 1633 * user will always see the normal notification style. 1634 * 1635 * @param style Object responsible for modifying the notification style. 1636 */ 1637 public Builder setStyle(Style style) { 1638 if (mStyle != style) { 1639 mStyle = style; 1640 if (mStyle != null) { 1641 mStyle.setBuilder(this); 1642 } 1643 } 1644 return this; 1645 } 1646 1647 /** 1648 * Sets {@link Notification#color}. 1649 * 1650 * @param argb The accent color to use 1651 * 1652 * @return The same Builder. 1653 */ 1654 public Builder setColor(@ColorInt int argb) { 1655 mColor = argb; 1656 return this; 1657 } 1658 1659 /** 1660 * Sets {@link Notification#visibility}. 1661 * 1662 * @param visibility One of {@link Notification#VISIBILITY_PRIVATE} (the default), 1663 * {@link Notification#VISIBILITY_PUBLIC}, or 1664 * {@link Notification#VISIBILITY_SECRET}. 1665 */ 1666 public Builder setVisibility(int visibility) { 1667 mVisibility = visibility; 1668 return this; 1669 } 1670 1671 /** 1672 * Supply a replacement Notification whose contents should be shown in insecure contexts 1673 * (i.e. atop the secure lockscreen). See {@link Notification#visibility} and 1674 * {@link #VISIBILITY_PUBLIC}. 1675 * 1676 * @param n A replacement notification, presumably with some or all info redacted. 1677 * @return The same Builder. 1678 */ 1679 public Builder setPublicVersion(Notification n) { 1680 mPublicVersion = n; 1681 return this; 1682 } 1683 1684 /** 1685 * Supply custom RemoteViews to use instead of the platform template. 1686 * 1687 * This will override the layout that would otherwise be constructed by this Builder 1688 * object. 1689 */ 1690 public Builder setCustomContentView(RemoteViews contentView) { 1691 mContentView = contentView; 1692 return this; 1693 } 1694 1695 /** 1696 * Supply custom RemoteViews to use instead of the platform template in the expanded form. 1697 * 1698 * This will override the expanded layout that would otherwise be constructed by this 1699 * Builder object. 1700 * 1701 * No-op on versions prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN}. 1702 */ 1703 public Builder setCustomBigContentView(RemoteViews contentView) { 1704 mBigContentView = contentView; 1705 return this; 1706 } 1707 1708 /** 1709 * Supply custom RemoteViews to use instead of the platform template in the heads up dialog. 1710 * 1711 * This will override the heads-up layout that would otherwise be constructed by this 1712 * Builder object. 1713 * 1714 * No-op on versions prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}. 1715 */ 1716 public Builder setCustomHeadsUpContentView(RemoteViews contentView) { 1717 mHeadsUpContentView = contentView; 1718 return this; 1719 } 1720 1721 /** 1722 * Apply an extender to this notification builder. Extenders may be used to add 1723 * metadata or change options on this builder. 1724 */ 1725 public Builder extend(Extender extender) { 1726 extender.extend(this); 1727 return this; 1728 } 1729 1730 /** 1731 * @deprecated Use {@link #build()} instead. 1732 */ 1733 @Deprecated 1734 public Notification getNotification() { 1735 return build(); 1736 } 1737 1738 /** 1739 * Combine all of the options that have been set and return a new {@link Notification} 1740 * object. 1741 */ 1742 public Notification build() { 1743 return IMPL.build(this, getExtender()); 1744 } 1745 1746 /** 1747 * @hide 1748 */ 1749 @RestrictTo(GROUP_ID) 1750 protected BuilderExtender getExtender() { 1751 return new BuilderExtender(); 1752 } 1753 1754 protected static CharSequence limitCharSequenceLength(CharSequence cs) { 1755 if (cs == null) return cs; 1756 if (cs.length() > MAX_CHARSEQUENCE_LENGTH) { 1757 cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH); 1758 } 1759 return cs; 1760 } 1761 1762 /** 1763 * @hide 1764 */ 1765 @RestrictTo(GROUP_ID) 1766 public RemoteViews getContentView() { 1767 return mContentView; 1768 } 1769 1770 /** 1771 * @hide 1772 */ 1773 @RestrictTo(GROUP_ID) 1774 public RemoteViews getBigContentView() { 1775 return mBigContentView; 1776 } 1777 1778 /** 1779 * @hide 1780 */ 1781 @RestrictTo(GROUP_ID) 1782 public RemoteViews getHeadsUpContentView() { 1783 return mHeadsUpContentView; 1784 } 1785 1786 /** 1787 * return when if it is showing or 0 otherwise 1788 * 1789 * @hide 1790 */ 1791 @RestrictTo(GROUP_ID) 1792 public long getWhenIfShowing() { 1793 return mShowWhen ? mNotification.when : 0; 1794 } 1795 1796 /** 1797 * @return the priority set on the notification 1798 * 1799 * @hide 1800 */ 1801 @RestrictTo(GROUP_ID) 1802 public int getPriority() { 1803 return mPriority; 1804 } 1805 1806 /** 1807 * @return the color of the notification 1808 * 1809 * @hide 1810 */ 1811 @RestrictTo(GROUP_ID) 1812 public int getColor() { 1813 return mColor; 1814 } 1815 1816 1817 /** 1818 * @return the text of the notification 1819 * 1820 * @hide 1821 */ 1822 @RestrictTo(GROUP_ID) 1823 protected CharSequence resolveText() { 1824 return mContentText; 1825 } 1826 1827 /** 1828 * @return the title of the notification 1829 * 1830 * @hide 1831 */ 1832 @RestrictTo(GROUP_ID) 1833 protected CharSequence resolveTitle() { 1834 return mContentTitle; 1835 } 1836 } 1837 1838 /** 1839 * An object that can apply a rich notification style to a {@link Notification.Builder} 1840 * object. 1841 * <br> 1842 * If the platform does not provide rich notification styles, methods in this class have no 1843 * effect. 1844 */ 1845 public static abstract class Style { 1846 Builder mBuilder; 1847 CharSequence mBigContentTitle; 1848 CharSequence mSummaryText; 1849 boolean mSummaryTextSet = false; 1850 1851 public void setBuilder(Builder builder) { 1852 if (mBuilder != builder) { 1853 mBuilder = builder; 1854 if (mBuilder != null) { 1855 mBuilder.setStyle(this); 1856 } 1857 } 1858 } 1859 1860 public Notification build() { 1861 Notification notification = null; 1862 if (mBuilder != null) { 1863 notification = mBuilder.build(); 1864 } 1865 return notification; 1866 } 1867 1868 /** 1869 * @hide 1870 */ 1871 @RestrictTo(GROUP_ID) 1872 // TODO: implement for all styles 1873 public void addCompatExtras(Bundle extras) { 1874 } 1875 1876 /** 1877 * @hide 1878 */ 1879 @RestrictTo(GROUP_ID) 1880 // TODO: implement for all styles 1881 protected void restoreFromCompatExtras(Bundle extras) { 1882 } 1883 } 1884 1885 /** 1886 * Helper class for generating large-format notifications that include a large image attachment. 1887 * <br> 1888 * If the platform does not provide large-format notifications, this method has no effect. The 1889 * user will always see the normal notification view. 1890 * <br> 1891 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so: 1892 * <pre class="prettyprint"> 1893 * Notification notif = new Notification.Builder(mContext) 1894 * .setContentTitle("New photo from " + sender.toString()) 1895 * .setContentText(subject) 1896 * .setSmallIcon(R.drawable.new_post) 1897 * .setLargeIcon(aBitmap) 1898 * .setStyle(new Notification.BigPictureStyle() 1899 * .bigPicture(aBigBitmap)) 1900 * .build(); 1901 * </pre> 1902 * 1903 * @see Notification#bigContentView 1904 */ 1905 public static class BigPictureStyle extends Style { 1906 Bitmap mPicture; 1907 Bitmap mBigLargeIcon; 1908 boolean mBigLargeIconSet; 1909 1910 public BigPictureStyle() { 1911 } 1912 1913 public BigPictureStyle(Builder builder) { 1914 setBuilder(builder); 1915 } 1916 1917 /** 1918 * Overrides ContentTitle in the big form of the template. 1919 * This defaults to the value passed to setContentTitle(). 1920 */ 1921 public BigPictureStyle setBigContentTitle(CharSequence title) { 1922 mBigContentTitle = Builder.limitCharSequenceLength(title); 1923 return this; 1924 } 1925 1926 /** 1927 * Set the first line of text after the detail section in the big form of the template. 1928 */ 1929 public BigPictureStyle setSummaryText(CharSequence cs) { 1930 mSummaryText = Builder.limitCharSequenceLength(cs); 1931 mSummaryTextSet = true; 1932 return this; 1933 } 1934 1935 /** 1936 * Provide the bitmap to be used as the payload for the BigPicture notification. 1937 */ 1938 public BigPictureStyle bigPicture(Bitmap b) { 1939 mPicture = b; 1940 return this; 1941 } 1942 1943 /** 1944 * Override the large icon when the big notification is shown. 1945 */ 1946 public BigPictureStyle bigLargeIcon(Bitmap b) { 1947 mBigLargeIcon = b; 1948 mBigLargeIconSet = true; 1949 return this; 1950 } 1951 } 1952 1953 /** 1954 * Helper class for generating large-format notifications that include a lot of text. 1955 * 1956 * <br> 1957 * If the platform does not provide large-format notifications, this method has no effect. The 1958 * user will always see the normal notification view. 1959 * <br> 1960 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so: 1961 * <pre class="prettyprint"> 1962 * Notification notif = new Notification.Builder(mContext) 1963 * .setContentTitle("New mail from " + sender.toString()) 1964 * .setContentText(subject) 1965 * .setSmallIcon(R.drawable.new_mail) 1966 * .setLargeIcon(aBitmap) 1967 * .setStyle(new Notification.BigTextStyle() 1968 * .bigText(aVeryLongString)) 1969 * .build(); 1970 * </pre> 1971 * 1972 * @see Notification#bigContentView 1973 */ 1974 public static class BigTextStyle extends Style { 1975 CharSequence mBigText; 1976 1977 public BigTextStyle() { 1978 } 1979 1980 public BigTextStyle(Builder builder) { 1981 setBuilder(builder); 1982 } 1983 1984 /** 1985 * Overrides ContentTitle in the big form of the template. 1986 * This defaults to the value passed to setContentTitle(). 1987 */ 1988 public BigTextStyle setBigContentTitle(CharSequence title) { 1989 mBigContentTitle = Builder.limitCharSequenceLength(title); 1990 return this; 1991 } 1992 1993 /** 1994 * Set the first line of text after the detail section in the big form of the template. 1995 */ 1996 public BigTextStyle setSummaryText(CharSequence cs) { 1997 mSummaryText = Builder.limitCharSequenceLength(cs); 1998 mSummaryTextSet = true; 1999 return this; 2000 } 2001 2002 /** 2003 * Provide the longer text to be displayed in the big form of the 2004 * template in place of the content text. 2005 */ 2006 public BigTextStyle bigText(CharSequence cs) { 2007 mBigText = Builder.limitCharSequenceLength(cs); 2008 return this; 2009 } 2010 } 2011 2012 /** 2013 * Helper class for generating large-format notifications that include multiple back-and-forth 2014 * messages of varying types between any number of people. 2015 * 2016 * <br> 2017 * In order to get a backwards compatible behavior, the app needs to use the v7 version of the 2018 * notification builder together with this style, otherwise the user will see the normal 2019 * notification view. 2020 * <br> 2021 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like 2022 * so: 2023 * <pre class="prettyprint"> 2024 * 2025 * Notification noti = new Notification.Builder() 2026 * .setContentTitle("2 new messages wtih " + sender.toString()) 2027 * .setContentText(subject) 2028 * .setSmallIcon(R.drawable.new_message) 2029 * .setLargeIcon(aBitmap) 2030 * .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name)) 2031 * .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender()) 2032 * .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender())) 2033 * .build(); 2034 * </pre> 2035 */ 2036 public static class MessagingStyle extends Style { 2037 2038 /** 2039 * The maximum number of messages that will be retained in the Notification itself (the 2040 * number displayed is up to the platform). 2041 */ 2042 public static final int MAXIMUM_RETAINED_MESSAGES = 25; 2043 2044 CharSequence mUserDisplayName; 2045 CharSequence mConversationTitle; 2046 List<Message> mMessages = new ArrayList<>(); 2047 2048 MessagingStyle() { 2049 } 2050 2051 /** 2052 * @param userDisplayName the name to be displayed for any replies sent by the user before the 2053 * posting app reposts the notification with those messages after they've been actually 2054 * sent and in previous messages sent by the user added in 2055 * {@link #addMessage(Message)} 2056 */ 2057 public MessagingStyle(CharSequence userDisplayName) { 2058 mUserDisplayName = userDisplayName; 2059 } 2060 2061 /** 2062 * Returns the name to be displayed for any replies sent by the user 2063 */ 2064 public CharSequence getUserDisplayName() { 2065 return mUserDisplayName; 2066 } 2067 2068 /** 2069 * Sets the title to be displayed on this conversation. This should only be used for 2070 * group messaging and left unset for one-on-one conversations. 2071 * @param conversationTitle 2072 * @return this object for method chaining. 2073 */ 2074 public MessagingStyle setConversationTitle(CharSequence conversationTitle) { 2075 mConversationTitle = conversationTitle; 2076 return this; 2077 } 2078 2079 /** 2080 * Return the title to be displayed on this conversation. Can be <code>null</code> and 2081 * should be for one-on-one conversations 2082 */ 2083 public CharSequence getConversationTitle() { 2084 return mConversationTitle; 2085 } 2086 2087 /** 2088 * Adds a message for display by this notification. Convenience call for a simple 2089 * {@link Message} in {@link #addMessage(Message)} 2090 * @param text A {@link CharSequence} to be displayed as the message content 2091 * @param timestamp Time at which the message arrived 2092 * @param sender A {@link CharSequence} to be used for displaying the name of the 2093 * sender. Should be <code>null</code> for messages by the current user, in which case 2094 * the platform will insert {@link #getUserDisplayName()}. 2095 * Should be unique amongst all individuals in the conversation, and should be 2096 * consistent during re-posts of the notification. 2097 * 2098 * @see Message#Message(CharSequence, long, CharSequence) 2099 * 2100 * @return this object for method chaining 2101 */ 2102 public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) { 2103 mMessages.add(new Message(text, timestamp, sender)); 2104 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) { 2105 mMessages.remove(0); 2106 } 2107 return this; 2108 } 2109 2110 /** 2111 * Adds a {@link Message} for display in this notification. 2112 * @param message The {@link Message} to be displayed 2113 * @return this object for method chaining 2114 */ 2115 public MessagingStyle addMessage(Message message) { 2116 mMessages.add(message); 2117 if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) { 2118 mMessages.remove(0); 2119 } 2120 return this; 2121 } 2122 2123 /** 2124 * Gets the list of {@code Message} objects that represent the notification 2125 */ 2126 public List<Message> getMessages() { 2127 return mMessages; 2128 } 2129 2130 /** 2131 * Retrieves a {@link MessagingStyle} from a {@link Notification}, enabling an application 2132 * that has set a {@link MessagingStyle} using {@link NotificationCompat} or 2133 * {@link android.app.Notification.Builder} to send messaging information to another 2134 * application using {@link NotificationCompat}, regardless of the API level of the system. 2135 * Returns {@code null} if there is no {@link MessagingStyle} set. 2136 */ 2137 public static MessagingStyle extractMessagingStyleFromNotification(Notification notif) { 2138 MessagingStyle style; 2139 Bundle extras = IMPL.getExtras(notif); 2140 if (!extras.containsKey(EXTRA_SELF_DISPLAY_NAME)) { 2141 style = null; 2142 } else { 2143 try { 2144 style = new MessagingStyle(); 2145 style.restoreFromCompatExtras(extras); 2146 } catch (ClassCastException e) { 2147 style = null; 2148 } 2149 } 2150 return style; 2151 } 2152 2153 @Override 2154 public void addCompatExtras(Bundle extras) { 2155 super.addCompatExtras(extras); 2156 if (mUserDisplayName != null) { 2157 extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName); 2158 } 2159 if (mConversationTitle != null) { 2160 extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle); 2161 } 2162 if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES, 2163 Message.getBundleArrayForMessages(mMessages)); 2164 } 2165 } 2166 2167 /** 2168 * @hide 2169 */ 2170 @RestrictTo(GROUP_ID) 2171 @Override 2172 protected void restoreFromCompatExtras(Bundle extras) { 2173 mMessages.clear(); 2174 mUserDisplayName = extras.getString(EXTRA_SELF_DISPLAY_NAME); 2175 mConversationTitle = extras.getString(EXTRA_CONVERSATION_TITLE); 2176 Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES); 2177 if (parcelables != null) { 2178 mMessages = Message.getMessagesFromBundleArray(parcelables); 2179 } 2180 } 2181 2182 public static final class Message { 2183 2184 static final String KEY_TEXT = "text"; 2185 static final String KEY_TIMESTAMP = "time"; 2186 static final String KEY_SENDER = "sender"; 2187 static final String KEY_DATA_MIME_TYPE = "type"; 2188 static final String KEY_DATA_URI= "uri"; 2189 2190 private final CharSequence mText; 2191 private final long mTimestamp; 2192 private final CharSequence mSender; 2193 2194 private String mDataMimeType; 2195 private Uri mDataUri; 2196 2197 /** 2198 * Constructor 2199 * @param text A {@link CharSequence} to be displayed as the message content 2200 * @param timestamp Time at which the message arrived 2201 * @param sender A {@link CharSequence} to be used for displaying the name of the 2202 * sender. Should be <code>null</code> for messages by the current user, in which case 2203 * the platform will insert {@link MessagingStyle#getUserDisplayName()}. 2204 * Should be unique amongst all individuals in the conversation, and should be 2205 * consistent during re-posts of the notification. 2206 */ 2207 public Message(CharSequence text, long timestamp, CharSequence sender){ 2208 mText = text; 2209 mTimestamp = timestamp; 2210 mSender = sender; 2211 } 2212 2213 /** 2214 * Sets a binary blob of data and an associated MIME type for a message. In the case 2215 * where the platform doesn't support the MIME type, the original text provided in the 2216 * constructor will be used. 2217 * @param dataMimeType The MIME type of the content. See 2218 * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME 2219 * types on Android and Android Wear. 2220 * @param dataUri The uri containing the content whose type is given by the MIME type. 2221 * <p class="note"> 2222 * <ol> 2223 * <li>Notification Listeners including the System UI need permission to access the 2224 * data the Uri points to. The recommended ways to do this are:</li> 2225 * <li>Store the data in your own ContentProvider, making sure that other apps have 2226 * the correct permission to access your provider. The preferred mechanism for 2227 * providing access is to use per-URI permissions which are temporary and only 2228 * grant access to the receiving application. An easy way to create a 2229 * ContentProvider like this is to use the FileProvider helper class.</li> 2230 * <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio 2231 * and image MIME types, however beginning with Android 3.0 (API level 11) it can 2232 * also store non-media types (see MediaStore.Files for more info). Files can be 2233 * inserted into the MediaStore using scanFile() after which a content:// style 2234 * Uri suitable for sharing is passed to the provided onScanCompleted() callback. 2235 * Note that once added to the system MediaStore the content is accessible to any 2236 * app on the device.</li> 2237 * </ol> 2238 * @return this object for method chaining 2239 */ 2240 public Message setData(String dataMimeType, Uri dataUri) { 2241 mDataMimeType = dataMimeType; 2242 mDataUri = dataUri; 2243 return this; 2244 } 2245 2246 /** 2247 * Get the text to be used for this message, or the fallback text if a type and content 2248 * Uri have been set 2249 */ 2250 public CharSequence getText() { 2251 return mText; 2252 } 2253 2254 /** 2255 * Get the time at which this message arrived 2256 */ 2257 public long getTimestamp() { 2258 return mTimestamp; 2259 } 2260 2261 /** 2262 * Get the text used to display the contact's name in the messaging experience 2263 */ 2264 public CharSequence getSender() { 2265 return mSender; 2266 } 2267 2268 /** 2269 * Get the MIME type of the data pointed to by the Uri 2270 */ 2271 public String getDataMimeType() { 2272 return mDataMimeType; 2273 } 2274 2275 /** 2276 * Get the the Uri pointing to the content of the message. Can be null, in which case 2277 * {@see #getText()} is used. 2278 */ 2279 public Uri getDataUri() { 2280 return mDataUri; 2281 } 2282 2283 private Bundle toBundle() { 2284 Bundle bundle = new Bundle(); 2285 if (mText != null) { 2286 bundle.putCharSequence(KEY_TEXT, mText); 2287 } 2288 bundle.putLong(KEY_TIMESTAMP, mTimestamp); 2289 if (mSender != null) { 2290 bundle.putCharSequence(KEY_SENDER, mSender); 2291 } 2292 if (mDataMimeType != null) { 2293 bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType); 2294 } 2295 if (mDataUri != null) { 2296 bundle.putParcelable(KEY_DATA_URI, mDataUri); 2297 } 2298 return bundle; 2299 } 2300 2301 static Bundle[] getBundleArrayForMessages(List<Message> messages) { 2302 Bundle[] bundles = new Bundle[messages.size()]; 2303 final int N = messages.size(); 2304 for (int i = 0; i < N; i++) { 2305 bundles[i] = messages.get(i).toBundle(); 2306 } 2307 return bundles; 2308 } 2309 2310 static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) { 2311 List<Message> messages = new ArrayList<>(bundles.length); 2312 for (int i = 0; i < bundles.length; i++) { 2313 if (bundles[i] instanceof Bundle) { 2314 Message message = getMessageFromBundle((Bundle)bundles[i]); 2315 if (message != null) { 2316 messages.add(message); 2317 } 2318 } 2319 } 2320 return messages; 2321 } 2322 2323 static Message getMessageFromBundle(Bundle bundle) { 2324 try { 2325 if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) { 2326 return null; 2327 } else { 2328 Message message = new Message(bundle.getCharSequence(KEY_TEXT), 2329 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER)); 2330 if (bundle.containsKey(KEY_DATA_MIME_TYPE) && 2331 bundle.containsKey(KEY_DATA_URI)) { 2332 2333 message.setData(bundle.getString(KEY_DATA_MIME_TYPE), 2334 (Uri) bundle.getParcelable(KEY_DATA_URI)); 2335 } 2336 return message; 2337 } 2338 } catch (ClassCastException e) { 2339 return null; 2340 } 2341 } 2342 } 2343 } 2344 2345 /** 2346 * Helper class for generating large-format notifications that include a list of (up to 5) strings. 2347 * 2348 * <br> 2349 * If the platform does not provide large-format notifications, this method has no effect. The 2350 * user will always see the normal notification view. 2351 * <br> 2352 * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so: 2353 * <pre class="prettyprint"> 2354 * Notification noti = new Notification.Builder() 2355 * .setContentTitle("5 New mails from " + sender.toString()) 2356 * .setContentText(subject) 2357 * .setSmallIcon(R.drawable.new_mail) 2358 * .setLargeIcon(aBitmap) 2359 * .setStyle(new Notification.InboxStyle() 2360 * .addLine(str1) 2361 * .addLine(str2) 2362 * .setContentTitle("") 2363 * .setSummaryText("+3 more")) 2364 * .build(); 2365 * </pre> 2366 * 2367 * @see Notification#bigContentView 2368 */ 2369 public static class InboxStyle extends Style { 2370 ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); 2371 2372 public InboxStyle() { 2373 } 2374 2375 public InboxStyle(Builder builder) { 2376 setBuilder(builder); 2377 } 2378 2379 /** 2380 * Overrides ContentTitle in the big form of the template. 2381 * This defaults to the value passed to setContentTitle(). 2382 */ 2383 public InboxStyle setBigContentTitle(CharSequence title) { 2384 mBigContentTitle = Builder.limitCharSequenceLength(title); 2385 return this; 2386 } 2387 2388 /** 2389 * Set the first line of text after the detail section in the big form of the template. 2390 */ 2391 public InboxStyle setSummaryText(CharSequence cs) { 2392 mSummaryText = Builder.limitCharSequenceLength(cs); 2393 mSummaryTextSet = true; 2394 return this; 2395 } 2396 2397 /** 2398 * Append a line to the digest section of the Inbox notification. 2399 */ 2400 public InboxStyle addLine(CharSequence cs) { 2401 mTexts.add(Builder.limitCharSequenceLength(cs)); 2402 return this; 2403 } 2404 } 2405 2406 /** 2407 * Structure to encapsulate a named action that can be shown as part of this notification. 2408 * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is 2409 * selected by the user. Action buttons won't appear on platforms prior to Android 4.1. 2410 * <p> 2411 * Apps should use {@link NotificationCompat.Builder#addAction(int, CharSequence, PendingIntent)} 2412 * or {@link NotificationCompat.Builder#addAction(NotificationCompat.Action)} 2413 * to attach actions. 2414 */ 2415 public static class Action extends NotificationCompatBase.Action { 2416 final Bundle mExtras; 2417 private final RemoteInput[] mRemoteInputs; 2418 private boolean mAllowGeneratedReplies = false; 2419 2420 /** 2421 * Small icon representing the action. 2422 */ 2423 public int icon; 2424 /** 2425 * Title of the action. 2426 */ 2427 public CharSequence title; 2428 /** 2429 * Intent to send when the user invokes this action. May be null, in which case the action 2430 * may be rendered in a disabled presentation. 2431 */ 2432 public PendingIntent actionIntent; 2433 2434 public Action(int icon, CharSequence title, PendingIntent intent) { 2435 this(icon, title, intent, new Bundle(), null, false); 2436 } 2437 2438 Action(int icon, CharSequence title, PendingIntent intent, Bundle extras, 2439 RemoteInput[] remoteInputs, boolean allowGeneratedReplies) { 2440 this.icon = icon; 2441 this.title = NotificationCompat.Builder.limitCharSequenceLength(title); 2442 this.actionIntent = intent; 2443 this.mExtras = extras != null ? extras : new Bundle(); 2444 this.mRemoteInputs = remoteInputs; 2445 this.mAllowGeneratedReplies = allowGeneratedReplies; 2446 } 2447 2448 @Override 2449 public int getIcon() { 2450 return icon; 2451 } 2452 2453 @Override 2454 public CharSequence getTitle() { 2455 return title; 2456 } 2457 2458 @Override 2459 public PendingIntent getActionIntent() { 2460 return actionIntent; 2461 } 2462 2463 /** 2464 * Get additional metadata carried around with this Action. 2465 */ 2466 @Override 2467 public Bundle getExtras() { 2468 return mExtras; 2469 } 2470 2471 /** 2472 * Return whether the platform should automatically generate possible replies for this 2473 * {@link Action} 2474 */ 2475 @Override 2476 public boolean getAllowGeneratedReplies() { 2477 return mAllowGeneratedReplies; 2478 } 2479 2480 /** 2481 * Get the list of inputs to be collected from the user when this action is sent. 2482 * May return null if no remote inputs were added. 2483 */ 2484 @Override 2485 public RemoteInput[] getRemoteInputs() { 2486 return mRemoteInputs; 2487 } 2488 2489 /** 2490 * Builder class for {@link Action} objects. 2491 */ 2492 public static final class Builder { 2493 private final int mIcon; 2494 private final CharSequence mTitle; 2495 private final PendingIntent mIntent; 2496 private boolean mAllowGeneratedReplies; 2497 private final Bundle mExtras; 2498 private ArrayList<RemoteInput> mRemoteInputs; 2499 2500 /** 2501 * Construct a new builder for {@link Action} object. 2502 * @param icon icon to show for this action 2503 * @param title the title of the action 2504 * @param intent the {@link PendingIntent} to fire when users trigger this action 2505 */ 2506 public Builder(int icon, CharSequence title, PendingIntent intent) { 2507 this(icon, title, intent, new Bundle()); 2508 } 2509 2510 /** 2511 * Construct a new builder for {@link Action} object using the fields from an 2512 * {@link Action}. 2513 * @param action the action to read fields from. 2514 */ 2515 public Builder(Action action) { 2516 this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras)); 2517 } 2518 2519 private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) { 2520 mIcon = icon; 2521 mTitle = NotificationCompat.Builder.limitCharSequenceLength(title); 2522 mIntent = intent; 2523 mExtras = extras; 2524 } 2525 2526 /** 2527 * Merge additional metadata into this builder. 2528 * 2529 * <p>Values within the Bundle will replace existing extras values in this Builder. 2530 * 2531 * @see NotificationCompat.Action#getExtras 2532 */ 2533 public Builder addExtras(Bundle extras) { 2534 if (extras != null) { 2535 mExtras.putAll(extras); 2536 } 2537 return this; 2538 } 2539 2540 /** 2541 * Get the metadata Bundle used by this Builder. 2542 * 2543 * <p>The returned Bundle is shared with this Builder. 2544 */ 2545 public Bundle getExtras() { 2546 return mExtras; 2547 } 2548 2549 /** 2550 * Add an input to be collected from the user when this action is sent. 2551 * Response values can be retrieved from the fired intent by using the 2552 * {@link RemoteInput#getResultsFromIntent} function. 2553 * @param remoteInput a {@link RemoteInput} to add to the action 2554 * @return this object for method chaining 2555 */ 2556 public Builder addRemoteInput(RemoteInput remoteInput) { 2557 if (mRemoteInputs == null) { 2558 mRemoteInputs = new ArrayList<RemoteInput>(); 2559 } 2560 mRemoteInputs.add(remoteInput); 2561 return this; 2562 } 2563 2564 /** 2565 * Set whether the platform should automatically generate possible replies to add to 2566 * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a 2567 * {@link RemoteInput}, this has no effect. 2568 * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false} 2569 * otherwise 2570 * @return this object for method chaining 2571 * The default value is {@code false} 2572 */ 2573 public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) { 2574 mAllowGeneratedReplies = allowGeneratedReplies; 2575 return this; 2576 } 2577 2578 /** 2579 * Apply an extender to this action builder. Extenders may be used to add 2580 * metadata or change options on this builder. 2581 */ 2582 public Builder extend(Extender extender) { 2583 extender.extend(this); 2584 return this; 2585 } 2586 2587 /** 2588 * Combine all of the options that have been set and return a new {@link Action} 2589 * object. 2590 * @return the built action 2591 */ 2592 public Action build() { 2593 RemoteInput[] remoteInputs = mRemoteInputs != null 2594 ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null; 2595 return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs, 2596 mAllowGeneratedReplies); 2597 } 2598 } 2599 2600 2601 /** 2602 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add 2603 * metadata or change options on an action builder. 2604 */ 2605 public interface Extender { 2606 /** 2607 * Apply this extender to a notification action builder. 2608 * @param builder the builder to be modified. 2609 * @return the build object for chaining. 2610 */ 2611 public Builder extend(Builder builder); 2612 } 2613 2614 /** 2615 * Wearable extender for notification actions. To add extensions to an action, 2616 * create a new {@link NotificationCompat.Action.WearableExtender} object using 2617 * the {@code WearableExtender()} constructor and apply it to a 2618 * {@link NotificationCompat.Action.Builder} using 2619 * {@link NotificationCompat.Action.Builder#extend}. 2620 * 2621 * <pre class="prettyprint"> 2622 * NotificationCompat.Action action = new NotificationCompat.Action.Builder( 2623 * R.drawable.archive_all, "Archive all", actionIntent) 2624 * .extend(new NotificationCompat.Action.WearableExtender() 2625 * .setAvailableOffline(false)) 2626 * .build();</pre> 2627 */ 2628 public static final class WearableExtender implements Extender { 2629 /** Notification action extra which contains wearable extensions */ 2630 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; 2631 2632 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options. 2633 private static final String KEY_FLAGS = "flags"; 2634 private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel"; 2635 private static final String KEY_CONFIRM_LABEL = "confirmLabel"; 2636 private static final String KEY_CANCEL_LABEL = "cancelLabel"; 2637 2638 // Flags bitwise-ored to mFlags 2639 private static final int FLAG_AVAILABLE_OFFLINE = 0x1; 2640 private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1; 2641 private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2; 2642 2643 // Default value for flags integer 2644 private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE; 2645 2646 private int mFlags = DEFAULT_FLAGS; 2647 2648 private CharSequence mInProgressLabel; 2649 private CharSequence mConfirmLabel; 2650 private CharSequence mCancelLabel; 2651 2652 /** 2653 * Create a {@link NotificationCompat.Action.WearableExtender} with default 2654 * options. 2655 */ 2656 public WearableExtender() { 2657 } 2658 2659 /** 2660 * Create a {@link NotificationCompat.Action.WearableExtender} by reading 2661 * wearable options present in an existing notification action. 2662 * @param action the notification action to inspect. 2663 */ 2664 public WearableExtender(Action action) { 2665 Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS); 2666 if (wearableBundle != null) { 2667 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS); 2668 mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL); 2669 mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL); 2670 mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL); 2671 } 2672 } 2673 2674 /** 2675 * Apply wearable extensions to a notification action that is being built. This is 2676 * typically called by the {@link NotificationCompat.Action.Builder#extend} 2677 * method of {@link NotificationCompat.Action.Builder}. 2678 */ 2679 @Override 2680 public Action.Builder extend(Action.Builder builder) { 2681 Bundle wearableBundle = new Bundle(); 2682 2683 if (mFlags != DEFAULT_FLAGS) { 2684 wearableBundle.putInt(KEY_FLAGS, mFlags); 2685 } 2686 if (mInProgressLabel != null) { 2687 wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel); 2688 } 2689 if (mConfirmLabel != null) { 2690 wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel); 2691 } 2692 if (mCancelLabel != null) { 2693 wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel); 2694 } 2695 2696 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); 2697 return builder; 2698 } 2699 2700 @Override 2701 public WearableExtender clone() { 2702 WearableExtender that = new WearableExtender(); 2703 that.mFlags = this.mFlags; 2704 that.mInProgressLabel = this.mInProgressLabel; 2705 that.mConfirmLabel = this.mConfirmLabel; 2706 that.mCancelLabel = this.mCancelLabel; 2707 return that; 2708 } 2709 2710 /** 2711 * Set whether this action is available when the wearable device is not connected to 2712 * a companion device. The user can still trigger this action when the wearable device 2713 * is offline, but a visual hint will indicate that the action may not be available. 2714 * Defaults to true. 2715 */ 2716 public WearableExtender setAvailableOffline(boolean availableOffline) { 2717 setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline); 2718 return this; 2719 } 2720 2721 /** 2722 * Get whether this action is available when the wearable device is not connected to 2723 * a companion device. The user can still trigger this action when the wearable device 2724 * is offline, but a visual hint will indicate that the action may not be available. 2725 * Defaults to true. 2726 */ 2727 public boolean isAvailableOffline() { 2728 return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0; 2729 } 2730 2731 private void setFlag(int mask, boolean value) { 2732 if (value) { 2733 mFlags |= mask; 2734 } else { 2735 mFlags &= ~mask; 2736 } 2737 } 2738 2739 /** 2740 * Set a label to display while the wearable is preparing to automatically execute the 2741 * action. This is usually a 'ing' verb ending in ellipsis like "Sending..." 2742 * 2743 * @param label the label to display while the action is being prepared to execute 2744 * @return this object for method chaining 2745 */ 2746 public WearableExtender setInProgressLabel(CharSequence label) { 2747 mInProgressLabel = label; 2748 return this; 2749 } 2750 2751 /** 2752 * Get the label to display while the wearable is preparing to automatically execute 2753 * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..." 2754 * 2755 * @return the label to display while the action is being prepared to execute 2756 */ 2757 public CharSequence getInProgressLabel() { 2758 return mInProgressLabel; 2759 } 2760 2761 /** 2762 * Set a label to display to confirm that the action should be executed. 2763 * This is usually an imperative verb like "Send". 2764 * 2765 * @param label the label to confirm the action should be executed 2766 * @return this object for method chaining 2767 */ 2768 public WearableExtender setConfirmLabel(CharSequence label) { 2769 mConfirmLabel = label; 2770 return this; 2771 } 2772 2773 /** 2774 * Get the label to display to confirm that the action should be executed. 2775 * This is usually an imperative verb like "Send". 2776 * 2777 * @return the label to confirm the action should be executed 2778 */ 2779 public CharSequence getConfirmLabel() { 2780 return mConfirmLabel; 2781 } 2782 2783 /** 2784 * Set a label to display to cancel the action. 2785 * This is usually an imperative verb, like "Cancel". 2786 * 2787 * @param label the label to display to cancel the action 2788 * @return this object for method chaining 2789 */ 2790 public WearableExtender setCancelLabel(CharSequence label) { 2791 mCancelLabel = label; 2792 return this; 2793 } 2794 2795 /** 2796 * Get the label to display to cancel the action. 2797 * This is usually an imperative verb like "Cancel". 2798 * 2799 * @return the label to display to cancel the action 2800 */ 2801 public CharSequence getCancelLabel() { 2802 return mCancelLabel; 2803 } 2804 2805 /** 2806 * Set a hint that this Action will launch an {@link Activity} directly, telling the 2807 * platform that it can generate the appropriate transitions. 2808 * @param hintLaunchesActivity {@code true} if the content intent will launch 2809 * an activity and transitions should be generated, false otherwise. 2810 * @return this object for method chaining 2811 */ 2812 public WearableExtender setHintLaunchesActivity( 2813 boolean hintLaunchesActivity) { 2814 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity); 2815 return this; 2816 } 2817 2818 /** 2819 * Get a hint that this Action will launch an {@link Activity} directly, telling the 2820 * platform that it can generate the appropriate transitions 2821 * @return {@code true} if the content intent will launch an activity and transitions 2822 * should be generated, false otherwise. The default value is {@code false} if this was 2823 * never set. 2824 */ 2825 public boolean getHintLaunchesActivity() { 2826 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0; 2827 } 2828 2829 /** 2830 * Set a hint that this Action should be displayed inline - i.e. it will have a visual 2831 * representation directly on the notification surface in addition to the expanded 2832 * Notification 2833 * 2834 * @param hintDisplayInline {@code true} if action should be displayed inline, false 2835 * otherwise 2836 * @return this object for method chaining 2837 */ 2838 public WearableExtender setHintDisplayActionInline( 2839 boolean hintDisplayInline) { 2840 setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline); 2841 return this; 2842 } 2843 2844 /** 2845 * Get a hint that this Action should be displayed inline - i.e. it should have a 2846 * visual representation directly on the notification surface in addition to the 2847 * expanded Notification 2848 * 2849 * @return {@code true} if the Action should be displayed inline, {@code false} 2850 * otherwise. The default value is {@code false} if this was never set. 2851 */ 2852 public boolean getHintDisplayActionInline() { 2853 return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0; 2854 } 2855 } 2856 2857 /** @hide */ 2858 @RestrictTo(GROUP_ID) 2859 public static final Factory FACTORY = new Factory() { 2860 @Override 2861 public NotificationCompatBase.Action build(int icon, CharSequence title, 2862 PendingIntent actionIntent, Bundle extras, 2863 RemoteInputCompatBase.RemoteInput[] remoteInputs, 2864 boolean allowGeneratedReplies) { 2865 return new Action(icon, title, actionIntent, extras, 2866 (RemoteInput[]) remoteInputs, allowGeneratedReplies); 2867 } 2868 2869 @Override 2870 public Action[] newArray(int length) { 2871 return new Action[length]; 2872 } 2873 }; 2874 } 2875 2876 2877 /** 2878 * Extender interface for use with {@link Builder#extend}. Extenders may be used to add 2879 * metadata or change options on a notification builder. 2880 */ 2881 public interface Extender { 2882 /** 2883 * Apply this extender to a notification builder. 2884 * @param builder the builder to be modified. 2885 * @return the build object for chaining. 2886 */ 2887 public Builder extend(Builder builder); 2888 } 2889 2890 /** 2891 * Helper class to add wearable extensions to notifications. 2892 * <p class="note"> See 2893 * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications 2894 * for Android Wear</a> for more information on how to use this class. 2895 * <p> 2896 * To create a notification with wearable extensions: 2897 * <ol> 2898 * <li>Create a {@link NotificationCompat.Builder}, setting any desired 2899 * properties. 2900 * <li>Create a {@link NotificationCompat.WearableExtender}. 2901 * <li>Set wearable-specific properties using the 2902 * {@code add} and {@code set} methods of {@link NotificationCompat.WearableExtender}. 2903 * <li>Call {@link NotificationCompat.Builder#extend} to apply the extensions to a 2904 * notification. 2905 * <li>Post the notification to the notification 2906 * system with the {@code NotificationManagerCompat.notify(...)} methods 2907 * and not the {@code NotificationManager.notify(...)} methods. 2908 * </ol> 2909 * 2910 * <pre class="prettyprint"> 2911 * Notification notif = new NotificationCompat.Builder(mContext) 2912 * .setContentTitle("New mail from " + sender.toString()) 2913 * .setContentText(subject) 2914 * .setSmallIcon(R.drawable.new_mail) 2915 * .extend(new NotificationCompat.WearableExtender() 2916 * .setContentIcon(R.drawable.new_mail)) 2917 * .build(); 2918 * NotificationManagerCompat.from(mContext).notify(0, notif);</pre> 2919 * 2920 * <p>Wearable extensions can be accessed on an existing notification by using the 2921 * {@code WearableExtender(Notification)} constructor, 2922 * and then using the {@code get} methods to access values. 2923 * 2924 * <pre class="prettyprint"> 2925 * NotificationCompat.WearableExtender wearableExtender = 2926 * new NotificationCompat.WearableExtender(notification); 2927 * List<Notification> pages = wearableExtender.getPages();</pre> 2928 */ 2929 public static final class WearableExtender implements Extender { 2930 /** 2931 * Sentinel value for an action index that is unset. 2932 */ 2933 public static final int UNSET_ACTION_INDEX = -1; 2934 2935 /** 2936 * Size value for use with {@link #setCustomSizePreset} to show this notification with 2937 * default sizing. 2938 * <p>For custom display notifications created using {@link #setDisplayIntent}, 2939 * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based 2940 * on their content. 2941 */ 2942 public static final int SIZE_DEFAULT = 0; 2943 2944 /** 2945 * Size value for use with {@link #setCustomSizePreset} to show this notification 2946 * with an extra small size. 2947 * <p>This value is only applicable for custom display notifications created using 2948 * {@link #setDisplayIntent}. 2949 */ 2950 public static final int SIZE_XSMALL = 1; 2951 2952 /** 2953 * Size value for use with {@link #setCustomSizePreset} to show this notification 2954 * with a small size. 2955 * <p>This value is only applicable for custom display notifications created using 2956 * {@link #setDisplayIntent}. 2957 */ 2958 public static final int SIZE_SMALL = 2; 2959 2960 /** 2961 * Size value for use with {@link #setCustomSizePreset} to show this notification 2962 * with a medium size. 2963 * <p>This value is only applicable for custom display notifications created using 2964 * {@link #setDisplayIntent}. 2965 */ 2966 public static final int SIZE_MEDIUM = 3; 2967 2968 /** 2969 * Size value for use with {@link #setCustomSizePreset} to show this notification 2970 * with a large size. 2971 * <p>This value is only applicable for custom display notifications created using 2972 * {@link #setDisplayIntent}. 2973 */ 2974 public static final int SIZE_LARGE = 4; 2975 2976 /** 2977 * Size value for use with {@link #setCustomSizePreset} to show this notification 2978 * full screen. 2979 * <p>This value is only applicable for custom display notifications created using 2980 * {@link #setDisplayIntent}. 2981 */ 2982 public static final int SIZE_FULL_SCREEN = 5; 2983 2984 /** 2985 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a 2986 * short amount of time when this notification is displayed on the screen. This 2987 * is the default value. 2988 */ 2989 public static final int SCREEN_TIMEOUT_SHORT = 0; 2990 2991 /** 2992 * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on 2993 * for a longer amount of time when this notification is displayed on the screen. 2994 */ 2995 public static final int SCREEN_TIMEOUT_LONG = -1; 2996 2997 /** Notification extra which contains wearable extensions */ 2998 private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; 2999 3000 // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options. 3001 private static final String KEY_ACTIONS = "actions"; 3002 private static final String KEY_FLAGS = "flags"; 3003 private static final String KEY_DISPLAY_INTENT = "displayIntent"; 3004 private static final String KEY_PAGES = "pages"; 3005 private static final String KEY_BACKGROUND = "background"; 3006 private static final String KEY_CONTENT_ICON = "contentIcon"; 3007 private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity"; 3008 private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex"; 3009 private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset"; 3010 private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight"; 3011 private static final String KEY_GRAVITY = "gravity"; 3012 private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout"; 3013 private static final String KEY_DISMISSAL_ID = "dismissalId"; 3014 3015 // Flags bitwise-ored to mFlags 3016 private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1; 3017 private static final int FLAG_HINT_HIDE_ICON = 1 << 1; 3018 private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2; 3019 private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3; 3020 private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4; 3021 private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5; 3022 private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6; 3023 3024 // Default value for flags integer 3025 private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE; 3026 3027 private static final int DEFAULT_CONTENT_ICON_GRAVITY = GravityCompat.END; 3028 private static final int DEFAULT_GRAVITY = Gravity.BOTTOM; 3029 3030 private ArrayList<Action> mActions = new ArrayList<Action>(); 3031 private int mFlags = DEFAULT_FLAGS; 3032 private PendingIntent mDisplayIntent; 3033 private ArrayList<Notification> mPages = new ArrayList<Notification>(); 3034 private Bitmap mBackground; 3035 private int mContentIcon; 3036 private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY; 3037 private int mContentActionIndex = UNSET_ACTION_INDEX; 3038 private int mCustomSizePreset = SIZE_DEFAULT; 3039 private int mCustomContentHeight; 3040 private int mGravity = DEFAULT_GRAVITY; 3041 private int mHintScreenTimeout; 3042 private String mDismissalId; 3043 3044 /** 3045 * Create a {@link NotificationCompat.WearableExtender} with default 3046 * options. 3047 */ 3048 public WearableExtender() { 3049 } 3050 3051 public WearableExtender(Notification notif) { 3052 Bundle extras = getExtras(notif); 3053 Bundle wearableBundle = extras != null ? extras.getBundle(EXTRA_WEARABLE_EXTENSIONS) 3054 : null; 3055 if (wearableBundle != null) { 3056 Action[] actions = IMPL.getActionsFromParcelableArrayList( 3057 wearableBundle.getParcelableArrayList(KEY_ACTIONS)); 3058 if (actions != null) { 3059 Collections.addAll(mActions, actions); 3060 } 3061 3062 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS); 3063 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT); 3064 3065 Notification[] pages = getNotificationArrayFromBundle( 3066 wearableBundle, KEY_PAGES); 3067 if (pages != null) { 3068 Collections.addAll(mPages, pages); 3069 } 3070 3071 mBackground = wearableBundle.getParcelable(KEY_BACKGROUND); 3072 mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON); 3073 mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY, 3074 DEFAULT_CONTENT_ICON_GRAVITY); 3075 mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX, 3076 UNSET_ACTION_INDEX); 3077 mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET, 3078 SIZE_DEFAULT); 3079 mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT); 3080 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY); 3081 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT); 3082 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID); 3083 } 3084 } 3085 3086 /** 3087 * Apply wearable extensions to a notification that is being built. This is typically 3088 * called by the {@link NotificationCompat.Builder#extend} method of 3089 * {@link NotificationCompat.Builder}. 3090 */ 3091 @Override 3092 public NotificationCompat.Builder extend(NotificationCompat.Builder builder) { 3093 Bundle wearableBundle = new Bundle(); 3094 3095 if (!mActions.isEmpty()) { 3096 wearableBundle.putParcelableArrayList(KEY_ACTIONS, 3097 IMPL.getParcelableArrayListForActions(mActions.toArray( 3098 new Action[mActions.size()]))); 3099 } 3100 if (mFlags != DEFAULT_FLAGS) { 3101 wearableBundle.putInt(KEY_FLAGS, mFlags); 3102 } 3103 if (mDisplayIntent != null) { 3104 wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent); 3105 } 3106 if (!mPages.isEmpty()) { 3107 wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray( 3108 new Notification[mPages.size()])); 3109 } 3110 if (mBackground != null) { 3111 wearableBundle.putParcelable(KEY_BACKGROUND, mBackground); 3112 } 3113 if (mContentIcon != 0) { 3114 wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon); 3115 } 3116 if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) { 3117 wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity); 3118 } 3119 if (mContentActionIndex != UNSET_ACTION_INDEX) { 3120 wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX, 3121 mContentActionIndex); 3122 } 3123 if (mCustomSizePreset != SIZE_DEFAULT) { 3124 wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset); 3125 } 3126 if (mCustomContentHeight != 0) { 3127 wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight); 3128 } 3129 if (mGravity != DEFAULT_GRAVITY) { 3130 wearableBundle.putInt(KEY_GRAVITY, mGravity); 3131 } 3132 if (mHintScreenTimeout != 0) { 3133 wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout); 3134 } 3135 if (mDismissalId != null) { 3136 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId); 3137 } 3138 3139 builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); 3140 return builder; 3141 } 3142 3143 @Override 3144 public WearableExtender clone() { 3145 WearableExtender that = new WearableExtender(); 3146 that.mActions = new ArrayList<Action>(this.mActions); 3147 that.mFlags = this.mFlags; 3148 that.mDisplayIntent = this.mDisplayIntent; 3149 that.mPages = new ArrayList<Notification>(this.mPages); 3150 that.mBackground = this.mBackground; 3151 that.mContentIcon = this.mContentIcon; 3152 that.mContentIconGravity = this.mContentIconGravity; 3153 that.mContentActionIndex = this.mContentActionIndex; 3154 that.mCustomSizePreset = this.mCustomSizePreset; 3155 that.mCustomContentHeight = this.mCustomContentHeight; 3156 that.mGravity = this.mGravity; 3157 that.mHintScreenTimeout = this.mHintScreenTimeout; 3158 that.mDismissalId = this.mDismissalId; 3159 return that; 3160 } 3161 3162 /** 3163 * Add a wearable action to this notification. 3164 * 3165 * <p>When wearable actions are added using this method, the set of actions that 3166 * show on a wearable device splits from devices that only show actions added 3167 * using {@link NotificationCompat.Builder#addAction}. This allows for customization 3168 * of which actions display on different devices. 3169 * 3170 * @param action the action to add to this notification 3171 * @return this object for method chaining 3172 * @see NotificationCompat.Action 3173 */ 3174 public WearableExtender addAction(Action action) { 3175 mActions.add(action); 3176 return this; 3177 } 3178 3179 /** 3180 * Adds wearable actions to this notification. 3181 * 3182 * <p>When wearable actions are added using this method, the set of actions that 3183 * show on a wearable device splits from devices that only show actions added 3184 * using {@link NotificationCompat.Builder#addAction}. This allows for customization 3185 * of which actions display on different devices. 3186 * 3187 * @param actions the actions to add to this notification 3188 * @return this object for method chaining 3189 * @see NotificationCompat.Action 3190 */ 3191 public WearableExtender addActions(List<Action> actions) { 3192 mActions.addAll(actions); 3193 return this; 3194 } 3195 3196 /** 3197 * Clear all wearable actions present on this builder. 3198 * @return this object for method chaining. 3199 * @see #addAction 3200 */ 3201 public WearableExtender clearActions() { 3202 mActions.clear(); 3203 return this; 3204 } 3205 3206 /** 3207 * Get the wearable actions present on this notification. 3208 */ 3209 public List<Action> getActions() { 3210 return mActions; 3211 } 3212 3213 /** 3214 * Set an intent to launch inside of an activity view when displaying 3215 * this notification. The {@link PendingIntent} provided should be for an activity. 3216 * 3217 * <pre class="prettyprint"> 3218 * Intent displayIntent = new Intent(context, MyDisplayActivity.class); 3219 * PendingIntent displayPendingIntent = PendingIntent.getActivity(context, 3220 * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT); 3221 * Notification notif = new NotificationCompat.Builder(context) 3222 * .extend(new NotificationCompat.WearableExtender() 3223 * .setDisplayIntent(displayPendingIntent) 3224 * .setCustomSizePreset(NotificationCompat.WearableExtender.SIZE_MEDIUM)) 3225 * .build();</pre> 3226 * 3227 * <p>The activity to launch needs to allow embedding, must be exported, and 3228 * should have an empty task affinity. It is also recommended to use the device 3229 * default light theme. 3230 * 3231 * <p>Example AndroidManifest.xml entry: 3232 * <pre class="prettyprint"> 3233 * <activity android:name="com.example.MyDisplayActivity" 3234 * android:exported="true" 3235 * android:allowEmbedded="true" 3236 * android:taskAffinity="" 3237 * android:theme="@android:style/Theme.DeviceDefault.Light" /></pre> 3238 * 3239 * @param intent the {@link PendingIntent} for an activity 3240 * @return this object for method chaining 3241 * @see NotificationCompat.WearableExtender#getDisplayIntent 3242 */ 3243 public WearableExtender setDisplayIntent(PendingIntent intent) { 3244 mDisplayIntent = intent; 3245 return this; 3246 } 3247 3248 /** 3249 * Get the intent to launch inside of an activity view when displaying this 3250 * notification. This {@code PendingIntent} should be for an activity. 3251 */ 3252 public PendingIntent getDisplayIntent() { 3253 return mDisplayIntent; 3254 } 3255 3256 /** 3257 * Add an additional page of content to display with this notification. The current 3258 * notification forms the first page, and pages added using this function form 3259 * subsequent pages. This field can be used to separate a notification into multiple 3260 * sections. 3261 * 3262 * @param page the notification to add as another page 3263 * @return this object for method chaining 3264 * @see NotificationCompat.WearableExtender#getPages 3265 */ 3266 public WearableExtender addPage(Notification page) { 3267 mPages.add(page); 3268 return this; 3269 } 3270 3271 /** 3272 * Add additional pages of content to display with this notification. The current 3273 * notification forms the first page, and pages added using this function form 3274 * subsequent pages. This field can be used to separate a notification into multiple 3275 * sections. 3276 * 3277 * @param pages a list of notifications 3278 * @return this object for method chaining 3279 * @see NotificationCompat.WearableExtender#getPages 3280 */ 3281 public WearableExtender addPages(List<Notification> pages) { 3282 mPages.addAll(pages); 3283 return this; 3284 } 3285 3286 /** 3287 * Clear all additional pages present on this builder. 3288 * @return this object for method chaining. 3289 * @see #addPage 3290 */ 3291 public WearableExtender clearPages() { 3292 mPages.clear(); 3293 return this; 3294 } 3295 3296 /** 3297 * Get the array of additional pages of content for displaying this notification. The 3298 * current notification forms the first page, and elements within this array form 3299 * subsequent pages. This field can be used to separate a notification into multiple 3300 * sections. 3301 * @return the pages for this notification 3302 */ 3303 public List<Notification> getPages() { 3304 return mPages; 3305 } 3306 3307 /** 3308 * Set a background image to be displayed behind the notification content. 3309 * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background 3310 * will work with any notification style. 3311 * 3312 * @param background the background bitmap 3313 * @return this object for method chaining 3314 * @see NotificationCompat.WearableExtender#getBackground 3315 */ 3316 public WearableExtender setBackground(Bitmap background) { 3317 mBackground = background; 3318 return this; 3319 } 3320 3321 /** 3322 * Get a background image to be displayed behind the notification content. 3323 * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background 3324 * will work with any notification style. 3325 * 3326 * @return the background image 3327 * @see NotificationCompat.WearableExtender#setBackground 3328 */ 3329 public Bitmap getBackground() { 3330 return mBackground; 3331 } 3332 3333 /** 3334 * Set an icon that goes with the content of this notification. 3335 */ 3336 public WearableExtender setContentIcon(int icon) { 3337 mContentIcon = icon; 3338 return this; 3339 } 3340 3341 /** 3342 * Get an icon that goes with the content of this notification. 3343 */ 3344 public int getContentIcon() { 3345 return mContentIcon; 3346 } 3347 3348 /** 3349 * Set the gravity that the content icon should have within the notification display. 3350 * Supported values include {@link android.view.Gravity#START} and 3351 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}. 3352 * @see #setContentIcon 3353 */ 3354 public WearableExtender setContentIconGravity(int contentIconGravity) { 3355 mContentIconGravity = contentIconGravity; 3356 return this; 3357 } 3358 3359 /** 3360 * Get the gravity that the content icon should have within the notification display. 3361 * Supported values include {@link android.view.Gravity#START} and 3362 * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}. 3363 * @see #getContentIcon 3364 */ 3365 public int getContentIconGravity() { 3366 return mContentIconGravity; 3367 } 3368 3369 /** 3370 * Set an action from this notification's actions to be clickable with the content of 3371 * this notification. This action will no longer display separately from the 3372 * notification's content. 3373 * 3374 * <p>For notifications with multiple pages, child pages can also have content actions 3375 * set, although the list of available actions comes from the main notification and not 3376 * from the child page's notification. 3377 * 3378 * @param actionIndex The index of the action to hoist onto the current notification page. 3379 * If wearable actions were added to the main notification, this index 3380 * will apply to that list, otherwise it will apply to the regular 3381 * actions list. 3382 */ 3383 public WearableExtender setContentAction(int actionIndex) { 3384 mContentActionIndex = actionIndex; 3385 return this; 3386 } 3387 3388 /** 3389 * Get the index of the notification action, if any, that was specified as being clickable 3390 * with the content of this notification. This action will no longer display separately 3391 * from the notification's content. 3392 * 3393 * <p>For notifications with multiple pages, child pages can also have content actions 3394 * set, although the list of available actions comes from the main notification and not 3395 * from the child page's notification. 3396 * 3397 * <p>If wearable specific actions were added to the main notification, this index will 3398 * apply to that list, otherwise it will apply to the regular actions list. 3399 * 3400 * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected. 3401 */ 3402 public int getContentAction() { 3403 return mContentActionIndex; 3404 } 3405 3406 /** 3407 * Set the gravity that this notification should have within the available viewport space. 3408 * Supported values include {@link android.view.Gravity#TOP}, 3409 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}. 3410 * The default value is {@link android.view.Gravity#BOTTOM}. 3411 */ 3412 public WearableExtender setGravity(int gravity) { 3413 mGravity = gravity; 3414 return this; 3415 } 3416 3417 /** 3418 * Get the gravity that this notification should have within the available viewport space. 3419 * Supported values include {@link android.view.Gravity#TOP}, 3420 * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}. 3421 * The default value is {@link android.view.Gravity#BOTTOM}. 3422 */ 3423 public int getGravity() { 3424 return mGravity; 3425 } 3426 3427 /** 3428 * Set the custom size preset for the display of this notification out of the available 3429 * presets found in {@link NotificationCompat.WearableExtender}, e.g. 3430 * {@link #SIZE_LARGE}. 3431 * <p>Some custom size presets are only applicable for custom display notifications created 3432 * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. Check the 3433 * documentation for the preset in question. See also 3434 * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}. 3435 */ 3436 public WearableExtender setCustomSizePreset(int sizePreset) { 3437 mCustomSizePreset = sizePreset; 3438 return this; 3439 } 3440 3441 /** 3442 * Get the custom size preset for the display of this notification out of the available 3443 * presets found in {@link NotificationCompat.WearableExtender}, e.g. 3444 * {@link #SIZE_LARGE}. 3445 * <p>Some custom size presets are only applicable for custom display notifications created 3446 * using {@link #setDisplayIntent}. Check the documentation for the preset in question. 3447 * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}. 3448 */ 3449 public int getCustomSizePreset() { 3450 return mCustomSizePreset; 3451 } 3452 3453 /** 3454 * Set the custom height in pixels for the display of this notification's content. 3455 * <p>This option is only available for custom display notifications created 3456 * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. See also 3457 * {@link NotificationCompat.WearableExtender#setCustomSizePreset} and 3458 * {@link #getCustomContentHeight}. 3459 */ 3460 public WearableExtender setCustomContentHeight(int height) { 3461 mCustomContentHeight = height; 3462 return this; 3463 } 3464 3465 /** 3466 * Get the custom height in pixels for the display of this notification's content. 3467 * <p>This option is only available for custom display notifications created 3468 * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and 3469 * {@link #setCustomContentHeight}. 3470 */ 3471 public int getCustomContentHeight() { 3472 return mCustomContentHeight; 3473 } 3474 3475 /** 3476 * Set whether the scrolling position for the contents of this notification should start 3477 * at the bottom of the contents instead of the top when the contents are too long to 3478 * display within the screen. Default is false (start scroll at the top). 3479 */ 3480 public WearableExtender setStartScrollBottom(boolean startScrollBottom) { 3481 setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom); 3482 return this; 3483 } 3484 3485 /** 3486 * Get whether the scrolling position for the contents of this notification should start 3487 * at the bottom of the contents instead of the top when the contents are too long to 3488 * display within the screen. Default is false (start scroll at the top). 3489 */ 3490 public boolean getStartScrollBottom() { 3491 return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0; 3492 } 3493 3494 /** 3495 * Set whether the content intent is available when the wearable device is not connected 3496 * to a companion device. The user can still trigger this intent when the wearable device 3497 * is offline, but a visual hint will indicate that the content intent may not be available. 3498 * Defaults to true. 3499 */ 3500 public WearableExtender setContentIntentAvailableOffline( 3501 boolean contentIntentAvailableOffline) { 3502 setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline); 3503 return this; 3504 } 3505 3506 /** 3507 * Get whether the content intent is available when the wearable device is not connected 3508 * to a companion device. The user can still trigger this intent when the wearable device 3509 * is offline, but a visual hint will indicate that the content intent may not be available. 3510 * Defaults to true. 3511 */ 3512 public boolean getContentIntentAvailableOffline() { 3513 return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0; 3514 } 3515 3516 /** 3517 * Set a hint that this notification's icon should not be displayed. 3518 * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise. 3519 * @return this object for method chaining 3520 */ 3521 public WearableExtender setHintHideIcon(boolean hintHideIcon) { 3522 setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon); 3523 return this; 3524 } 3525 3526 /** 3527 * Get a hint that this notification's icon should not be displayed. 3528 * @return {@code true} if this icon should not be displayed, false otherwise. 3529 * The default value is {@code false} if this was never set. 3530 */ 3531 public boolean getHintHideIcon() { 3532 return (mFlags & FLAG_HINT_HIDE_ICON) != 0; 3533 } 3534 3535 /** 3536 * Set a visual hint that only the background image of this notification should be 3537 * displayed, and other semantic content should be hidden. This hint is only applicable 3538 * to sub-pages added using {@link #addPage}. 3539 */ 3540 public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) { 3541 setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly); 3542 return this; 3543 } 3544 3545 /** 3546 * Get a visual hint that only the background image of this notification should be 3547 * displayed, and other semantic content should be hidden. This hint is only applicable 3548 * to sub-pages added using {@link NotificationCompat.WearableExtender#addPage}. 3549 */ 3550 public boolean getHintShowBackgroundOnly() { 3551 return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0; 3552 } 3553 3554 /** 3555 * Set a hint that this notification's background should not be clipped if possible, 3556 * and should instead be resized to fully display on the screen, retaining the aspect 3557 * ratio of the image. This can be useful for images like barcodes or qr codes. 3558 * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible. 3559 * @return this object for method chaining 3560 */ 3561 public WearableExtender setHintAvoidBackgroundClipping( 3562 boolean hintAvoidBackgroundClipping) { 3563 setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping); 3564 return this; 3565 } 3566 3567 /** 3568 * Get a hint that this notification's background should not be clipped if possible, 3569 * and should instead be resized to fully display on the screen, retaining the aspect 3570 * ratio of the image. This can be useful for images like barcodes or qr codes. 3571 * @return {@code true} if it's ok if the background is clipped on the screen, false 3572 * otherwise. The default value is {@code false} if this was never set. 3573 */ 3574 public boolean getHintAvoidBackgroundClipping() { 3575 return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0; 3576 } 3577 3578 /** 3579 * Set a hint that the screen should remain on for at least this duration when 3580 * this notification is displayed on the screen. 3581 * @param timeout The requested screen timeout in milliseconds. Can also be either 3582 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}. 3583 * @return this object for method chaining 3584 */ 3585 public WearableExtender setHintScreenTimeout(int timeout) { 3586 mHintScreenTimeout = timeout; 3587 return this; 3588 } 3589 3590 /** 3591 * Get the duration, in milliseconds, that the screen should remain on for 3592 * when this notification is displayed. 3593 * @return the duration in milliseconds if > 0, or either one of the sentinel values 3594 * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}. 3595 */ 3596 public int getHintScreenTimeout() { 3597 return mHintScreenTimeout; 3598 } 3599 3600 /** 3601 * Set a hint that this notification's {@link BigPictureStyle} (if present) should be 3602 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and 3603 * qr codes, as well as other simple black-and-white tickets. 3604 * @param hintAmbientBigPicture {@code true} to enable converstion and ambient. 3605 * @return this object for method chaining 3606 */ 3607 public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) { 3608 setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture); 3609 return this; 3610 } 3611 3612 /** 3613 * Get a hint that this notification's {@link BigPictureStyle} (if present) should be 3614 * converted to low-bit and displayed in ambient mode, especially useful for barcodes and 3615 * qr codes, as well as other simple black-and-white tickets. 3616 * @return {@code true} if it should be displayed in ambient, false otherwise 3617 * otherwise. The default value is {@code false} if this was never set. 3618 */ 3619 public boolean getHintAmbientBigPicture() { 3620 return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0; 3621 } 3622 3623 /** 3624 * Set a hint that this notification's content intent will launch an {@link Activity} 3625 * directly, telling the platform that it can generate the appropriate transitions. 3626 * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch 3627 * an activity and transitions should be generated, false otherwise. 3628 * @return this object for method chaining 3629 */ 3630 public WearableExtender setHintContentIntentLaunchesActivity( 3631 boolean hintContentIntentLaunchesActivity) { 3632 setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity); 3633 return this; 3634 } 3635 3636 /** 3637 * Get a hint that this notification's content intent will launch an {@link Activity} 3638 * directly, telling the platform that it can generate the appropriate transitions 3639 * @return {@code true} if the content intent will launch an activity and transitions should 3640 * be generated, false otherwise. The default value is {@code false} if this was never set. 3641 */ 3642 public boolean getHintContentIntentLaunchesActivity() { 3643 return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0; 3644 } 3645 3646 /** 3647 * When you post a notification, if you set the dismissal id field, then when that 3648 * notification is canceled, notifications on other wearables and the paired Android phone 3649 * having that same dismissal id will also be canceled. Note that this only works if you 3650 * have notification bridge mode set to NO_BRIDGING in your Wear app manifest. See 3651 * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to 3652 * Notifications</a> for more information on how to use the bridge mode feature. 3653 * @param dismissalId the dismissal id of the notification. 3654 * @return this object for method chaining 3655 */ 3656 public WearableExtender setDismissalId(String dismissalId) { 3657 mDismissalId = dismissalId; 3658 return this; 3659 } 3660 3661 /** 3662 * Returns the dismissal id of the notification. 3663 * @return the dismissal id of the notification or null if it has not been set. 3664 */ 3665 public String getDismissalId() { 3666 return mDismissalId; 3667 } 3668 3669 private void setFlag(int mask, boolean value) { 3670 if (value) { 3671 mFlags |= mask; 3672 } else { 3673 mFlags &= ~mask; 3674 } 3675 } 3676 } 3677 3678 /** 3679 * <p>Helper class to add Android Auto extensions to notifications. To create a notification 3680 * with car extensions: 3681 * 3682 * <ol> 3683 * <li>Create an {@link NotificationCompat.Builder}, setting any desired 3684 * properties. 3685 * <li>Create a {@link CarExtender}. 3686 * <li>Set car-specific properties using the {@code add} and {@code set} methods of 3687 * {@link CarExtender}. 3688 * <li>Call {@link android.support.v4.app.NotificationCompat.Builder#extend(NotificationCompat.Extender)} 3689 * to apply the extensions to a notification. 3690 * <li>Post the notification to the notification system with the 3691 * {@code NotificationManagerCompat.notify(...)} methods and not the 3692 * {@code NotificationManager.notify(...)} methods. 3693 * </ol> 3694 * 3695 * <pre class="prettyprint"> 3696 * Notification notification = new NotificationCompat.Builder(context) 3697 * ... 3698 * .extend(new CarExtender() 3699 * .set*(...)) 3700 * .build(); 3701 * </pre> 3702 * 3703 * <p>Car extensions can be accessed on an existing notification by using the 3704 * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods 3705 * to access values. 3706 */ 3707 public static final class CarExtender implements Extender { 3708 private static final String TAG = "CarExtender"; 3709 3710 private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS"; 3711 private static final String EXTRA_LARGE_ICON = "large_icon"; 3712 private static final String EXTRA_CONVERSATION = "car_conversation"; 3713 private static final String EXTRA_COLOR = "app_color"; 3714 3715 private Bitmap mLargeIcon; 3716 private UnreadConversation mUnreadConversation; 3717 private int mColor = NotificationCompat.COLOR_DEFAULT; 3718 3719 /** 3720 * Create a {@link CarExtender} with default options. 3721 */ 3722 public CarExtender() { 3723 } 3724 3725 /** 3726 * Create a {@link CarExtender} from the CarExtender options of an existing Notification. 3727 * 3728 * @param notif The notification from which to copy options. 3729 */ 3730 public CarExtender(Notification notif) { 3731 if (Build.VERSION.SDK_INT < 21) { 3732 return; 3733 } 3734 3735 Bundle carBundle = getExtras(notif)==null ? 3736 null : getExtras(notif).getBundle(EXTRA_CAR_EXTENDER); 3737 if (carBundle != null) { 3738 mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON); 3739 mColor = carBundle.getInt(EXTRA_COLOR, NotificationCompat.COLOR_DEFAULT); 3740 3741 Bundle b = carBundle.getBundle(EXTRA_CONVERSATION); 3742 mUnreadConversation = (UnreadConversation) IMPL.getUnreadConversationFromBundle( 3743 b, UnreadConversation.FACTORY, RemoteInput.FACTORY); 3744 } 3745 } 3746 3747 /** 3748 * Apply car extensions to a notification that is being built. This is typically called by 3749 * the {@link android.support.v4.app.NotificationCompat.Builder#extend(NotificationCompat.Extender)} 3750 * method of {@link NotificationCompat.Builder}. 3751 */ 3752 @Override 3753 public NotificationCompat.Builder extend(NotificationCompat.Builder builder) { 3754 if (Build.VERSION.SDK_INT < 21) { 3755 return builder; 3756 } 3757 3758 Bundle carExtensions = new Bundle(); 3759 3760 if (mLargeIcon != null) { 3761 carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); 3762 } 3763 if (mColor != NotificationCompat.COLOR_DEFAULT) { 3764 carExtensions.putInt(EXTRA_COLOR, mColor); 3765 } 3766 3767 if (mUnreadConversation != null) { 3768 Bundle b = IMPL.getBundleForUnreadConversation(mUnreadConversation); 3769 carExtensions.putBundle(EXTRA_CONVERSATION, b); 3770 } 3771 3772 builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions); 3773 return builder; 3774 } 3775 3776 /** 3777 * Sets the accent color to use when Android Auto presents the notification. 3778 * 3779 * Android Auto uses the color set with {@link android.support.v4.app.NotificationCompat.Builder#setColor(int)} 3780 * to accent the displayed notification. However, not all colors are acceptable in an 3781 * automotive setting. This method can be used to override the color provided in the 3782 * notification in such a situation. 3783 */ 3784 public CarExtender setColor(@ColorInt int color) { 3785 mColor = color; 3786 return this; 3787 } 3788 3789 /** 3790 * Gets the accent color. 3791 * 3792 * @see #setColor 3793 */ 3794 @ColorInt 3795 public int getColor() { 3796 return mColor; 3797 } 3798 3799 /** 3800 * Sets the large icon of the car notification. 3801 * 3802 * If no large icon is set in the extender, Android Auto will display the icon 3803 * specified by {@link android.support.v4.app.NotificationCompat.Builder#setLargeIcon(android.graphics.Bitmap)} 3804 * 3805 * @param largeIcon The large icon to use in the car notification. 3806 * @return This object for method chaining. 3807 */ 3808 public CarExtender setLargeIcon(Bitmap largeIcon) { 3809 mLargeIcon = largeIcon; 3810 return this; 3811 } 3812 3813 /** 3814 * Gets the large icon used in this car notification, or null if no icon has been set. 3815 * 3816 * @return The large icon for the car notification. 3817 * @see CarExtender#setLargeIcon 3818 */ 3819 public Bitmap getLargeIcon() { 3820 return mLargeIcon; 3821 } 3822 3823 /** 3824 * Sets the unread conversation in a message notification. 3825 * 3826 * @param unreadConversation The unread part of the conversation this notification conveys. 3827 * @return This object for method chaining. 3828 */ 3829 public CarExtender setUnreadConversation(UnreadConversation unreadConversation) { 3830 mUnreadConversation = unreadConversation; 3831 return this; 3832 } 3833 3834 /** 3835 * Returns the unread conversation conveyed by this notification. 3836 * @see #setUnreadConversation(UnreadConversation) 3837 */ 3838 public UnreadConversation getUnreadConversation() { 3839 return mUnreadConversation; 3840 } 3841 3842 /** 3843 * A class which holds the unread messages from a conversation. 3844 */ 3845 public static class UnreadConversation extends NotificationCompatBase.UnreadConversation { 3846 private final String[] mMessages; 3847 private final RemoteInput mRemoteInput; 3848 private final PendingIntent mReplyPendingIntent; 3849 private final PendingIntent mReadPendingIntent; 3850 private final String[] mParticipants; 3851 private final long mLatestTimestamp; 3852 3853 UnreadConversation(String[] messages, RemoteInput remoteInput, 3854 PendingIntent replyPendingIntent, PendingIntent readPendingIntent, 3855 String[] participants, long latestTimestamp) { 3856 mMessages = messages; 3857 mRemoteInput = remoteInput; 3858 mReadPendingIntent = readPendingIntent; 3859 mReplyPendingIntent = replyPendingIntent; 3860 mParticipants = participants; 3861 mLatestTimestamp = latestTimestamp; 3862 } 3863 3864 /** 3865 * Gets the list of messages conveyed by this notification. 3866 */ 3867 @Override 3868 public String[] getMessages() { 3869 return mMessages; 3870 } 3871 3872 /** 3873 * Gets the remote input that will be used to convey the response to a message list, or 3874 * null if no such remote input exists. 3875 */ 3876 @Override 3877 public RemoteInput getRemoteInput() { 3878 return mRemoteInput; 3879 } 3880 3881 /** 3882 * Gets the pending intent that will be triggered when the user replies to this 3883 * notification. 3884 */ 3885 @Override 3886 public PendingIntent getReplyPendingIntent() { 3887 return mReplyPendingIntent; 3888 } 3889 3890 /** 3891 * Gets the pending intent that Android Auto will send after it reads aloud all messages 3892 * in this object's message list. 3893 */ 3894 @Override 3895 public PendingIntent getReadPendingIntent() { 3896 return mReadPendingIntent; 3897 } 3898 3899 /** 3900 * Gets the participants in the conversation. 3901 */ 3902 @Override 3903 public String[] getParticipants() { 3904 return mParticipants; 3905 } 3906 3907 /** 3908 * Gets the firs participant in the conversation. 3909 */ 3910 @Override 3911 public String getParticipant() { 3912 return mParticipants.length > 0 ? mParticipants[0] : null; 3913 } 3914 3915 /** 3916 * Gets the timestamp of the conversation. 3917 */ 3918 @Override 3919 public long getLatestTimestamp() { 3920 return mLatestTimestamp; 3921 } 3922 3923 static final Factory FACTORY = new Factory() { 3924 @Override 3925 public UnreadConversation build( 3926 String[] messages, RemoteInputCompatBase.RemoteInput remoteInput, 3927 PendingIntent replyPendingIntent, PendingIntent readPendingIntent, 3928 String[] participants, long latestTimestamp) { 3929 return new UnreadConversation( 3930 messages, (RemoteInput) remoteInput, replyPendingIntent, 3931 readPendingIntent, 3932 participants, latestTimestamp); 3933 } 3934 }; 3935 3936 /** 3937 * Builder class for {@link CarExtender.UnreadConversation} objects. 3938 */ 3939 public static class Builder { 3940 private final List<String> mMessages = new ArrayList<String>(); 3941 private final String mParticipant; 3942 private RemoteInput mRemoteInput; 3943 private PendingIntent mReadPendingIntent; 3944 private PendingIntent mReplyPendingIntent; 3945 private long mLatestTimestamp; 3946 3947 /** 3948 * Constructs a new builder for {@link CarExtender.UnreadConversation}. 3949 * 3950 * @param name The name of the other participant in the conversation. 3951 */ 3952 public Builder(String name) { 3953 mParticipant = name; 3954 } 3955 3956 /** 3957 * Appends a new unread message to the list of messages for this conversation. 3958 * 3959 * The messages should be added from oldest to newest. 3960 * 3961 * @param message The text of the new unread message. 3962 * @return This object for method chaining. 3963 */ 3964 public Builder addMessage(String message) { 3965 mMessages.add(message); 3966 return this; 3967 } 3968 3969 /** 3970 * Sets the pending intent and remote input which will convey the reply to this 3971 * notification. 3972 * 3973 * @param pendingIntent The pending intent which will be triggered on a reply. 3974 * @param remoteInput The remote input parcelable which will carry the reply. 3975 * @return This object for method chaining. 3976 * 3977 * @see CarExtender.UnreadConversation#getRemoteInput 3978 * @see CarExtender.UnreadConversation#getReplyPendingIntent 3979 */ 3980 public Builder setReplyAction( 3981 PendingIntent pendingIntent, RemoteInput remoteInput) { 3982 mRemoteInput = remoteInput; 3983 mReplyPendingIntent = pendingIntent; 3984 3985 return this; 3986 } 3987 3988 /** 3989 * Sets the pending intent that will be sent once the messages in this notification 3990 * are read. 3991 * 3992 * @param pendingIntent The pending intent to use. 3993 * @return This object for method chaining. 3994 */ 3995 public Builder setReadPendingIntent(PendingIntent pendingIntent) { 3996 mReadPendingIntent = pendingIntent; 3997 return this; 3998 } 3999 4000 /** 4001 * Sets the timestamp of the most recent message in an unread conversation. 4002 * 4003 * If a messaging notification has been posted by your application and has not 4004 * yet been cancelled, posting a later notification with the same id and tag 4005 * but without a newer timestamp may result in Android Auto not displaying a 4006 * heads up notification for the later notification. 4007 * 4008 * @param timestamp The timestamp of the most recent message in the conversation. 4009 * @return This object for method chaining. 4010 */ 4011 public Builder setLatestTimestamp(long timestamp) { 4012 mLatestTimestamp = timestamp; 4013 return this; 4014 } 4015 4016 /** 4017 * Builds a new unread conversation object. 4018 * 4019 * @return The new unread conversation object. 4020 */ 4021 public UnreadConversation build() { 4022 String[] messages = mMessages.toArray(new String[mMessages.size()]); 4023 String[] participants = { mParticipant }; 4024 return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent, 4025 mReadPendingIntent, participants, mLatestTimestamp); 4026 } 4027 } 4028 } 4029 } 4030 4031 4032 /** 4033 * Get an array of Notification objects from a parcelable array bundle field. 4034 * Update the bundle to have a typed array so fetches in the future don't need 4035 * to do an array copy. 4036 */ 4037 static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) { 4038 Parcelable[] array = bundle.getParcelableArray(key); 4039 if (array instanceof Notification[] || array == null) { 4040 return (Notification[]) array; 4041 } 4042 Notification[] typedArray = new Notification[array.length]; 4043 for (int i = 0; i < array.length; i++) { 4044 typedArray[i] = (Notification) array[i]; 4045 } 4046 bundle.putParcelableArray(key, typedArray); 4047 return typedArray; 4048 } 4049 4050 /** 4051 * Gets the {@link Notification#extras} field from a notification in a backwards 4052 * compatible manner. Extras field was supported from JellyBean (Api level 16) 4053 * forwards. This function will return null on older api levels. 4054 */ 4055 public static Bundle getExtras(Notification notif) { 4056 return IMPL.getExtras(notif); 4057 } 4058 4059 /** 4060 * Get the number of actions in this notification in a backwards compatible 4061 * manner. Actions were supported from JellyBean (Api level 16) forwards. 4062 */ 4063 public static int getActionCount(Notification notif) { 4064 return IMPL.getActionCount(notif); 4065 } 4066 4067 /** 4068 * Get an action on this notification in a backwards compatible 4069 * manner. Actions were supported from JellyBean (Api level 16) forwards. 4070 * @param notif The notification to inspect. 4071 * @param actionIndex The index of the action to retrieve. 4072 */ 4073 public static Action getAction(Notification notif, int actionIndex) { 4074 return IMPL.getAction(notif, actionIndex); 4075 } 4076 4077 /** 4078 * Get the category of this notification in a backwards compatible 4079 * manner. 4080 * @param notif The notification to inspect. 4081 */ 4082 public static String getCategory(Notification notif) { 4083 return IMPL.getCategory(notif); 4084 } 4085 4086 /** 4087 * Get whether or not this notification is only relevant to the current device. 4088 * 4089 * <p>Some notifications can be bridged to other devices for remote display. 4090 * If this hint is set, it is recommend that this notification not be bridged. 4091 */ 4092 public static boolean getLocalOnly(Notification notif) { 4093 return IMPL.getLocalOnly(notif); 4094 } 4095 4096 /** 4097 * Get the key used to group this notification into a cluster or stack 4098 * with other notifications on devices which support such rendering. 4099 */ 4100 public static String getGroup(Notification notif) { 4101 return IMPL.getGroup(notif); 4102 } 4103 4104 /** 4105 * Get whether this notification to be the group summary for a group of notifications. 4106 * Grouped notifications may display in a cluster or stack on devices which 4107 * support such rendering. Requires a group key also be set using {@link Builder#setGroup}. 4108 * @return Whether this notification is a group summary. 4109 */ 4110 public static boolean isGroupSummary(Notification notif) { 4111 return IMPL.isGroupSummary(notif); 4112 } 4113 4114 /** 4115 * Get a sort key that orders this notification among other notifications from the 4116 * same package. This can be useful if an external sort was already applied and an app 4117 * would like to preserve this. Notifications will be sorted lexicographically using this 4118 * value, although providing different priorities in addition to providing sort key may 4119 * cause this value to be ignored. 4120 * 4121 * <p>This sort key can also be used to order members of a notification group. See 4122 * {@link Builder#setGroup}. 4123 * 4124 * @see String#compareTo(String) 4125 */ 4126 public static String getSortKey(Notification notif) { 4127 return IMPL.getSortKey(notif); 4128 } 4129} 4130