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