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