blob: 5a6f27dcfaae51c6288068d30efa1616f2fbf451 [file] [log] [blame]
Mark Renouf6b2331c2019-03-21 13:40:08 -04001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.statusbar.phone;
18
Mady Mellorc2ff0112019-03-28 14:18:06 -070019import static android.service.notification.NotificationListenerService.REASON_CLICK;
20
Mark Renouf6b2331c2019-03-21 13:40:08 -040021import static org.mockito.AdditionalAnswers.answerVoid;
22import static org.mockito.ArgumentMatchers.any;
23import static org.mockito.ArgumentMatchers.anyBoolean;
24import static org.mockito.ArgumentMatchers.anyInt;
25import static org.mockito.ArgumentMatchers.eq;
26import static org.mockito.Mockito.atLeastOnce;
27import static org.mockito.Mockito.doAnswer;
28import static org.mockito.Mockito.mock;
Mark Renouffec45da2019-03-13 13:24:27 -040029import static org.mockito.Mockito.never;
Mark Renouf6b2331c2019-03-21 13:40:08 -040030import static org.mockito.Mockito.verify;
Mark Renouffec45da2019-03-13 13:24:27 -040031import static org.mockito.Mockito.verifyNoMoreInteractions;
32import static org.mockito.Mockito.verifyZeroInteractions;
Mark Renouf6b2331c2019-03-21 13:40:08 -040033import static org.mockito.Mockito.when;
34
35import android.app.KeyguardManager;
36import android.app.Notification;
37import android.app.PendingIntent;
38import android.content.Context;
39import android.content.Intent;
40import android.os.Handler;
41import android.os.RemoteException;
42import android.os.UserHandle;
43import android.service.dreams.IDreamManager;
44import android.service.notification.StatusBarNotification;
45import android.testing.AndroidTestingRunner;
46import android.testing.TestableLooper;
47
48import androidx.test.filters.SmallTest;
49
50import com.android.internal.logging.MetricsLogger;
51import com.android.internal.statusbar.IStatusBarService;
52import com.android.internal.statusbar.NotificationVisibility;
53import com.android.internal.widget.LockPatternUtils;
54import com.android.systemui.ActivityIntentHelper;
55import com.android.systemui.SysuiTestCase;
56import com.android.systemui.assist.AssistManager;
Mark Renouffec45da2019-03-13 13:24:27 -040057import com.android.systemui.bubbles.BubbleController;
Mark Renouf6b2331c2019-03-21 13:40:08 -040058import com.android.systemui.plugins.ActivityStarter;
59import com.android.systemui.plugins.statusbar.StatusBarStateController;
60import com.android.systemui.statusbar.CommandQueue;
61import com.android.systemui.statusbar.NotificationLockscreenUserManager;
62import com.android.systemui.statusbar.NotificationPresenter;
63import com.android.systemui.statusbar.NotificationRemoteInputManager;
64import com.android.systemui.statusbar.NotificationTestHelper;
65import com.android.systemui.statusbar.RemoteInputController;
66import com.android.systemui.statusbar.StatusBarState;
67import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
68import com.android.systemui.statusbar.notification.NotificationActivityStarter;
69import com.android.systemui.statusbar.notification.NotificationEntryManager;
70import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
71import com.android.systemui.statusbar.notification.collection.NotificationData;
72import com.android.systemui.statusbar.notification.collection.NotificationEntry;
73import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
74import com.android.systemui.statusbar.policy.KeyguardMonitor;
75
76import org.junit.Before;
77import org.junit.Test;
78import org.junit.runner.RunWith;
79import org.mockito.Mock;
80import org.mockito.MockitoAnnotations;
81import org.mockito.stubbing.Answer;
82
83import java.util.ArrayList;
84
85@SmallTest
86@RunWith(AndroidTestingRunner.class)
87@TestableLooper.RunWithLooper(setAsMainLooper = true)
88public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
89
90 @Mock
91 private AssistManager mAssistManager;
92 @Mock
93 private NotificationEntryManager mEntryManager;
94 @Mock
95 private ActivityStarter mActivityStarter;
96 @Mock
97 private IStatusBarService mStatusBarService;
98 @Mock
99 private StatusBarStateController mStatusBarStateController;
100 @Mock
101 private NotificationRemoteInputManager mRemoteInputManager;
102 @Mock
103 private RemoteInputController mRemoteInputController;
104 @Mock
105 private ShadeController mShadeController;
106 @Mock
107 private KeyguardMonitor mKeyguardMonitor;
108 @Mock
109 private Handler mHandler;
Mark Renouffec45da2019-03-13 13:24:27 -0400110 @Mock
111 private BubbleController mBubbleController;
Mark Renouf6b2331c2019-03-21 13:40:08 -0400112
113 @Mock
114 private ActivityIntentHelper mActivityIntentHelper;
115 @Mock
116 private PendingIntent mContentIntent;
117 @Mock
Mark Renouffec45da2019-03-13 13:24:27 -0400118 private Intent mContentIntentInner;
119 @Mock
Mark Renouf6b2331c2019-03-21 13:40:08 -0400120 private NotificationData mNotificationData;
Mark Renouf6b2331c2019-03-21 13:40:08 -0400121
122 private NotificationActivityStarter mNotificationActivityStarter;
123
124 private NotificationTestHelper mNotificationTestHelper;
Mark Renouffec45da2019-03-13 13:24:27 -0400125 private ExpandableNotificationRow mNotificationRow;
126 private ExpandableNotificationRow mBubbleNotificationRow;
Mark Renouf6b2331c2019-03-21 13:40:08 -0400127
128 private final Answer<Void> mCallOnDismiss = answerVoid(
129 (ActivityStarter.OnDismissAction dismissAction, Runnable cancel,
130 Boolean afterKeyguardGone) -> dismissAction.onDismiss());
131 private ArrayList<NotificationEntry> mActiveNotifications;
132
133 @Before
134 public void setUp() throws Exception {
135 MockitoAnnotations.initMocks(this);
136 when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
137 when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
138
Mark Renouffec45da2019-03-13 13:24:27 -0400139 when(mContentIntent.isActivity()).thenReturn(true);
140 when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
141 when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400142
143 mNotificationTestHelper = new NotificationTestHelper(mContext);
Mark Renouffec45da2019-03-13 13:24:27 -0400144
145 // Create standard notification with contentIntent
Mark Renouf6b2331c2019-03-21 13:40:08 -0400146 mNotificationRow = mNotificationTestHelper.createRow();
Mark Renouffec45da2019-03-13 13:24:27 -0400147 StatusBarNotification sbn = mNotificationRow.getStatusBarNotification();
148 sbn.getNotification().contentIntent = mContentIntent;
149 sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
150
151 // Create bubble notification row with contentIntent
152 mBubbleNotificationRow = mNotificationTestHelper.createBubble();
153 StatusBarNotification bubbleSbn = mBubbleNotificationRow.getStatusBarNotification();
154 bubbleSbn.getNotification().contentIntent = mContentIntent;
155 bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
Mark Renouffec45da2019-03-13 13:24:27 -0400156
157 mActiveNotifications = new ArrayList<>();
158 mActiveNotifications.add(mNotificationRow.getEntry());
159 mActiveNotifications.add(mBubbleNotificationRow.getEntry());
160 when(mNotificationData.getActiveNotifications()).thenReturn(mActiveNotifications);
161 when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400162
163 mNotificationActivityStarter = new StatusBarNotificationActivityStarter(getContext(),
164 mock(CommandQueue.class), mAssistManager, mock(NotificationPanelView.class),
165 mock(NotificationPresenter.class), mEntryManager, mock(HeadsUpManagerPhone.class),
166 mActivityStarter, mock(ActivityLaunchAnimator.class), mStatusBarService,
167 mock(StatusBarStateController.class), mock(KeyguardManager.class),
168 mock(IDreamManager.class), mRemoteInputManager,
169 mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class),
170 mock(NotificationLockscreenUserManager.class), mShadeController, mKeyguardMonitor,
171 mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
Mark Renouffec45da2019-03-13 13:24:27 -0400172 mock(LockPatternUtils.class), mHandler, mHandler, mActivityIntentHelper,
173 mBubbleController);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400174
175 // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
176 doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
177 any(ActivityStarter.OnDismissAction.class), any(), anyBoolean());
178
179 // set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg
180 doAnswer(answerVoid(Runnable::run))
181 .when(mShadeController).addAfterKeyguardGoneRunnable(any(Runnable.class));
182
183 // set up addPostCollapseAction to synchronously invoke the Runnable arg
184 doAnswer(answerVoid(Runnable::run))
185 .when(mShadeController).addPostCollapseAction(any(Runnable.class));
186
187 // set up Handler to synchronously invoke the Runnable arg
188 doAnswer(answerVoid(Runnable::run))
189 .when(mHandler).post(any(Runnable.class));
Mark Renouffec45da2019-03-13 13:24:27 -0400190
191 doAnswer(answerVoid(Runnable::run))
192 .when(mHandler).postAtFrontOfQueue(any(Runnable.class));
Mark Renouf6b2331c2019-03-21 13:40:08 -0400193 }
194
195 @Test
Mark Renouffec45da2019-03-13 13:24:27 -0400196 public void testOnNotificationClicked_keyGuardShowing()
Mark Renouf6b2331c2019-03-21 13:40:08 -0400197 throws PendingIntent.CanceledException, RemoteException {
198 // Given
Mark Renouffec45da2019-03-13 13:24:27 -0400199 StatusBarNotification sbn = mNotificationRow.getStatusBarNotification();
200 sbn.getNotification().contentIntent = mContentIntent;
201 sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
202
Mark Renouf6b2331c2019-03-21 13:40:08 -0400203 when(mKeyguardMonitor.isShowing()).thenReturn(true);
204 when(mShadeController.isOccluded()).thenReturn(true);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400205
206 // When
Mark Renouffec45da2019-03-13 13:24:27 -0400207 mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400208
209 // Then
Mark Renouf6b2331c2019-03-21 13:40:08 -0400210 verify(mShadeController, atLeastOnce()).collapsePanel();
211
212 verify(mContentIntent).sendAndReturnResult(
213 any(Context.class),
214 anyInt() /* code */,
215 any() /* fillInIntent */,
216 any() /* PendingIntent.OnFinished */,
217 any() /* Handler */,
218 any() /* requiredPermission */,
219 any() /* Bundle options */);
220
221 verify(mAssistManager).hideAssist();
222
223 verify(mStatusBarService).onNotificationClick(
Mark Renouffec45da2019-03-13 13:24:27 -0400224 eq(sbn.getKey()), any(NotificationVisibility.class));
Mark Renouf6b2331c2019-03-21 13:40:08 -0400225
226 // Notification is removed due to FLAG_AUTO_CANCEL
Mady Mellorc2ff0112019-03-28 14:18:06 -0700227 verify(mEntryManager).performRemoveNotification(eq(sbn), eq(REASON_CLICK));
Mark Renouffec45da2019-03-13 13:24:27 -0400228 }
229
230 @Test
231 public void testOnNotificationClicked_bubble_noContentIntent_noKeyGuard()
232 throws RemoteException {
233 StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
234
235 // Given
236 sbn.getNotification().contentIntent = null;
237
238 // When
239 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
240
241 // Then
242 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
243
244 // This is called regardless, and simply short circuits when there is nothing to do.
245 verify(mShadeController, atLeastOnce()).collapsePanel();
246
247 verify(mAssistManager).hideAssist();
248
249 verify(mStatusBarService).onNotificationClick(
250 eq(sbn.getKey()), any(NotificationVisibility.class));
251
252 // The content intent should NOT be sent on click.
253 verifyZeroInteractions(mContentIntent);
254
255 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700256 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouffec45da2019-03-13 13:24:27 -0400257 }
258
259 @Test
260 public void testOnNotificationClicked_bubble_noContentIntent_keyGuardShowing()
261 throws RemoteException {
262 StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
263
264 // Given
265 sbn.getNotification().contentIntent = null;
266 when(mKeyguardMonitor.isShowing()).thenReturn(true);
267 when(mShadeController.isOccluded()).thenReturn(true);
268
269 // When
270 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
271
272 // Then
273 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
274
275 verify(mShadeController, atLeastOnce()).collapsePanel();
276
277 verify(mAssistManager).hideAssist();
278
279 verify(mStatusBarService).onNotificationClick(
280 eq(sbn.getKey()), any(NotificationVisibility.class));
281
282 // The content intent should NOT be sent on click.
283 verifyZeroInteractions(mContentIntent);
284
285 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700286 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouffec45da2019-03-13 13:24:27 -0400287 }
288
289 @Test
290 public void testOnNotificationClicked_bubble_withContentIntent_keyGuardShowing()
291 throws RemoteException {
292 StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
293
294 // Given
295 sbn.getNotification().contentIntent = mContentIntent;
296 when(mKeyguardMonitor.isShowing()).thenReturn(true);
297 when(mShadeController.isOccluded()).thenReturn(true);
298
299 // When
300 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
301
302 // Then
303 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
304
305 verify(mShadeController, atLeastOnce()).collapsePanel();
306
307 verify(mAssistManager).hideAssist();
308
309 verify(mStatusBarService).onNotificationClick(
310 eq(sbn.getKey()), any(NotificationVisibility.class));
311
312 // The content intent should NOT be sent on click.
313 verify(mContentIntent).getIntent();
314 verify(mContentIntent).isActivity();
315 verifyNoMoreInteractions(mContentIntent);
316
317 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700318 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouf6b2331c2019-03-21 13:40:08 -0400319 }
320}