blob: 86b2a44a1acb5b945e4ff7c3ce80db6ea3d0054f [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;
Dave Mankoff4c73e652019-11-14 17:39:08 -050067import com.android.systemui.statusbar.SuperStatusBarViewFactory;
Mark Renouf6b2331c2019-03-21 13:40:08 -040068import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
69import com.android.systemui.statusbar.notification.NotificationActivityStarter;
70import com.android.systemui.statusbar.notification.NotificationEntryManager;
71import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
Mark Renouf6b2331c2019-03-21 13:40:08 -040072import com.android.systemui.statusbar.notification.collection.NotificationEntry;
73import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Lucas Dupinc8f16e82019-09-17 18:24:50 -040074import com.android.systemui.statusbar.policy.KeyguardStateController;
Dave Mankoffc7cf9fc2019-12-19 15:43:20 -050075import com.android.systemui.util.concurrency.FakeExecutor;
76import com.android.systemui.util.time.FakeSystemClock;
Mark Renouf6b2331c2019-03-21 13:40:08 -040077
78import org.junit.Before;
79import org.junit.Test;
80import org.junit.runner.RunWith;
81import org.mockito.Mock;
82import org.mockito.MockitoAnnotations;
83import org.mockito.stubbing.Answer;
84
85import java.util.ArrayList;
86
87@SmallTest
88@RunWith(AndroidTestingRunner.class)
89@TestableLooper.RunWithLooper(setAsMainLooper = true)
90public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
91
92 @Mock
93 private AssistManager mAssistManager;
94 @Mock
95 private NotificationEntryManager mEntryManager;
96 @Mock
97 private ActivityStarter mActivityStarter;
98 @Mock
99 private IStatusBarService mStatusBarService;
100 @Mock
101 private StatusBarStateController mStatusBarStateController;
102 @Mock
Heemin Seog98df6972019-12-05 13:01:38 -0800103 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
104 @Mock
Mark Renouf6b2331c2019-03-21 13:40:08 -0400105 private NotificationRemoteInputManager mRemoteInputManager;
106 @Mock
107 private RemoteInputController mRemoteInputController;
108 @Mock
Heemin Seogbd9bcbf2019-12-04 17:07:32 -0800109 private StatusBar mStatusBar;
Mark Renouf6b2331c2019-03-21 13:40:08 -0400110 @Mock
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400111 private KeyguardStateController mKeyguardStateController;
Mark Renouf6b2331c2019-03-21 13:40:08 -0400112 @Mock
113 private Handler mHandler;
Mark Renouffec45da2019-03-13 13:24:27 -0400114 @Mock
115 private BubbleController mBubbleController;
Heemin Seogba6337f2019-12-10 15:34:37 -0800116 @Mock
117 private ShadeControllerImpl mShadeController;
Mark Renouf6b2331c2019-03-21 13:40:08 -0400118
119 @Mock
120 private ActivityIntentHelper mActivityIntentHelper;
121 @Mock
122 private PendingIntent mContentIntent;
123 @Mock
Mark Renouffec45da2019-03-13 13:24:27 -0400124 private Intent mContentIntentInner;
125 @Mock
Mark Renouf6b2331c2019-03-21 13:40:08 -0400126 private NotificationActivityStarter mNotificationActivityStarter;
Dave Mankoff4c73e652019-11-14 17:39:08 -0500127 @Mock
128 private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
129 @Mock
130 private NotificationPanelView mNotificationPanelView;
Dave Mankoffc7cf9fc2019-12-19 15:43:20 -0500131 private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
Mark Renouf6b2331c2019-03-21 13:40:08 -0400132
133 private NotificationTestHelper mNotificationTestHelper;
Mark Renouffec45da2019-03-13 13:24:27 -0400134 private ExpandableNotificationRow mNotificationRow;
135 private ExpandableNotificationRow mBubbleNotificationRow;
Mark Renouf6b2331c2019-03-21 13:40:08 -0400136
137 private final Answer<Void> mCallOnDismiss = answerVoid(
138 (ActivityStarter.OnDismissAction dismissAction, Runnable cancel,
139 Boolean afterKeyguardGone) -> dismissAction.onDismiss());
140 private ArrayList<NotificationEntry> mActiveNotifications;
141
142 @Before
143 public void setUp() throws Exception {
144 MockitoAnnotations.initMocks(this);
145 when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400146
Mark Renouffec45da2019-03-13 13:24:27 -0400147 when(mContentIntent.isActivity()).thenReturn(true);
148 when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
149 when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400150
Lucas Dupind236ee32019-10-08 15:33:59 -0700151 mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
Mark Renouffec45da2019-03-13 13:24:27 -0400152
153 // Create standard notification with contentIntent
Mark Renouf6b2331c2019-03-21 13:40:08 -0400154 mNotificationRow = mNotificationTestHelper.createRow();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500155 StatusBarNotification sbn = mNotificationRow.getEntry().getSbn();
Mark Renouffec45da2019-03-13 13:24:27 -0400156 sbn.getNotification().contentIntent = mContentIntent;
157 sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
158
159 // Create bubble notification row with contentIntent
160 mBubbleNotificationRow = mNotificationTestHelper.createBubble();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500161 StatusBarNotification bubbleSbn = mBubbleNotificationRow.getEntry().getSbn();
Mark Renouffec45da2019-03-13 13:24:27 -0400162 bubbleSbn.getNotification().contentIntent = mContentIntent;
163 bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
Mark Renouffec45da2019-03-13 13:24:27 -0400164
165 mActiveNotifications = new ArrayList<>();
166 mActiveNotifications.add(mNotificationRow.getEntry());
167 mActiveNotifications.add(mBubbleNotificationRow.getEntry());
Evan Laird181de622019-10-24 09:53:02 -0400168 when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications);
Mark Renouffec45da2019-03-13 13:24:27 -0400169 when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
Dave Mankoff4c73e652019-11-14 17:39:08 -0500170 when(mSuperStatusBarViewFactory.getNotificationPanelView())
171 .thenReturn(mNotificationPanelView);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400172
Dave Mankoff4c73e652019-11-14 17:39:08 -0500173 mNotificationActivityStarter = (new StatusBarNotificationActivityStarter.Builder(
174 getContext(), mock(CommandQueue.class), () -> mAssistManager,
175 mEntryManager, mock(HeadsUpManagerPhone.class),
176 mActivityStarter, mStatusBarService,
Heemin Seog98df6972019-12-05 13:01:38 -0800177 mock(StatusBarStateController.class), mStatusBarKeyguardViewManager,
178 mock(KeyguardManager.class),
Mark Renouf6b2331c2019-03-21 13:40:08 -0400179 mock(IDreamManager.class), mRemoteInputManager,
180 mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class),
Dave Mankoff4c73e652019-11-14 17:39:08 -0500181 mock(NotificationLockscreenUserManager.class),
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400182 mKeyguardStateController,
Mark Renouf6b2331c2019-03-21 13:40:08 -0400183 mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
Dave Mankoffc7cf9fc2019-12-19 15:43:20 -0500184 mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor,
185 mActivityIntentHelper, mBubbleController, mShadeController,
186 mSuperStatusBarViewFactory))
Heemin Seogbd9bcbf2019-12-04 17:07:32 -0800187 .setStatusBar(mStatusBar)
Dave Mankoff4c73e652019-11-14 17:39:08 -0500188 .setNotificationPresenter(mock(NotificationPresenter.class))
189 .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
190 .build();
Mark Renouf6b2331c2019-03-21 13:40:08 -0400191
192 // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
193 doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
194 any(ActivityStarter.OnDismissAction.class), any(), anyBoolean());
195
196 // set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg
197 doAnswer(answerVoid(Runnable::run))
Heemin Seog98df6972019-12-05 13:01:38 -0800198 .when(mStatusBarKeyguardViewManager)
199 .addAfterKeyguardGoneRunnable(any(Runnable.class));
Mark Renouf6b2331c2019-03-21 13:40:08 -0400200
201 // set up addPostCollapseAction to synchronously invoke the Runnable arg
202 doAnswer(answerVoid(Runnable::run))
Heemin Seogba6337f2019-12-10 15:34:37 -0800203 .when(mShadeController).addPostCollapseAction(any(Runnable.class));
Mark Renouf6b2331c2019-03-21 13:40:08 -0400204
205 // set up Handler to synchronously invoke the Runnable arg
206 doAnswer(answerVoid(Runnable::run))
207 .when(mHandler).post(any(Runnable.class));
Mark Renouffec45da2019-03-13 13:24:27 -0400208
209 doAnswer(answerVoid(Runnable::run))
210 .when(mHandler).postAtFrontOfQueue(any(Runnable.class));
Mark Renouf6b2331c2019-03-21 13:40:08 -0400211 }
212
213 @Test
Mark Renouffec45da2019-03-13 13:24:27 -0400214 public void testOnNotificationClicked_keyGuardShowing()
Mark Renouf6b2331c2019-03-21 13:40:08 -0400215 throws PendingIntent.CanceledException, RemoteException {
216 // Given
Ned Burns1c2b85a42019-11-14 15:37:03 -0500217 StatusBarNotification sbn = mNotificationRow.getEntry().getSbn();
Mark Renouffec45da2019-03-13 13:24:27 -0400218 sbn.getNotification().contentIntent = mContentIntent;
219 sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
220
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400221 when(mKeyguardStateController.isShowing()).thenReturn(true);
Heemin Seogbd9bcbf2019-12-04 17:07:32 -0800222 when(mStatusBar.isOccluded()).thenReturn(true);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400223
224 // When
Mark Renouffec45da2019-03-13 13:24:27 -0400225 mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow);
Mark Renouf6b2331c2019-03-21 13:40:08 -0400226
227 // Then
Heemin Seogba6337f2019-12-10 15:34:37 -0800228 verify(mShadeController, atLeastOnce()).collapsePanel();
Mark Renouf6b2331c2019-03-21 13:40:08 -0400229
230 verify(mContentIntent).sendAndReturnResult(
231 any(Context.class),
232 anyInt() /* code */,
233 any() /* fillInIntent */,
234 any() /* PendingIntent.OnFinished */,
235 any() /* Handler */,
236 any() /* requiredPermission */,
237 any() /* Bundle options */);
238
239 verify(mAssistManager).hideAssist();
240
241 verify(mStatusBarService).onNotificationClick(
Mark Renouffec45da2019-03-13 13:24:27 -0400242 eq(sbn.getKey()), any(NotificationVisibility.class));
Mark Renouf6b2331c2019-03-21 13:40:08 -0400243
244 // Notification is removed due to FLAG_AUTO_CANCEL
Mady Mellorc2ff0112019-03-28 14:18:06 -0700245 verify(mEntryManager).performRemoveNotification(eq(sbn), eq(REASON_CLICK));
Mark Renouffec45da2019-03-13 13:24:27 -0400246 }
247
248 @Test
249 public void testOnNotificationClicked_bubble_noContentIntent_noKeyGuard()
250 throws RemoteException {
Ned Burns1c2b85a42019-11-14 15:37:03 -0500251 StatusBarNotification sbn = mBubbleNotificationRow.getEntry().getSbn();
Mark Renouffec45da2019-03-13 13:24:27 -0400252
253 // Given
254 sbn.getNotification().contentIntent = null;
255
256 // When
257 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
258
259 // Then
260 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
261
262 // This is called regardless, and simply short circuits when there is nothing to do.
Heemin Seogba6337f2019-12-10 15:34:37 -0800263 verify(mShadeController, atLeastOnce()).collapsePanel();
Mark Renouffec45da2019-03-13 13:24:27 -0400264
265 verify(mAssistManager).hideAssist();
266
267 verify(mStatusBarService).onNotificationClick(
268 eq(sbn.getKey()), any(NotificationVisibility.class));
269
270 // The content intent should NOT be sent on click.
271 verifyZeroInteractions(mContentIntent);
272
273 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700274 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouffec45da2019-03-13 13:24:27 -0400275 }
276
277 @Test
278 public void testOnNotificationClicked_bubble_noContentIntent_keyGuardShowing()
279 throws RemoteException {
Ned Burns1c2b85a42019-11-14 15:37:03 -0500280 StatusBarNotification sbn = mBubbleNotificationRow.getEntry().getSbn();
Mark Renouffec45da2019-03-13 13:24:27 -0400281
282 // Given
283 sbn.getNotification().contentIntent = null;
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400284 when(mKeyguardStateController.isShowing()).thenReturn(true);
Heemin Seogbd9bcbf2019-12-04 17:07:32 -0800285 when(mStatusBar.isOccluded()).thenReturn(true);
Mark Renouffec45da2019-03-13 13:24:27 -0400286
287 // When
288 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
289
290 // Then
291 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
292
Heemin Seogba6337f2019-12-10 15:34:37 -0800293 verify(mShadeController, atLeastOnce()).collapsePanel();
Mark Renouffec45da2019-03-13 13:24:27 -0400294
295 verify(mAssistManager).hideAssist();
296
297 verify(mStatusBarService).onNotificationClick(
298 eq(sbn.getKey()), any(NotificationVisibility.class));
299
300 // The content intent should NOT be sent on click.
301 verifyZeroInteractions(mContentIntent);
302
303 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700304 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouffec45da2019-03-13 13:24:27 -0400305 }
306
307 @Test
308 public void testOnNotificationClicked_bubble_withContentIntent_keyGuardShowing()
309 throws RemoteException {
Ned Burns1c2b85a42019-11-14 15:37:03 -0500310 StatusBarNotification sbn = mBubbleNotificationRow.getEntry().getSbn();
Mark Renouffec45da2019-03-13 13:24:27 -0400311
312 // Given
313 sbn.getNotification().contentIntent = mContentIntent;
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400314 when(mKeyguardStateController.isShowing()).thenReturn(true);
Heemin Seogbd9bcbf2019-12-04 17:07:32 -0800315 when(mStatusBar.isOccluded()).thenReturn(true);
Mark Renouffec45da2019-03-13 13:24:27 -0400316
317 // When
318 mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
319
320 // Then
321 verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
322
Heemin Seogba6337f2019-12-10 15:34:37 -0800323 verify(mShadeController, atLeastOnce()).collapsePanel();
Mark Renouffec45da2019-03-13 13:24:27 -0400324
325 verify(mAssistManager).hideAssist();
326
327 verify(mStatusBarService).onNotificationClick(
328 eq(sbn.getKey()), any(NotificationVisibility.class));
329
330 // The content intent should NOT be sent on click.
331 verify(mContentIntent).getIntent();
332 verify(mContentIntent).isActivity();
333 verifyNoMoreInteractions(mContentIntent);
334
335 // Notification should not be cancelled.
Mady Mellorc2ff0112019-03-28 14:18:06 -0700336 verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
Mark Renouf6b2331c2019-03-21 13:40:08 -0400337 }
338}