1 package com.google.android.car.kitchensink.notification; 2 3 import static android.app.Notification.FLAG_FOREGROUND_SERVICE; 4 5 import android.annotation.Nullable; 6 import android.app.Notification; 7 import android.app.NotificationChannel; 8 import android.app.NotificationManager; 9 import android.app.PendingIntent; 10 import android.content.Context; 11 import android.content.Intent; 12 import android.graphics.drawable.Icon; 13 import android.media.session.MediaSession; 14 import android.os.Bundle; 15 import android.os.Handler; 16 import android.view.LayoutInflater; 17 import android.view.View; 18 import android.view.ViewGroup; 19 import android.widget.NumberPicker; 20 21 import androidx.annotation.DrawableRes; 22 import androidx.core.app.NotificationCompat; 23 import androidx.core.app.NotificationCompat.Action; 24 import androidx.core.app.NotificationCompat.MessagingStyle; 25 import androidx.core.app.Person; 26 import androidx.core.app.RemoteInput; 27 import androidx.core.graphics.drawable.IconCompat; 28 import androidx.fragment.app.Fragment; 29 30 import com.google.android.car.kitchensink.KitchenSinkActivity; 31 import com.google.android.car.kitchensink.R; 32 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 import java.util.List; 36 37 /** 38 * Test fragment that can send all sorts of notifications. 39 */ 40 public class NotificationFragment extends Fragment { 41 private static final String IMPORTANCE_HIGH_ID = "importance_high"; 42 private static final String IMPORTANCE_HIGH_NO_SOUND_ID = "importance_high_no_sound"; 43 private static final String IMPORTANCE_DEFAULT_ID = "importance_default"; 44 private static final String IMPORTANCE_LOW_ID = "importance_low"; 45 private static final String IMPORTANCE_MIN_ID = "importance_min"; 46 private static final String IMPORTANCE_NONE_ID = "importance_none"; 47 public static final String INTENT_CATEGORY_SELF_DISMISS = 48 "com.google.android.car.kitchensink.notification.INTENT_CATEGORY_SELF_DISMISS"; 49 public static final int SELF_DISMISS_NOTIFICATION_ID = 987; 50 private int mCurrentNotificationId; 51 private int mCurrentGroupNotificationCount; 52 private NotificationManager mManager; 53 private Context mContext; 54 private Handler mHandler = new Handler(); 55 private HashMap<Integer, Runnable> mUpdateRunnables = new HashMap<>(); 56 57 @Override onCreate(Bundle savedInstanceState)58 public void onCreate(Bundle savedInstanceState) { 59 super.onCreate(savedInstanceState); 60 mContext = getActivity(); 61 mManager = 62 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 63 64 mManager.createNotificationChannel(new NotificationChannel( 65 IMPORTANCE_HIGH_ID, "Importance High", NotificationManager.IMPORTANCE_HIGH)); 66 67 NotificationChannel noSoundChannel = new NotificationChannel( 68 IMPORTANCE_HIGH_NO_SOUND_ID, "No sound", NotificationManager.IMPORTANCE_HIGH); 69 noSoundChannel.setSound(null, null); 70 mManager.createNotificationChannel(noSoundChannel); 71 72 mManager.createNotificationChannel(new NotificationChannel( 73 IMPORTANCE_DEFAULT_ID, 74 "Importance Default", 75 NotificationManager.IMPORTANCE_DEFAULT)); 76 77 mManager.createNotificationChannel(new NotificationChannel( 78 IMPORTANCE_LOW_ID, "Importance Low", NotificationManager.IMPORTANCE_LOW)); 79 80 mManager.createNotificationChannel(new NotificationChannel( 81 IMPORTANCE_MIN_ID, "Importance Min", NotificationManager.IMPORTANCE_MIN)); 82 83 mManager.createNotificationChannel(new NotificationChannel( 84 IMPORTANCE_NONE_ID, "Importance None", NotificationManager.IMPORTANCE_NONE)); 85 } 86 87 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)88 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 89 @Nullable Bundle savedInstanceState) { 90 View view = inflater.inflate(R.layout.notification_fragment, container, false); 91 92 initCancelAllButton(view); 93 94 initCarCategoriesButton(view); 95 96 initImportanceHighBotton(view); 97 initImportanceDefaultButton(view); 98 initImportanceLowButton(view); 99 initImportanceMinButton(view); 100 101 initIncomingButton(view); 102 initOngoingButton(view); 103 initMessagingStyleButtonForDiffPerson(view); 104 initMessagingStyleButtonForSamePerson(view); 105 initMessagingStyleButtonForLongMessageSamePerson(view); 106 initMessagingStyleButtonForMessageSameGroup(view); 107 initMessagingStyleButtonWithMuteAction(view); 108 initTestMessagesButton(view); 109 initProgressButton(view); 110 initProgressColorizedButton(view); 111 initNavigationButton(view); 112 initMediaButton(view); 113 initCallButton(view); 114 initCustomGroupSummaryButton(view); 115 initGroupWithoutSummaryButton(view); 116 initCustomizableMessageButton(view); 117 initButtonWithCustomActionIcon(view); 118 initSelfRemovingNotification(view); 119 120 return view; 121 } 122 123 @Override onResume()124 public void onResume() { 125 super.onResume(); 126 View view = getView(); 127 if (view != null) { 128 view.post(() -> view.scrollTo(0, view.findViewById(R.id.fragment_top).getTop())); 129 } 130 } 131 createServiceIntent(int notificationId, String action)132 private PendingIntent createServiceIntent(int notificationId, String action) { 133 Intent intent = new Intent(mContext, KitchenSinkActivity.class).setAction(action); 134 135 return PendingIntent.getForegroundService(mContext, notificationId, intent, 136 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); 137 } 138 initCancelAllButton(View view)139 private void initCancelAllButton(View view) { 140 view.findViewById(R.id.cancel_all_button).setOnClickListener(v -> { 141 for (Runnable runnable : mUpdateRunnables.values()) { 142 mHandler.removeCallbacks(runnable); 143 } 144 mUpdateRunnables.clear(); 145 mManager.cancelAll(); 146 }); 147 } 148 initCarCategoriesButton(View view)149 private void initCarCategoriesButton(View view) { 150 view.findViewById(R.id.category_car_emergency_button).setOnClickListener(v -> { 151 Notification notification = new Notification 152 .Builder(mContext, IMPORTANCE_HIGH_ID) 153 .setContentTitle("Car Emergency") 154 .setContentText("Shows heads-up; Shows on top of the list; Does not group") 155 .setCategory(Notification.CATEGORY_CAR_EMERGENCY) 156 .setSmallIcon(R.drawable.car_ic_mode) 157 .build(); 158 mManager.notify(mCurrentNotificationId++, notification); 159 }); 160 161 view.findViewById(R.id.category_car_warning_button).setOnClickListener(v -> { 162 163 Notification notification = new Notification 164 .Builder(mContext, IMPORTANCE_HIGH_ID) 165 .setContentTitle("Car Warning") 166 .setContentText( 167 "Shows heads-up; Shows on top of the list but below Car Emergency; " 168 + "Does not group") 169 .setCategory(Notification.CATEGORY_CAR_WARNING) 170 .setColor(mContext.getColor(android.R.color.holo_orange_dark)) 171 .setColorized(true) 172 .setSmallIcon(R.drawable.car_ic_mode) 173 .build(); 174 mManager.notify(mCurrentNotificationId++, notification); 175 }); 176 177 view.findViewById(R.id.category_car_info_button).setOnClickListener(v -> { 178 Notification notification = new Notification 179 .Builder(mContext, IMPORTANCE_DEFAULT_ID) 180 .setContentTitle("Car information") 181 .setContentText("Doesn't show heads-up; Importance Default; Groups") 182 .setCategory(Notification.CATEGORY_CAR_INFORMATION) 183 .setColor(mContext.getColor(android.R.color.holo_orange_light)) 184 .setColorized(true) 185 .setSmallIcon(R.drawable.car_ic_mode) 186 .build(); 187 mManager.notify(mCurrentNotificationId++, notification); 188 }); 189 190 } 191 initImportanceHighBotton(View view)192 private void initImportanceHighBotton(View view) { 193 Intent intent = new Intent(mContext, KitchenSinkActivity.class); 194 PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 195 PendingIntent.FLAG_IMMUTABLE); 196 197 Notification notification1 = new Notification 198 .Builder(mContext, IMPORTANCE_HIGH_ID) 199 .setContentTitle("Importance High: Shows as a heads-up") 200 .setContentText( 201 "Each click generates a new notification. And some " 202 + "looooooong text. " 203 + "Loooooooooooooooooooooong. " 204 + "Loooooooooooooooooooooooooooooooooooooooooooooooooong.") 205 .setSmallIcon(R.drawable.car_ic_mode) 206 .addAction( 207 new Notification.Action.Builder( 208 null, "Long Action (no-op)", pendingIntent).build()) 209 .addAction( 210 new Notification.Action.Builder( 211 null, "Action (no-op)", pendingIntent).build()) 212 .addAction( 213 new Notification.Action.Builder( 214 null, "Long Action (no-op)", pendingIntent).build()) 215 .setColor(mContext.getColor(android.R.color.holo_red_light)) 216 .build(); 217 218 view.findViewById(R.id.importance_high_button).setOnClickListener( 219 v -> mManager.notify(mCurrentNotificationId++, notification1) 220 ); 221 } 222 initImportanceDefaultButton(View view)223 private void initImportanceDefaultButton(View view) { 224 view.findViewById(R.id.importance_default_button).setOnClickListener(v -> { 225 Notification notification = new Notification 226 .Builder(mContext, IMPORTANCE_DEFAULT_ID) 227 .setContentTitle("No heads-up; Importance Default; Groups") 228 .setSmallIcon(R.drawable.car_ic_mode) 229 .build(); 230 mManager.notify(mCurrentNotificationId++, notification); 231 }); 232 } 233 initImportanceLowButton(View view)234 private void initImportanceLowButton(View view) { 235 view.findViewById(R.id.importance_low_button).setOnClickListener(v -> { 236 237 Notification notification = new Notification.Builder(mContext, IMPORTANCE_LOW_ID) 238 .setContentTitle("Importance Low") 239 .setContentText("No heads-up; Below Importance Default; Groups") 240 .setSmallIcon(R.drawable.car_ic_mode) 241 .build(); 242 mManager.notify(mCurrentNotificationId++, notification); 243 }); 244 } 245 initImportanceMinButton(View view)246 private void initImportanceMinButton(View view) { 247 view.findViewById(R.id.importance_min_button).setOnClickListener(v -> { 248 249 Notification notification = new Notification.Builder(mContext, IMPORTANCE_MIN_ID) 250 .setContentTitle("Importance Min") 251 .setContentText("No heads-up; Below Importance Low; Groups") 252 .setSmallIcon(R.drawable.car_ic_mode) 253 .build(); 254 mManager.notify(mCurrentNotificationId++, notification); 255 }); 256 } 257 getAction(String text, @DrawableRes int actionIcon)258 private Notification.Action getAction(String text, @DrawableRes int actionIcon) { 259 Icon icon = Icon.createWithResource(mContext, actionIcon); 260 Intent intent = new Intent(mContext, KitchenSinkActivity.class); 261 PendingIntent pendingIntent = PendingIntent.getActivity( 262 mContext, 263 /* requestCode= */ 0, 264 intent, 265 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 266 return new Notification.Action.Builder(icon, text, pendingIntent).build(); 267 } 268 initIncomingButton(View view)269 private void initIncomingButton(View view) { 270 view.findViewById(R.id.incoming_notificationbuilder_button).setOnClickListener(v -> { 271 Notification notification = new Notification.Builder(mContext, IMPORTANCE_HIGH_ID) 272 .setSmallIcon(R.drawable.car_ic_mode) 273 .setContentTitle("Unknown number") 274 .setContentText("Incoming call") 275 .setOngoing(true) 276 .setActions( 277 getAction("Answer", R.drawable.ic_answer_icon), 278 getAction("Decline", R.drawable.ic_decline_icon)) 279 .build(); 280 281 mManager.notify(mCurrentNotificationId++, notification); 282 }); 283 284 view.findViewById(R.id.incoming_forIncomingCall_button).setOnClickListener(v -> { 285 286 android.app.Person caller = new android.app.Person.Builder() 287 .setName("Chuck Norris") 288 .setImportant(true) 289 .build(); 290 // Creating the call notification style 291 int declineId = mCurrentNotificationId++; 292 int answerId = mCurrentNotificationId++; 293 PendingIntent declineIntent = createServiceIntent(declineId, "Decline"); 294 PendingIntent answerIntent = createServiceIntent(answerId, "Answer"); 295 Notification.CallStyle notificationStyle = 296 Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent); 297 298 Notification notification = new Notification.Builder(mContext, IMPORTANCE_HIGH_ID) 299 .setSmallIcon(R.drawable.car_ic_mode) 300 .setContentTitle("Incoming call") 301 .setContentText("Incoming call from Chuck Norris") 302 .setStyle(notificationStyle) 303 .setOngoing(true) 304 .setCategory(Notification.CATEGORY_CALL) 305 .build(); 306 notification.flags = notification.flags | FLAG_FOREGROUND_SERVICE; 307 mManager.notify(mCurrentNotificationId++, notification); 308 }); 309 } 310 initOngoingButton(View view)311 private void initOngoingButton(View view) { 312 view.findViewById(R.id.ongoing_button).setOnClickListener(v -> { 313 314 Notification notification = new Notification 315 .Builder(mContext, IMPORTANCE_DEFAULT_ID) 316 .setContentTitle("Persistent/Ongoing Notification") 317 .setContentText("Cannot be dismissed; No heads-up; Importance default; Groups") 318 .setSmallIcon(R.drawable.car_ic_mode) 319 .setOngoing(true) 320 .build(); 321 mManager.notify(mCurrentNotificationId++, notification); 322 }); 323 } 324 initCustomizableMessageButton(View view)325 private void initCustomizableMessageButton(View view) { 326 NumberPicker messagesPicker = view.findViewById(R.id.number_messages); 327 messagesPicker.setMinValue(1); 328 messagesPicker.setMaxValue(25); 329 messagesPicker.setWrapSelectorWheel(true); 330 NumberPicker peoplePicker = view.findViewById(R.id.number_people); 331 peoplePicker.setMinValue(1); 332 peoplePicker.setMaxValue(25); 333 peoplePicker.setWrapSelectorWheel(true); 334 335 view.findViewById(R.id.customizable_message_button).setOnClickListener(v -> { 336 int id = mCurrentNotificationId++; 337 338 int numPeople = peoplePicker.getValue(); 339 int numMessages = messagesPicker.getValue(); 340 341 PendingIntent replyIntent = createServiceIntent(id, "reply"); 342 PendingIntent markAsReadIntent = createServiceIntent(id, "read"); 343 344 List<Person> personList = new ArrayList<>(); 345 346 for (int i = 1; i <= numPeople; i++) { 347 personList.add(new Person.Builder() 348 .setName("Person " + i) 349 .setIcon(IconCompat.createWithResource(v.getContext(), 350 i % 2 == 1 ? R.drawable.avatar1 : R.drawable.avatar2)) 351 .build()); 352 } 353 354 MessagingStyle messagingStyle = 355 new MessagingStyle(personList.get(0)) 356 .setConversationTitle("Customizable Group chat: " + id); 357 if (personList.size() > 1) { 358 messagingStyle.setGroupConversation(true); 359 } 360 361 int messageNumber = 1; 362 for (int i = 0; i < numMessages; i++) { 363 int personNum = i % numPeople; 364 if (personNum == numPeople - 1) { 365 messageNumber++; 366 } 367 Person person = personList.get(personNum); 368 String messageText = person.getName() + "'s " + messageNumber + " message"; 369 messagingStyle.addMessage( 370 new MessagingStyle.Message( 371 messageText, 372 System.currentTimeMillis(), 373 person)); 374 } 375 376 NotificationCompat.Builder notification = new NotificationCompat 377 .Builder(mContext, IMPORTANCE_HIGH_ID) 378 .setContentTitle("Customizable Group chat (Title)") 379 .setContentText("Customizable Group chat (Text)") 380 .setShowWhen(true) 381 .setCategory(Notification.CATEGORY_MESSAGE) 382 .setSmallIcon(R.drawable.car_ic_mode) 383 .setStyle(messagingStyle) 384 .setAutoCancel(true) 385 .setColor(mContext.getColor(android.R.color.holo_green_light)) 386 .addAction( 387 new Action.Builder(R.drawable.ic_check_box, "read", markAsReadIntent) 388 .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ) 389 .setShowsUserInterface(false) 390 .build()) 391 .addAction( 392 new Action.Builder(R.drawable.ic_check_box, "reply", replyIntent) 393 .setSemanticAction(Action.SEMANTIC_ACTION_REPLY) 394 .setShowsUserInterface(false) 395 .addRemoteInput(new RemoteInput.Builder("input").build()) 396 .build()); 397 398 mManager.notify(id, notification.build()); 399 }); 400 } 401 initMessagingStyleButtonForDiffPerson(View view)402 private void initMessagingStyleButtonForDiffPerson(View view) { 403 view.findViewById(R.id.category_message_diff_person_button).setOnClickListener(v -> { 404 int id = mCurrentNotificationId++; 405 406 PendingIntent replyIntent = createServiceIntent(id, "reply"); 407 PendingIntent markAsReadIntent = createServiceIntent(id, "read"); 408 409 Person person1 = new Person.Builder() 410 .setName("Person " + id) 411 .setIcon(IconCompat.createWithResource(v.getContext(), R.drawable.avatar1)) 412 .build(); 413 Person person2 = new Person.Builder() 414 .setName("Person " + id + 1) 415 .setIcon(IconCompat.createWithResource(v.getContext(), R.drawable.android_logo)) 416 .build(); 417 Person person3 = new Person.Builder() 418 .setName("Person " + id + 2) 419 .setIcon(IconCompat.createWithResource(v.getContext(), R.drawable.avatar2)) 420 .build(); 421 MessagingStyle messagingStyle = 422 new MessagingStyle(person3) 423 .setConversationTitle("Group chat") 424 .addMessage( 425 new MessagingStyle.Message( 426 person1.getName() + "'s message", 427 System.currentTimeMillis(), 428 person1)) 429 .addMessage( 430 new MessagingStyle.Message( 431 person2.getName() + "'s message", 432 System.currentTimeMillis(), 433 person2)) 434 .addMessage( 435 new MessagingStyle.Message( 436 person3.getName() + "'s message; " 437 + "Each click generates a new" 438 + "notification. And some looooooong text. " 439 + "Loooooooooooooooooooooong. " 440 + "Loooooooooooooooooooooooooong." 441 + "Long long long long text.", 442 System.currentTimeMillis(), 443 person3)); 444 445 NotificationCompat.Builder notification = new NotificationCompat 446 .Builder(mContext, IMPORTANCE_HIGH_ID) 447 .setContentTitle("Jane, John, Joe") 448 .setContentText("Group chat") 449 .setShowWhen(true) 450 .setCategory(Notification.CATEGORY_MESSAGE) 451 .setSmallIcon(R.drawable.car_ic_mode) 452 .setStyle(messagingStyle) 453 .setAutoCancel(true) 454 .setColor(mContext.getColor(android.R.color.holo_green_light)) 455 .addAction( 456 new Action.Builder(R.drawable.ic_check_box, "read", markAsReadIntent) 457 .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ) 458 .setShowsUserInterface(false) 459 .build()) 460 .addAction( 461 new Action.Builder(R.drawable.ic_check_box, "reply", replyIntent) 462 .setSemanticAction(Action.SEMANTIC_ACTION_REPLY) 463 .setShowsUserInterface(false) 464 .addRemoteInput(new RemoteInput.Builder("input").build()) 465 .build()); 466 467 mManager.notify(id, notification.build()); 468 }); 469 } 470 initMessagingStyleButtonForMessageSameGroup(View view)471 private void initMessagingStyleButtonForMessageSameGroup(View view) { 472 int numOfPeople = 3; 473 Person user = new Person.Builder() 474 .setName("User") 475 .setIcon(IconCompat.createWithResource(view.getContext(), R.drawable.avatar1)) 476 .build(); 477 478 MessagingStyle messagingStyle = 479 new MessagingStyle(user) 480 .setConversationTitle("Same group chat") 481 .setGroupConversation(true); 482 483 List<Person> personList = new ArrayList<>(); 484 for (int i = 1; i <= numOfPeople; i++) { 485 personList.add(new Person.Builder() 486 .setName("Person " + i) 487 .setIcon(IconCompat.createWithResource(view.getContext(), 488 i % 2 == 1 ? R.drawable.avatar1 : R.drawable.avatar2)) 489 .build()); 490 } 491 492 view.findViewById(R.id.category_message_same_group_button).setOnClickListener(v -> { 493 mCurrentGroupNotificationCount++; 494 PendingIntent replyIntent = createServiceIntent(123456, "reply"); 495 PendingIntent markAsReadIntent = createServiceIntent(123456, "read"); 496 Person person = personList.get(mCurrentGroupNotificationCount % numOfPeople); 497 String messageText = 498 person.getName() + "'s " + mCurrentGroupNotificationCount + " message"; 499 messagingStyle.addMessage( 500 new MessagingStyle.Message(messageText, System.currentTimeMillis(), person)); 501 502 NotificationCompat.Builder notification = new NotificationCompat 503 .Builder(mContext, IMPORTANCE_HIGH_ID) 504 .setContentTitle("Same Group chat (Title)") 505 .setContentText("Same Group chat (Text)") 506 .setShowWhen(true) 507 .setCategory(Notification.CATEGORY_MESSAGE) 508 .setSmallIcon(R.drawable.car_ic_mode) 509 .setStyle(messagingStyle) 510 .setAutoCancel(true) 511 .addAction( 512 new Action.Builder(R.drawable.ic_check_box, "read", markAsReadIntent) 513 .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ) 514 .setShowsUserInterface(false) 515 .build()) 516 .addAction( 517 new Action.Builder(R.drawable.ic_check_box, "reply", replyIntent) 518 .setSemanticAction(Action.SEMANTIC_ACTION_REPLY) 519 .setShowsUserInterface(false) 520 .addRemoteInput(new RemoteInput.Builder("input").build()) 521 .build()); 522 523 mManager.notify(123456, notification.build()); 524 }); 525 } 526 initMessagingStyleButtonForSamePerson(View view)527 private void initMessagingStyleButtonForSamePerson(View view) { 528 view.findViewById(R.id.category_message_same_person_button).setOnClickListener(v -> { 529 int id = mCurrentNotificationId++; 530 531 PendingIntent replyIntent = createServiceIntent(id, "reply"); 532 PendingIntent markAsReadIntent = createServiceIntent(id, "read"); 533 534 Person person = new Person.Builder().setName("John Doe").build(); 535 MessagingStyle messagingStyle = 536 new MessagingStyle(person).setConversationTitle("Hello!"); 537 NotificationCompat.Builder builder = new NotificationCompat 538 .Builder(mContext, IMPORTANCE_HIGH_ID) 539 .setContentTitle("Message from someone") 540 .setContentText("hi") 541 .setShowWhen(true) 542 .setCategory(Notification.CATEGORY_MESSAGE) 543 .setSmallIcon(R.drawable.car_ic_mode) 544 .setAutoCancel(true) 545 .setColor(mContext.getColor(android.R.color.holo_green_light)) 546 .addAction( 547 new Action.Builder(R.drawable.ic_check_box, "read", markAsReadIntent) 548 .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ) 549 .setShowsUserInterface(false) 550 .build()) 551 .addAction( 552 new Action.Builder(R.drawable.ic_check_box, "reply", replyIntent) 553 .setSemanticAction(Action.SEMANTIC_ACTION_REPLY) 554 .setShowsUserInterface(false) 555 .addRemoteInput(new RemoteInput.Builder("input").build()) 556 .build()); 557 558 NotificationCompat.Builder updateNotification = 559 builder.setStyle(messagingStyle.addMessage( 560 new MessagingStyle.Message( 561 "Message " + id, 562 System.currentTimeMillis(), 563 person))); 564 mManager.notify(12345, updateNotification.build()); 565 }); 566 } 567 initMessagingStyleButtonForLongMessageSamePerson(View view)568 private void initMessagingStyleButtonForLongMessageSamePerson(View view) { 569 view.findViewById(R.id.category_long_message_same_person_button).setOnClickListener(v -> { 570 int id = mCurrentNotificationId++; 571 572 PendingIntent replyIntent = createServiceIntent(id, "reply"); 573 PendingIntent markAsReadIntent = createServiceIntent(id, "read"); 574 575 576 Person person = new Person.Builder().setName("John Doe").build(); 577 MessagingStyle messagingStyle = 578 new MessagingStyle(person).setConversationTitle("Hello!"); 579 NotificationCompat.Builder builder = new NotificationCompat 580 .Builder(mContext, IMPORTANCE_HIGH_ID) 581 .setContentTitle("Message from someone") 582 .setContentText("hi") 583 .setShowWhen(true) 584 .setCategory(Notification.CATEGORY_MESSAGE) 585 .setSmallIcon(R.drawable.car_ic_mode) 586 .setAutoCancel(true) 587 .setColor(mContext.getColor(android.R.color.holo_green_light)) 588 .addAction( 589 new Action.Builder(R.drawable.ic_check_box, "read", markAsReadIntent) 590 .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ) 591 .setShowsUserInterface(false) 592 .build()) 593 .addAction( 594 new Action.Builder(R.drawable.ic_check_box, "reply", replyIntent) 595 .setSemanticAction(Action.SEMANTIC_ACTION_REPLY) 596 .setShowsUserInterface(false) 597 .addRemoteInput(new RemoteInput.Builder("input").build()) 598 .build()); 599 600 String messageText = ""; 601 for (int i = 0; i < 100; i++) { 602 messageText += " test"; 603 } 604 605 NotificationCompat.Builder updateNotification = 606 builder.setStyle(messagingStyle.addMessage( 607 new MessagingStyle.Message( 608 id + messageText, 609 System.currentTimeMillis(), 610 person))); 611 mManager.notify(12345, updateNotification.build()); 612 }); 613 } 614 615 initMessagingStyleButtonWithMuteAction(View view)616 private void initMessagingStyleButtonWithMuteAction(View view) { 617 view.findViewById(R.id.category_message_mute_action_button).setOnClickListener(v -> { 618 int id = mCurrentNotificationId++; 619 620 PendingIntent replyIntent = createServiceIntent(id, "reply"); 621 PendingIntent markAsReadIntent = createServiceIntent(id, "read"); 622 PendingIntent muteIntent = createServiceIntent(id, "mute"); 623 624 Person person = new Person.Builder().setName("John Doe").build(); 625 MessagingStyle messagingStyle = 626 new MessagingStyle(person).setConversationTitle("Hello, try muting me!"); 627 NotificationCompat.Builder builder = new NotificationCompat 628 .Builder(mContext, IMPORTANCE_HIGH_ID) 629 .setContentTitle("Message from someone") 630 .setContentText("Muting notification when " 631 + "mute pending intent is provided by posting app") 632 .setShowWhen(true) 633 .setCategory(Notification.CATEGORY_MESSAGE) 634 .setSmallIcon(R.drawable.car_ic_mode) 635 .setAutoCancel(true) 636 .setColor(mContext.getColor(android.R.color.holo_green_light)) 637 .addAction( 638 new Action.Builder(R.drawable.ic_check_box, "read", markAsReadIntent) 639 .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ) 640 .setShowsUserInterface(false) 641 .build()) 642 .addAction( 643 new Action.Builder(R.drawable.ic_check_box, "mute", muteIntent) 644 .setSemanticAction(Action.SEMANTIC_ACTION_MUTE) 645 .setShowsUserInterface(false) 646 .build()) 647 .addAction( 648 new Action.Builder(R.drawable.ic_check_box, "reply", replyIntent) 649 .setSemanticAction(Action.SEMANTIC_ACTION_REPLY) 650 .setShowsUserInterface(false) 651 .addRemoteInput(new RemoteInput.Builder("input").build()) 652 .build()); 653 654 builder.setStyle(messagingStyle.addMessage( 655 new MessagingStyle.Message( 656 "Message with mute pending intent" + id, 657 System.currentTimeMillis(), 658 person))); 659 mManager.notify(id, builder.build()); 660 }); 661 } 662 initTestMessagesButton(View view)663 private void initTestMessagesButton(View view) { 664 view.findViewById(R.id.test_message_button).setOnClickListener(v -> { 665 int id = mCurrentNotificationId++; 666 667 PendingIntent replyIntent = createServiceIntent(id, "reply"); 668 PendingIntent markAsReadIntent = createServiceIntent(id, "read"); 669 670 Person person = new Person.Builder().setName("John Doe " + id).build(); 671 MessagingStyle messagingStyle = 672 new MessagingStyle(person).setConversationTitle("Hello!"); 673 NotificationCompat.Builder builder = new NotificationCompat 674 .Builder(mContext, IMPORTANCE_HIGH_ID) 675 .setContentTitle("Message from someone") 676 .setContentText("hi") 677 .setShowWhen(true) 678 .setCategory(Notification.CATEGORY_MESSAGE) 679 .setSmallIcon(R.drawable.car_ic_mode) 680 .setAutoCancel(true) 681 .setColor(mContext.getColor(android.R.color.holo_green_light)) 682 .addAction( 683 new Action.Builder(R.drawable.ic_check_box, "read", markAsReadIntent) 684 .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ) 685 .setShowsUserInterface(false) 686 .build()) 687 .addAction( 688 new Action.Builder(R.drawable.ic_check_box, "reply", replyIntent) 689 .setSemanticAction(Action.SEMANTIC_ACTION_REPLY) 690 .setShowsUserInterface(false) 691 .addRemoteInput(new RemoteInput.Builder("input").build()) 692 .build()); 693 694 Runnable runnable = new Runnable() { 695 int mCount = 1; 696 697 @Override 698 public void run() { 699 NotificationCompat.Builder updateNotification = 700 builder.setStyle(messagingStyle.addMessage( 701 new MessagingStyle.Message( 702 "Message " + mCount++, 703 System.currentTimeMillis(), 704 person))); 705 mManager.notify(id, updateNotification.build()); 706 if (mCount < 5) { 707 mHandler.postDelayed(this, 6000); 708 } 709 } 710 }; 711 mUpdateRunnables.put(id, runnable); 712 mHandler.post(runnable); 713 }); 714 } 715 initProgressButton(View view)716 private void initProgressButton(View view) { 717 view.findViewById(R.id.progress_button).setOnClickListener(v -> { 718 int id = mCurrentNotificationId++; 719 720 Notification notification = new Notification 721 .Builder(mContext, IMPORTANCE_DEFAULT_ID) 722 .setContentTitle("Progress") 723 .setOngoing(/* ongoing= */ true) 724 .setContentText( 725 "Doesn't show heads-up; Importance Default; Groups; Ongoing (cannot " 726 + "be dismissed)") 727 .setProgress(/* max= */ 100, /* progress= */ 0, /* indeterminate= */ false) 728 .setContentInfo("0%") 729 .setSmallIcon(R.drawable.car_ic_mode) 730 .build(); 731 mManager.notify(id, notification); 732 733 int progress = 0; 734 Runnable runnable = getProgressNotifUpdateRunnable(id, progress, /* isColorized= */ 735 false); 736 mUpdateRunnables.put(id, runnable); 737 mHandler.post(runnable); 738 }); 739 } 740 initProgressColorizedButton(View view)741 private void initProgressColorizedButton(View view) { 742 view.findViewById(R.id.progress_button_colorized).setOnClickListener(v -> { 743 int id = mCurrentNotificationId++; 744 745 Notification notification = new Notification 746 .Builder(mContext, IMPORTANCE_DEFAULT_ID) 747 .setContentTitle("Progress (Colorized)") 748 .setOngoing(/* ongoing= */ true) 749 .setContentText( 750 "Doesn't show heads-up; Importance Default; Groups; Ongoing (cannot " 751 + "be dismissed)") 752 .setProgress(/* max= */ 100, /* progress= */ 0, /* indeterminate= */ false) 753 .setColor(mContext.getColor(android.R.color.holo_purple)) 754 .setContentInfo("0%") 755 .setSmallIcon(R.drawable.car_ic_mode) 756 .build(); 757 mManager.notify(id, notification); 758 759 int progress = 0; 760 Runnable runnable = getProgressNotifUpdateRunnable(id, progress, /* isColorized= */ 761 true); 762 mUpdateRunnables.put(id, runnable); 763 mHandler.post(runnable); 764 }); 765 } 766 getProgressNotifUpdateRunnable(int id, int progress, boolean isColorized)767 private Runnable getProgressNotifUpdateRunnable(int id, int progress, boolean isColorized) { 768 Runnable runnable = () -> { 769 Notification.Builder builder = new Notification 770 .Builder(mContext, IMPORTANCE_DEFAULT_ID) 771 .setContentTitle("Progress") 772 .setContentText("Doesn't show heads-up; Importance Default; Groups") 773 .setProgress(/* max= */ 100, progress, /* indeterminate= */ false) 774 .setOngoing(/* ongoing= */ true) 775 .setContentInfo(progress + "%") 776 .setSmallIcon(R.drawable.car_ic_mode); 777 if (isColorized) { 778 builder.setColor(mContext.getColor(android.R.color.holo_purple)); 779 } 780 Notification updateNotification = builder.build(); 781 mManager.notify(id, updateNotification); 782 if (progress + 5 <= 100) { 783 mHandler.postDelayed(getProgressNotifUpdateRunnable(id, progress + 5, isColorized), 784 /* delayMillis= */ 1000); 785 } 786 }; 787 mUpdateRunnables.put(id, runnable); 788 return runnable; 789 } 790 initNavigationButton(View view)791 private void initNavigationButton(View view) { 792 view.findViewById(R.id.navigation_button).setOnClickListener(v -> { 793 794 int id1 = mCurrentNotificationId++; 795 Runnable rightTurnRunnable = new Runnable() { 796 int mDistance = 900; 797 798 @Override 799 public void run() { 800 Notification updateNotification = new Notification 801 .Builder(mContext, IMPORTANCE_HIGH_ID) 802 .setCategory("navigation") 803 .setContentTitle("Navigation") 804 .setContentText("Turn right in " + mDistance + " ft") 805 .setColor(mContext.getColor(android.R.color.holo_green_dark)) 806 .setColorized(true) 807 .setSubText(mDistance + " ft") 808 .setSmallIcon(R.drawable.car_ic_mode) 809 .setOnlyAlertOnce(true) 810 .build(); 811 mManager.notify(id1, updateNotification); 812 mDistance -= 100; 813 if (mDistance >= 0) { 814 mHandler.postDelayed(this, 1000); 815 } else { 816 mManager.cancel(id1); 817 } 818 } 819 }; 820 mUpdateRunnables.put(id1, rightTurnRunnable); 821 mHandler.postDelayed(rightTurnRunnable, 1000); 822 823 int id2 = mCurrentNotificationId++; 824 Runnable exitRunnable = new Runnable() { 825 int mDistance = 20; 826 827 @Override 828 public void run() { 829 Notification updateNotification = new Notification 830 .Builder(mContext, IMPORTANCE_HIGH_ID) 831 .setCategory("navigation") 832 .setContentTitle("Navigation") 833 .setContentText("Exit in " + mDistance + " miles") 834 .setColor(mContext.getColor(android.R.color.holo_green_dark)) 835 .setColorized(true) 836 .setSubText(mDistance + " miles") 837 .setSmallIcon(R.drawable.car_ic_mode) 838 .setOnlyAlertOnce(true) 839 .build(); 840 mManager.notify(id2, updateNotification); 841 mDistance -= 1; 842 if (mDistance >= 0) { 843 mHandler.postDelayed(this, 500); 844 } 845 } 846 }; 847 mUpdateRunnables.put(id2, exitRunnable); 848 mHandler.postDelayed(exitRunnable, 10000); 849 }); 850 } 851 initMediaButton(View view)852 private void initMediaButton(View view) { 853 view.findViewById(R.id.media_button).setOnClickListener(v -> { 854 int id = mCurrentNotificationId++; 855 856 Notification.Builder builder = new Notification 857 .Builder(mContext, IMPORTANCE_DEFAULT_ID) 858 .setContentTitle("Lady Adora") 859 .setContentText("Funny Face") 860 .setColor(mContext.getColor(android.R.color.holo_orange_dark)) 861 .setColorized(true) 862 .setSubText("Some album") 863 .addAction(new Notification.Action(R.drawable.thumb_down, "Thumb down", null)) 864 .addAction(new Notification.Action(R.drawable.skip_prev, "Skip prev", null)) 865 .addAction(new Notification.Action(R.drawable.play_arrow, "Play", null)) 866 .addAction(new Notification.Action(R.drawable.skip_next, "Skip next", null)) 867 .addAction(new Notification.Action(R.drawable.thumb_up, "Thumb up", null)) 868 .setSmallIcon(R.drawable.play_arrow) 869 .setLargeIcon(Icon.createWithResource(mContext, R.drawable.android_logo)); 870 871 Notification.MediaStyle style = new Notification.MediaStyle(); 872 style.setShowActionsInCompactView(1, 2, 3); 873 MediaSession mediaSession = new MediaSession(mContext, "KitchenSink"); 874 style.setMediaSession(mediaSession.getSessionToken()); 875 builder.setStyle(style); 876 mediaSession.release(); 877 878 mManager.notify(id, builder.build()); 879 }); 880 } 881 initCallButton(View view)882 private void initCallButton(View view) { 883 Intent intent = new Intent(mContext, KitchenSinkActivity.class); 884 PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 885 PendingIntent.FLAG_IMMUTABLE); 886 887 view.findViewById(R.id.category_call_button).setOnClickListener(v -> { 888 Notification notification = new Notification 889 .Builder(mContext, IMPORTANCE_HIGH_ID) 890 .setContentTitle("+1 1231231234") 891 .setContentText("Shows persistent heads-up") 892 .setCategory(Notification.CATEGORY_CALL) 893 .setOngoing(true) 894 .setSmallIcon(R.drawable.car_ic_mode) 895 .setFullScreenIntent(pendingIntent, true) 896 .setColor(mContext.getColor(android.R.color.holo_red_light)) 897 .setColorized(true) 898 .build(); 899 mManager.notify(mCurrentNotificationId++, notification); 900 }); 901 } 902 initCustomGroupSummaryButton(View view)903 private void initCustomGroupSummaryButton(View view) { 904 view.findViewById(R.id.custom_group_summary_button).setOnClickListener(v -> { 905 String groupKey = "GROUP_KEY" + mCurrentNotificationId++; 906 int delay = 500; 907 908 Notification summaryNotification = new Notification 909 .Builder(mContext, IMPORTANCE_HIGH_ID) 910 .setContentTitle("6 New mails") 911 .setContentText("this is some summary") 912 .setSmallIcon(R.drawable.thumb_up) 913 .setLargeIcon(Icon.createWithResource(mContext, R.drawable.avatar1)) 914 .setGroup(groupKey) 915 .setGroupSummary(true) 916 .setStyle(new Notification.InboxStyle() 917 .addLine("line 1") 918 .addLine("line 2") 919 .addLine("line 3") 920 .addLine("line 4") 921 .addLine("line 5") 922 .setBigContentTitle("You've received 6 messages") 923 .setSummaryText("From Alice, Bob, Claire, Douglas..")) 924 .build(); 925 926 mHandler.postDelayed( 927 () -> mManager.notify(mCurrentNotificationId++, summaryNotification), delay); 928 for (int i = 1; i <= 6; i++) { 929 Notification notification = new Notification 930 .Builder(mContext, IMPORTANCE_HIGH_ID) 931 .setContentTitle("Group child " + i) 932 .setSmallIcon(R.drawable.car_ic_mode) 933 .setGroup(groupKey) 934 .setSortKey(Integer.toString(6 - i)) 935 .build(); 936 mHandler.postDelayed(() -> mManager.notify(mCurrentNotificationId++, notification), 937 delay += 5000); 938 } 939 }); 940 } 941 initGroupWithoutSummaryButton(View view)942 private void initGroupWithoutSummaryButton(View view) { 943 view.findViewById(R.id.group_without_summary_button).setOnClickListener(v -> { 944 String groupKey = "GROUP_KEY" + mCurrentNotificationId++; 945 946 for (int i = 1; i <= 6; i++) { 947 Notification notification = new Notification 948 .Builder(mContext, IMPORTANCE_DEFAULT_ID) 949 .setContentTitle("This notification should not group " + i) 950 .setSmallIcon(R.drawable.car_ic_mode) 951 .setGroup(groupKey) 952 .setSortKey(Integer.toString(i)) 953 .build(); 954 mHandler.post(() -> mManager.notify(mCurrentNotificationId++, notification)); 955 } 956 }); 957 } 958 initButtonWithCustomActionIcon(View view)959 private void initButtonWithCustomActionIcon(View view) { 960 Intent intent = new Intent(mContext, KitchenSinkActivity.class); 961 PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 962 PendingIntent.FLAG_IMMUTABLE); 963 964 Notification notification = new Notification 965 .Builder(mContext, IMPORTANCE_HIGH_ID) 966 .setContentTitle("Notification with custom button icon") 967 .setContentText( 968 "Icons should be shown in the action buttons") 969 .setSmallIcon(R.drawable.car_ic_mode) 970 .addAction( 971 new Notification.Action.Builder( 972 R.drawable.architecture, "architecture", pendingIntent) 973 .build()) 974 .addAction( 975 new Notification.Action.Builder( 976 Icon.createWithResource(this.getContext(), R.drawable.archive), 977 "archive", pendingIntent).build()) 978 .addAction( 979 new Notification.Action.Builder( 980 Icon.createWithResource(this.getContext().getPackageName(), 981 R.drawable.audiotrack), 982 "audio-track", pendingIntent).build()) 983 .setColor(mContext.getColor(android.R.color.holo_red_light)) 984 .build(); 985 986 view.findViewById(R.id.actions_with_icons).setOnClickListener( 987 v -> mManager.notify(mCurrentNotificationId++, notification) 988 ); 989 } 990 initSelfRemovingNotification(View view)991 private void initSelfRemovingNotification(View view) { 992 Intent intent = new Intent(mContext, KitchenSinkActivity.class); 993 intent.addCategory(INTENT_CATEGORY_SELF_DISMISS); 994 PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 995 PendingIntent.FLAG_IMMUTABLE); 996 997 Notification notification = new Notification 998 .Builder(mContext, IMPORTANCE_HIGH_ID) 999 .setContentTitle("Self Canceling notification") 1000 .setSmallIcon(R.drawable.car_ic_mode) 1001 .setContentIntent(pendingIntent) 1002 .addAction(new Notification.Action.Builder( 1003 null, "Click to cancel this notification", pendingIntent 1004 ).build()) 1005 .build(); 1006 1007 view.findViewById(R.id.self_dismiss_notification).setOnClickListener( 1008 v -> mManager.notify(SELF_DISMISS_NOTIFICATION_ID, notification) 1009 ); 1010 } 1011 } 1012