blob: 06d76ebcff2852ae5078938f95d33726638599e2 [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;
156 // Do what BubbleController's NotificationEntryListener#onPendingEntryAdded does:
Mark Renouffec45da2019-03-13 13:24:27 -0400157 mBubbleNotificationRow.getEntry().setShowInShadeWhenBubble(true);
158
159 mActiveNotifications = new ArrayList<>();
160 mActiveNotifications.add(mNotificationRow.getEntry());
161 mActiveNotifications.add(mBubbleNotificationRow.getEntry());
162 when(mNotificationData.getActiveNotifications()).thenReturn(mActiveNotifications);
163 when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400164
165 mNotificationActivityStarter = new StatusBarNotificationActivityStarter(getContext(),
166 mock(CommandQueue.class), mAssistManager, mock(NotificationPanelView.class),
167 mock(NotificationPresenter.class), mEntryManager, mock(HeadsUpManagerPhone.class),
168 mActivityStarter, mock(ActivityLaunchAnimator.class), mStatusBarService,
169 mock(StatusBarStateController.class), mock(KeyguardManager.class),
170 mock(IDreamManager.class), mRemoteInputManager,
171 mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class),
172 mock(NotificationLockscreenUserManager.class), mShadeController, mKeyguardMonitor,
173 mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
Mark Renouffec45da2019-03-13 13:24:27 -0400174 mock(LockPatternUtils.class), mHandler, mHandler, mActivityIntentHelper,
175 mBubbleController);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400176
177 // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
178 doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
179 any(ActivityStarter.OnDismissAction.class), any(), anyBoolean());
180
181 // set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg
182 doAnswer(answerVoid(Runnable::run))
183 .when(mShadeController).addAfterKeyguardGoneRunnable(any(Runnable.class));
184
185 // set up addPostCollapseAction to synchronously invoke the Runnable arg
186 doAnswer(answerVoid(Runnable::run))
187 .when(mShadeController).addPostCollapseAction(any(Runnable.class));
188
189 // set up Handler to synchronously invoke the Runnable arg
190 doAnswer(answerVoid(Runnable::run))
191 .when(mHandler).post(any(Runnable.class));
Mark Renouffec45da2019-03-13 13:24:27 -0400192
193 doAnswer(answerVoid(Runnable::run))
194 .when(mHandler).postAtFrontOfQueue(any(Runnable.class));
Mark Renouf6b2331c2019-03-21 13:40:08 -0400195 }
196
197 @Test
Mark Renouffec45da2019-03-13 13:24:27 -0400198 public void testOnNotificationClicked_keyGuardShowing()
Mark Renouf6b2331c2019-03-21 13:40:08 -0400199 throws PendingIntent.CanceledException, RemoteException {
200 // Given
Mark Renouffec45da2019-03-13 13:24:27 -0400201 StatusBarNotification sbn = mNotificationRow.getStatusBarNotification();
202 sbn.getNotification().contentIntent = mContentIntent;
203 sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
204
Mark Renouf6b2331c2019-03-21 13:40:08 -0400205 when(mKeyguardMonitor.isShowing()).thenReturn(true);
206 when(mShadeController.isOccluded()).thenReturn(true);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400207
208 // When
Mark Renouffec45da2019-03-13 13:24:27 -0400209 mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400210
211 // Then
Mark Renouf6b2331c2019-03-21 13:40:08 -0400212 verify(mShadeController, atLeastOnce()).collapsePanel();
213
214 verify(mContentIntent).sendAndReturnResult(
215 any(Context.class),
216 anyInt() /* code */,
217 any() /* fillInIntent */,
218 any() /* PendingIntent.OnFinished */,
219 any() /* Handler */,
220 any() /* requiredPermission */,
221 any() /* Bundle options */);
222
223 verify(mAssistManager).hideAssist();
224
225 verify(mStatusBarService).onNotificationClick(
Mark Renouffec45da2019-03-13 13:24:27 -0400226 eq(sbn.getKey()), any(NotificationVisibility.class));
Mark Renouf6b2331c2019-03-21 13:40:08 -0400227
228 // Notification is removed due to FLAG_AUTO_CANCEL
Mady Mellorc2ff0112019-03-28 14:18:06 -0700229 verify(mEntryManager).performRemoveNotification(eq(sbn), eq(REASON_CLICK));
Mark Renouffec45da2019-03-13 13:24:27 -0400230 }
231
232 @Test
233 public void testOnNotificationClicked_bubble_noContentIntent_noKeyGuard()
234 throws RemoteException {
235 StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
236
237 // Given
238 sbn.getNotification().contentIntent = null;
239
240 // When
241 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
242
243 // Then
244 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
245
246 // This is called regardless, and simply short circuits when there is nothing to do.
247 verify(mShadeController, atLeastOnce()).collapsePanel();
248
249 verify(mAssistManager).hideAssist();
250
251 verify(mStatusBarService).onNotificationClick(
252 eq(sbn.getKey()), any(NotificationVisibility.class));
253
254 // The content intent should NOT be sent on click.
255 verifyZeroInteractions(mContentIntent);
256
257 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700258 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouffec45da2019-03-13 13:24:27 -0400259 }
260
261 @Test
262 public void testOnNotificationClicked_bubble_noContentIntent_keyGuardShowing()
263 throws RemoteException {
264 StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
265
266 // Given
267 sbn.getNotification().contentIntent = null;
268 when(mKeyguardMonitor.isShowing()).thenReturn(true);
269 when(mShadeController.isOccluded()).thenReturn(true);
270
271 // When
272 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
273
274 // Then
275 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
276
277 verify(mShadeController, atLeastOnce()).collapsePanel();
278
279 verify(mAssistManager).hideAssist();
280
281 verify(mStatusBarService).onNotificationClick(
282 eq(sbn.getKey()), any(NotificationVisibility.class));
283
284 // The content intent should NOT be sent on click.
285 verifyZeroInteractions(mContentIntent);
286
287 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700288 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouffec45da2019-03-13 13:24:27 -0400289 }
290
291 @Test
292 public void testOnNotificationClicked_bubble_withContentIntent_keyGuardShowing()
293 throws RemoteException {
294 StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
295
296 // Given
297 sbn.getNotification().contentIntent = mContentIntent;
298 when(mKeyguardMonitor.isShowing()).thenReturn(true);
299 when(mShadeController.isOccluded()).thenReturn(true);
300
301 // When
302 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
303
304 // Then
305 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
306
307 verify(mShadeController, atLeastOnce()).collapsePanel();
308
309 verify(mAssistManager).hideAssist();
310
311 verify(mStatusBarService).onNotificationClick(
312 eq(sbn.getKey()), any(NotificationVisibility.class));
313
314 // The content intent should NOT be sent on click.
315 verify(mContentIntent).getIntent();
316 verify(mContentIntent).isActivity();
317 verifyNoMoreInteractions(mContentIntent);
318
319 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700320 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouf6b2331c2019-03-21 13:40:08 -0400321 }
322}