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