1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.notification;
18 
19 import static android.app.PendingIntent.FLAG_IMMUTABLE;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.ArgumentMatchers.anyInt;
25 import static org.mockito.ArgumentMatchers.anyString;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.ArgumentMatchers.isNull;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.times;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.app.ActivityManager;
34 import android.app.Notification;
35 import android.app.PendingIntent;
36 import android.app.RemoteInput;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.os.Bundle;
40 import android.os.Looper;
41 import android.os.RemoteException;
42 import android.os.UserHandle;
43 import android.service.notification.NotificationStats;
44 import android.service.notification.StatusBarNotification;
45 import android.view.View;
46 
47 import androidx.test.core.app.ApplicationProvider;
48 import androidx.test.ext.junit.runners.AndroidJUnit4;
49 
50 import com.android.car.assist.client.CarAssistUtils;
51 import com.android.car.notification.template.CarNotificationActionButton;
52 import com.android.car.notification.utils.MockMessageNotificationBuilder;
53 import com.android.internal.statusbar.IStatusBarService;
54 import com.android.internal.statusbar.NotificationVisibility;
55 
56 import org.junit.After;
57 import org.junit.Before;
58 import org.junit.Test;
59 import org.junit.runner.RunWith;
60 import org.mockito.Mock;
61 import org.mockito.MockitoAnnotations;
62 
63 import java.util.ArrayList;
64 import java.util.List;
65 
66 @RunWith(AndroidJUnit4.class)
67 public class NotificationClickHandlerFactoryTest {
68     private static final String PKG_1 = "package_1";
69     private static final String OP_PKG = "OpPackage";
70     private static final int ID = 1;
71     private static final String TAG = "Tag";
72     private static final int UID = 2;
73     private static final int INITIAL_PID = 3;
74     private static final String CHANNEL_ID = "CHANNEL_ID";
75     private static final String CONTENT_TITLE = "CONTENT_TITLE";
76     private static final String OVERRIDE_GROUP_KEY = "OVERRIDE_GROUP_KEY";
77     private static final long POST_TIME = 12345L;
78     private static final UserHandle USER_HANDLE = new UserHandle(/* userId= */ 12);
79 
80     private Context mContext;
81     private NotificationClickHandlerFactory mNotificationClickHandlerFactory;
82     private AlertEntry mAlertEntry1;
83     private AlertEntry mAlertEntry2;
84     private AlertEntry mAlertEntryMessageHeadsUp;
85     private AlertEntry mAlertEntryMessageWithMuteAction;
86     private PendingIntent mReplyActionPendingIntent;
87     private PendingIntent mMuteActionPendingIntent;
88 
89     @Mock
90     private IStatusBarService mBarService;
91     @Mock
92     private NotificationClickHandlerFactory.OnNotificationClickListener mListener1;
93     @Mock
94     private NotificationClickHandlerFactory.OnNotificationClickListener mListener2;
95     @Mock
96     private View mView;
97     @Mock
98     private StatusBarNotification mStatusBarNotification1;
99     @Mock
100     private StatusBarNotification mStatusBarNotification2;
101     @Mock
102     private UserHandle mUser1;
103     @Mock
104     private UserHandle mUser2;
105     @Mock
106     private PendingIntent mActionIntent;
107     @Mock
108     private RemoteInput mRemoteInput;
109     @Mock
110     private CarAssistUtils mCarAssistUtils;
111     @Mock
112     private NotificationClickHandlerFactory.MuteStatusSetter mMuteStatusSetter;
113 
114     @Before
setUp()115     public void setUp() {
116         MockitoAnnotations.initMocks(this);
117         mContext = ApplicationProvider.getApplicationContext();
118         mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(mBarService);
119         Notification notification1 = new Notification();
120         Notification notification2 = new Notification();
121         notification1.contentIntent = PendingIntent.getForegroundService(
122                 mContext, /* requestCode= */ 0, new Intent(), FLAG_IMMUTABLE);
123         notification2.contentIntent = PendingIntent.getForegroundService(
124                 mContext, /* requestCode= */ 0, new Intent(), FLAG_IMMUTABLE);
125         when(mStatusBarNotification1.getNotification()).thenReturn(notification1);
126         when(mStatusBarNotification2.getNotification()).thenReturn(notification2);
127         when(mStatusBarNotification1.getKey()).thenReturn("TEST_KEY_1");
128         when(mStatusBarNotification2.getKey()).thenReturn("TEST_KEY_2");
129         when(mStatusBarNotification1.getUser()).thenReturn(mUser1);
130         when(mStatusBarNotification2.getUser()).thenReturn(mUser2);
131         mAlertEntry1 = new AlertEntry(mStatusBarNotification1);
132         mAlertEntry2 = new AlertEntry(mStatusBarNotification2);
133 
134         MockMessageNotificationBuilder mockNotificationBuilder_messageHeadsUp =
135                 new MockMessageNotificationBuilder(mContext,
136                         CHANNEL_ID, android.R.drawable.sym_def_app_icon)
137                         .setContentTitle(CONTENT_TITLE)
138                         .setCategory(Notification.CATEGORY_MESSAGE)
139                         .setHasMessagingStyle(true)
140                         .setHasReplyAction(true)
141                         .setPendingIntentIsMocked(true)
142                         .setHasMarkAsRead(true);
143         MockMessageNotificationBuilder mockNotificationBuilder_messageHeadsUpWithMute =
144                 new MockMessageNotificationBuilder(mContext,
145                         CHANNEL_ID, android.R.drawable.sym_def_app_icon)
146                         .setContentTitle(CONTENT_TITLE)
147                         .setCategory(Notification.CATEGORY_MESSAGE)
148                         .setHasMessagingStyle(true)
149                         .setHasReplyAction(true)
150                         .setPendingIntentIsMocked(true)
151                         .setHasMarkAsRead(true)
152                         .setHasMuteAction(true);
153         mAlertEntryMessageHeadsUp = new AlertEntry(
154                 new StatusBarNotification(PKG_1, OP_PKG, ID, TAG, UID, INITIAL_PID,
155                         mockNotificationBuilder_messageHeadsUp.build(), USER_HANDLE,
156                         OVERRIDE_GROUP_KEY, POST_TIME));
157         mAlertEntryMessageWithMuteAction = new AlertEntry(
158                 new StatusBarNotification(PKG_1, OP_PKG, ID, TAG, UID, INITIAL_PID,
159                         mockNotificationBuilder_messageHeadsUpWithMute
160                                 .build(), USER_HANDLE,
161                         OVERRIDE_GROUP_KEY, POST_TIME));
162         mReplyActionPendingIntent = mockNotificationBuilder_messageHeadsUp.getPendingIntent();
163         mMuteActionPendingIntent =
164                 mockNotificationBuilder_messageHeadsUpWithMute.getPendingIntent();
165 
166         when(mView.getContext()).thenReturn(mContext);
167         mNotificationClickHandlerFactory.setCarAssistUtils(mCarAssistUtils);
168     }
169 
170     @After
tearDown()171     public void tearDown() {
172         NotificationDataManager.refreshInstance();
173     }
174 
175     @Test
onClickClickHandler_noIntent_returnsImmediately()176     public void onClickClickHandler_noIntent_returnsImmediately() {
177         mNotificationClickHandlerFactory.registerClickListener(mListener1);
178         // overriding the non-null value assigned in setup.
179         mAlertEntry1.getNotification().contentIntent = null;
180         mAlertEntry1.getNotification().fullScreenIntent = null;
181 
182         mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1).onClick(mView);
183 
184         verify(mListener1, never()).onNotificationClicked(anyInt(), any(AlertEntry.class));
185         try {
186             verify(mBarService, never())
187                     .onNotificationClick(anyString(), any(NotificationVisibility.class));
188         } catch (RemoteException ex) {
189             // ignore
190         }
191     }
192 
193     @Test
onClickClickHandler_intentExists_callsBarServiceNotificationClicked()194     public void onClickClickHandler_intentExists_callsBarServiceNotificationClicked() {
195         mNotificationClickHandlerFactory.registerClickListener(mListener1);
196 
197         mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1).onClick(mView);
198 
199         try {
200             verify(mBarService)
201                     .onNotificationClick(anyString(), any(NotificationVisibility.class));
202         } catch (RemoteException ex) {
203             // ignore
204         }
205     }
206 
207     @Test
onClickClickHandler_intentExists_invokesRegisteredClickListeners()208     public void onClickClickHandler_intentExists_invokesRegisteredClickListeners() {
209         mNotificationClickHandlerFactory.registerClickListener(mListener1);
210 
211         mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1).onClick(mView);
212 
213         verify(mListener1).onNotificationClicked(anyInt(), any(AlertEntry.class));
214     }
215 
216     @Test
onClickClickHandler_shouldAutoCancel_callsBarServiceOnNotificationClear()217     public void onClickClickHandler_shouldAutoCancel_callsBarServiceOnNotificationClear() {
218         mAlertEntry1.getStatusBarNotification().getNotification().flags =
219                 mStatusBarNotification1.getNotification().flags | Notification.FLAG_AUTO_CANCEL;
220         UserHandle user = new UserHandle( /* handle= */ 0);
221         when(mStatusBarNotification1.getUser()).thenReturn(user);
222         mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1).onClick(mView);
223         NotificationVisibility notificationVisibility = NotificationVisibility.obtain(
224                 mAlertEntry1.getKey(), /* rank= */ -1, /* count= */ -1, /* visible= */ true);
225 
226         try {
227             verify(mBarService).onNotificationClear(
228                     mAlertEntry1.getStatusBarNotification().getPackageName(),
229                     mAlertEntry1.getStatusBarNotification().getUser().getIdentifier(),
230                     mAlertEntry1.getKey(),
231                     NotificationStats.DISMISSAL_SHADE,
232                     NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
233                     notificationVisibility);
234         } catch (RemoteException ex) {
235             // ignore
236         }
237 
238     }
239 
240     @Test
onClickActionClickHandler_isReplyAction_sendsPendingIntent()241     public void onClickActionClickHandler_isReplyAction_sendsPendingIntent() {
242         // Have to prepare looper since Toast will be shown in test.
243         Looper.prepare();
244 
245         mNotificationClickHandlerFactory.getActionClickHandler(
246                 mAlertEntryMessageHeadsUp, /* index= */
247                 0).onClick(mView);
248         try {
249             verify(mReplyActionPendingIntent).sendAndReturnResult(any(Context.class), eq(0),
250                     any(Intent.class), isNull(), isNull(), isNull(), any(Bundle.class));
251         } catch (PendingIntent.CanceledException e) {
252             // ignore
253         }
254     }
255 
256     @Test
onClickActionClickHandler_isReplyAction_doesNotInvokeRegisteredClickListeners()257     public void onClickActionClickHandler_isReplyAction_doesNotInvokeRegisteredClickListeners() {
258         // Have to prepare looper since Toast will be shown in test.
259         Looper.prepare();
260 
261         mNotificationClickHandlerFactory.getActionClickHandler(
262                 mAlertEntryMessageHeadsUp, /* index= */
263                 0).onClick(mView);
264 
265         verify(mListener1, never()).onNotificationClicked(anyInt(), any(AlertEntry.class));
266     }
267 
268     @Test
onClickActionClickHandler_notCarCompatibleMessage_sendsPendingIntent()269     public void onClickActionClickHandler_notCarCompatibleMessage_sendsPendingIntent() {
270         PendingIntent pendingIntent = PendingIntent.getForegroundService(
271                 mContext, /* requestCode= */0,
272                 new Intent(), FLAG_IMMUTABLE);
273         Notification.Action action = new Notification.Action
274                 .Builder(/* icon= */ null, "ACTION_TITLE", pendingIntent)
275                 .setSemanticAction(Notification.Action.SEMANTIC_ACTION_REPLY)
276                 .addRemoteInput(mRemoteInput)
277                 .build();
278         action.actionIntent = mActionIntent;
279         Notification.Action[] actions = {action};
280         mStatusBarNotification1.getNotification().actions = actions;
281 
282         mNotificationClickHandlerFactory.getActionClickHandler(mAlertEntry1, /* index= */
283                 0).onClick(mView);
284         try {
285             verify(mActionIntent).sendAndReturnResult(isNull(), eq(0), isNull(), isNull(),
286                     isNull(), isNull(), any(Bundle.class));
287         } catch (PendingIntent.CanceledException e) {
288             // ignore
289         }
290     }
291 
292     @Test
onClickActionClickHandler_notCarCompatibleMessage_invokesRegisteredListeners()293     public void onClickActionClickHandler_notCarCompatibleMessage_invokesRegisteredListeners() {
294         mNotificationClickHandlerFactory.registerClickListener(mListener1);
295         PendingIntent pendingIntent = PendingIntent.getForegroundService(
296                 mContext, /* requestCode= */0,
297                 new Intent(), FLAG_IMMUTABLE);
298         Notification.Action action = new Notification.Action
299                 .Builder(/* icon= */ null, "ACTION_TITLE", pendingIntent)
300                 .setSemanticAction(Notification.Action.SEMANTIC_ACTION_REPLY)
301                 .addRemoteInput(mRemoteInput)
302                 .build();
303         action.actionIntent = mActionIntent;
304         Notification.Action[] actions = {action};
305         mStatusBarNotification1.getNotification().actions = actions;
306 
307         mNotificationClickHandlerFactory.getActionClickHandler(mAlertEntry1, /* index= */
308                 0).onClick(mView);
309 
310         verify(mListener1).onNotificationClicked(anyInt(), any(AlertEntry.class));
311     }
312 
313     @Test
onClickPlayClickHandler_notCarCompatibleMessage_returnsImmediately()314     public void onClickPlayClickHandler_notCarCompatibleMessage_returnsImmediately() {
315         mNotificationClickHandlerFactory.getPlayClickHandler(mAlertEntry1).onClick(mView);
316 
317         verify(mCarAssistUtils, never()).requestAssistantVoiceAction(any(), any(), any());
318     }
319 
320     @Test
onClickPlayClickHandler_isCarCompatibleMessage_requestsAssistantVoiceAction()321     public void onClickPlayClickHandler_isCarCompatibleMessage_requestsAssistantVoiceAction() {
322         mNotificationClickHandlerFactory.getPlayClickHandler(mAlertEntryMessageHeadsUp).onClick(
323                 mView);
324 
325         verify(mCarAssistUtils).requestAssistantVoiceAction(any(), any(), any());
326     }
327 
328     @Test
onClickMuteClickHandler_togglesMute()329     public void onClickMuteClickHandler_togglesMute() {
330         NotificationDataManager notificationDataManager = NotificationDataManager.getInstance();
331         notificationDataManager.addNewMessageNotification(mAlertEntryMessageHeadsUp);
332         CarNotificationActionButton button = new CarNotificationActionButton(mContext);
333 
334         // first make sure it is not muted by default
335         assertThat(notificationDataManager.isMessageNotificationMuted(
336                 mAlertEntryMessageHeadsUp)).isFalse();
337 
338         mNotificationClickHandlerFactory.getMuteClickHandler(button,
339                 mAlertEntryMessageHeadsUp, mMuteStatusSetter).onClick(mView);
340 
341         assertThat(notificationDataManager.isMessageNotificationMuted(
342                 mAlertEntryMessageHeadsUp)).isTrue();
343 
344         mNotificationClickHandlerFactory.getMuteClickHandler(button,
345                 mAlertEntryMessageHeadsUp, mMuteStatusSetter).onClick(mView);
346 
347         assertThat(notificationDataManager.isMessageNotificationMuted(
348                 mAlertEntryMessageHeadsUp)).isFalse();
349     }
350 
351     @Test
onClickMuteClickHandler_mutePendingIntent_notificationDataManagerUnchanged()352     public void onClickMuteClickHandler_mutePendingIntent_notificationDataManagerUnchanged() {
353         NotificationDataManager notificationDataManager = NotificationDataManager.getInstance();
354         notificationDataManager.addNewMessageNotification(mAlertEntryMessageWithMuteAction);
355         CarNotificationActionButton button = new CarNotificationActionButton(mContext);
356 
357         // first make sure it is not muted by default
358         assertThat(notificationDataManager.isMessageNotificationMuted(
359                 mAlertEntryMessageWithMuteAction)).isFalse();
360 
361         mNotificationClickHandlerFactory.getMuteClickHandler(button,
362                 mAlertEntryMessageWithMuteAction, mMuteStatusSetter).onClick(mView);
363 
364         // verify that notification data manager does not handle muting the notification
365         assertThat(notificationDataManager.isMessageNotificationMuted(
366                 mAlertEntryMessageWithMuteAction)).isFalse();
367     }
368 
369     @Test
onClickMuteClickHandler_mutePendingIntent_dismissesNotification()370     public void onClickMuteClickHandler_mutePendingIntent_dismissesNotification() {
371         NotificationDataManager notificationDataManager = NotificationDataManager.getInstance();
372         notificationDataManager.addNewMessageNotification(mAlertEntryMessageWithMuteAction);
373         CarNotificationActionButton button = new CarNotificationActionButton(mContext);
374         NotificationVisibility notificationVisibility = NotificationVisibility.obtain(
375                 mAlertEntryMessageWithMuteAction.getKey(),
376                 /* rank= */ -1,
377                 /* count= */ -1,
378                 /* visible= */ true);
379 
380         mNotificationClickHandlerFactory.getMuteClickHandler(button,
381                 mAlertEntryMessageWithMuteAction, mMuteStatusSetter).onClick(mView);
382 
383         // verify notification is dismissed
384         try {
385             verify(mBarService).onNotificationClear(
386                     mAlertEntryMessageWithMuteAction.getStatusBarNotification().getPackageName(),
387                     mAlertEntryMessageWithMuteAction
388                             .getStatusBarNotification().getUser().getIdentifier(),
389                     mAlertEntryMessageWithMuteAction.getStatusBarNotification().getKey(),
390                     NotificationStats.DISMISSAL_SHADE,
391                     NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
392                     notificationVisibility);
393         } catch (RemoteException e) {
394             // ignore.
395         }
396     }
397 
398     @Test
onClickMuteClickHandler_mutePendingIntent_firesPendingIntent()399     public void onClickMuteClickHandler_mutePendingIntent_firesPendingIntent() {
400         NotificationDataManager notificationDataManager = NotificationDataManager.getInstance();
401         notificationDataManager.addNewMessageNotification(mAlertEntryMessageWithMuteAction);
402         CarNotificationActionButton button = new CarNotificationActionButton(mContext);
403 
404         // first make sure it is not muted by default
405         assertThat(notificationDataManager.isMessageNotificationMuted(
406                 mAlertEntryMessageWithMuteAction)).isFalse();
407 
408         mNotificationClickHandlerFactory.getMuteClickHandler(button,
409                 mAlertEntryMessageWithMuteAction, mMuteStatusSetter).onClick(mView);
410         try {
411             verify(mMuteActionPendingIntent).send();
412         } catch (PendingIntent.CanceledException e) {
413             // ignore
414         }
415     }
416 
417     @Test
onClickMuteClickHandler_isMuted_muteStatusSetTrue()418     public void onClickMuteClickHandler_isMuted_muteStatusSetTrue() {
419         NotificationDataManager notificationDataManager = NotificationDataManager.getInstance();
420         notificationDataManager.addNewMessageNotification(mAlertEntryMessageHeadsUp);
421         CarNotificationActionButton button = new CarNotificationActionButton(mContext);
422         // first make sure it is not muted by default
423         assertThat(notificationDataManager.isMessageNotificationMuted(
424                 mAlertEntryMessageHeadsUp)).isFalse();
425 
426         mNotificationClickHandlerFactory.getMuteClickHandler(button,
427                 mAlertEntryMessageHeadsUp, mMuteStatusSetter).onClick(mView);
428 
429         verify(mMuteStatusSetter).setMuteStatus(button, /* isMuted= */ true);
430     }
431 
432     @Test
onClickMuteClickHandler_isUnmuted_muteStatusSetFalse()433     public void onClickMuteClickHandler_isUnmuted_muteStatusSetFalse() {
434         NotificationDataManager notificationDataManager = NotificationDataManager.getInstance();
435         notificationDataManager.addNewMessageNotification(mAlertEntryMessageHeadsUp);
436         CarNotificationActionButton button = new CarNotificationActionButton(mContext);
437         // first make sure it is not muted by default
438         assertThat(notificationDataManager.isMessageNotificationMuted(
439                 mAlertEntryMessageHeadsUp)).isFalse();
440         mNotificationClickHandlerFactory.getMuteClickHandler(button,
441                 mAlertEntryMessageHeadsUp, mMuteStatusSetter).onClick(mView);
442         verify(mMuteStatusSetter).setMuteStatus(button, /* isMuted= */ true);
443 
444         mNotificationClickHandlerFactory.getMuteClickHandler(button,
445                 mAlertEntryMessageHeadsUp, mMuteStatusSetter).onClick(mView);
446 
447         verify(mMuteStatusSetter).setMuteStatus(button, /* isMuted= */ false);
448     }
449 
450     @Test
onNotificationClicked_twoListenersRegistered_invokesTwoHandlerCallbacks()451     public void onNotificationClicked_twoListenersRegistered_invokesTwoHandlerCallbacks() {
452         mNotificationClickHandlerFactory.registerClickListener(mListener1);
453         mNotificationClickHandlerFactory.registerClickListener(mListener2);
454         mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1).onClick(mView);
455 
456         verify(mListener1).onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry1);
457         verify(mListener2).onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry1);
458     }
459 
460     @Test
onNotificationClicked_oneListenersUnregistered_invokesRegisteredCallback()461     public void onNotificationClicked_oneListenersUnregistered_invokesRegisteredCallback() {
462         mNotificationClickHandlerFactory.registerClickListener(mListener1);
463         mNotificationClickHandlerFactory.registerClickListener(mListener2);
464         mNotificationClickHandlerFactory.unregisterClickListener(mListener2);
465 
466         mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1).onClick(mView);
467 
468         verify(mListener1).onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry1);
469         verify(mListener2, never()).onNotificationClicked(ActivityManager.START_SUCCESS,
470                 mAlertEntry1);
471     }
472 
473     @Test
onNotificationClicked_duplicateListenersRegistered_invokesCallbackOnce()474     public void onNotificationClicked_duplicateListenersRegistered_invokesCallbackOnce() {
475         mNotificationClickHandlerFactory.registerClickListener(mListener1);
476         mNotificationClickHandlerFactory.registerClickListener(mListener1);
477         mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1).onClick(mView);
478 
479         verify(mListener1).onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry1);
480     }
481 
482     @Test
onNotificationClicked_twoAlertEntriesSubscribe_passesTheRightAlertEntry()483     public void onNotificationClicked_twoAlertEntriesSubscribe_passesTheRightAlertEntry() {
484         mNotificationClickHandlerFactory.registerClickListener(mListener1);
485         View.OnClickListener clickListener1 =
486                 mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1);
487         View.OnClickListener clickListener2 =
488                 mNotificationClickHandlerFactory.getClickHandler(mAlertEntry2);
489 
490         clickListener2.onClick(mView);
491 
492         verify(mListener1, never())
493                 .onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry1);
494         verify(mListener1).onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry2);
495     }
496 
497     @Test
onNotificationClicked_multipleListenersAndAes_passesTheRightAeToAllListeners()498     public void onNotificationClicked_multipleListenersAndAes_passesTheRightAeToAllListeners() {
499         mNotificationClickHandlerFactory.registerClickListener(mListener1);
500         mNotificationClickHandlerFactory.registerClickListener(mListener2);
501         View.OnClickListener clickListener1 =
502                 mNotificationClickHandlerFactory.getClickHandler(mAlertEntry1);
503         View.OnClickListener clickListener2 =
504                 mNotificationClickHandlerFactory.getClickHandler(mAlertEntry2);
505 
506         clickListener2.onClick(mView);
507 
508         verify(mListener1, never())
509                 .onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry1);
510         verify(mListener1).onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry2);
511 
512         verify(mListener2, never())
513                 .onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry1);
514         verify(mListener2).onNotificationClicked(ActivityManager.START_SUCCESS, mAlertEntry2);
515     }
516 
517     @Test
onClearNotifications_groupNotificationWithSummary_clearsSummary()518     public void onClearNotifications_groupNotificationWithSummary_clearsSummary() {
519         List<NotificationGroup> notificationsToClear = new ArrayList<>();
520         NotificationGroup notificationGroup = new NotificationGroup();
521         notificationGroup.setGroupSummaryNotification(mAlertEntry1);
522         notificationGroup.addNotification(mAlertEntry2);
523         notificationGroup.addNotification(mAlertEntry2);
524         notificationGroup.addNotification(mAlertEntry2);
525         notificationsToClear.add(notificationGroup);
526         NotificationVisibility notificationVisibility = NotificationVisibility.obtain(
527                 mAlertEntry1.getKey(),
528                 /* rank= */ -1,
529                 /* count= */ -1,
530                 /* visible= */ true);
531 
532         mNotificationClickHandlerFactory.clearNotifications(notificationsToClear);
533 
534         try {
535             verify(mBarService).onNotificationClear(
536                     mAlertEntry1.getStatusBarNotification().getPackageName(),
537                     mAlertEntry1.getStatusBarNotification().getUser().getIdentifier(),
538                     mAlertEntry1.getStatusBarNotification().getKey(),
539                     NotificationStats.DISMISSAL_SHADE,
540                     NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
541                     notificationVisibility);
542         } catch (RemoteException e) {
543             // ignore.
544         }
545     }
546 
547     @Test
onClearNotifications_groupNotificationWithSummary_clearsAllChildren()548     public void onClearNotifications_groupNotificationWithSummary_clearsAllChildren() {
549         List<NotificationGroup> notificationsToClear = new ArrayList<>();
550         NotificationGroup notificationGroup = new NotificationGroup();
551         notificationGroup.setGroupSummaryNotification(mAlertEntry1);
552         notificationGroup.addNotification(mAlertEntry2);
553         notificationGroup.addNotification(mAlertEntry2);
554         notificationGroup.addNotification(mAlertEntry2);
555         notificationsToClear.add(notificationGroup);
556         NotificationVisibility notificationVisibility = NotificationVisibility.obtain(
557                 mAlertEntry2.getKey(),
558                 /* rank= */ -1,
559                 /* count= */ -1,
560                 /* visible= */ true);
561 
562         mNotificationClickHandlerFactory.clearNotifications(notificationsToClear);
563 
564         try {
565             verify(mBarService, times(3)).onNotificationClear(
566                     mAlertEntry2.getStatusBarNotification().getPackageName(),
567                     mAlertEntry2.getStatusBarNotification().getUser().getIdentifier(),
568                     mAlertEntry2.getStatusBarNotification().getKey(),
569                     NotificationStats.DISMISSAL_SHADE,
570                     NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
571                     notificationVisibility);
572         } catch (RemoteException e) {
573             // ignore.
574         }
575     }
576 
577     @Test
onClearNotifications_groupNotificationWithoutSummary_clearsAllChildren()578     public void onClearNotifications_groupNotificationWithoutSummary_clearsAllChildren() {
579         List<NotificationGroup> notificationsToClear = new ArrayList<>();
580         NotificationGroup notificationGroup = new NotificationGroup();
581         notificationGroup.addNotification(mAlertEntry2);
582         notificationGroup.addNotification(mAlertEntry2);
583         notificationGroup.addNotification(mAlertEntry2);
584         notificationsToClear.add(notificationGroup);
585         NotificationVisibility notificationVisibility = NotificationVisibility.obtain(
586                 mAlertEntry2.getKey(),
587                 /* rank= */ -1,
588                 /* count= */ -1,
589                 /* visible= */ true);
590 
591         mNotificationClickHandlerFactory.clearNotifications(notificationsToClear);
592 
593         try {
594             verify(mBarService, times(3)).onNotificationClear(
595                     mAlertEntry2.getStatusBarNotification().getPackageName(),
596                     mAlertEntry2.getStatusBarNotification().getUser().getIdentifier(),
597                     mAlertEntry2.getStatusBarNotification().getKey(),
598                     NotificationStats.DISMISSAL_SHADE,
599                     NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
600                     notificationVisibility);
601         } catch (RemoteException e) {
602             // ignore.
603         }
604     }
605 }
606