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