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