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