blob: 7829830329237944bf1dfc0ba7064af09538510c [file] [log] [blame]
Matthew Ng86a436e2018-10-26 16:00:53 -07001/*
2 * Copyright (C) 2018 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
19import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
20import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
21import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
22
23import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
24import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
25import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
Matthew Ngb831fb42019-01-30 11:20:48 -080026import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM;
Matthew Ng86a436e2018-10-26 16:00:53 -070027
28import static org.junit.Assert.assertEquals;
29import static org.junit.Assert.assertFalse;
30import static org.junit.Assert.assertNull;
31import static org.junit.Assert.assertTrue;
32import static org.mockito.Mockito.anyFloat;
Matthew Ng86a436e2018-10-26 16:00:53 -070033import static org.mockito.Mockito.doReturn;
34import static org.mockito.Mockito.mock;
35import static org.mockito.Mockito.never;
36import static org.mockito.Mockito.reset;
37import static org.mockito.Mockito.spy;
38import static org.mockito.Mockito.times;
39import static org.mockito.Mockito.verify;
40
Matthew Nga62b62082018-12-03 15:58:52 -080041import android.content.Context;
Matthew Ng86a436e2018-10-26 16:00:53 -070042import android.content.res.Resources;
Matthew Ng86a436e2018-10-26 16:00:53 -070043import android.testing.AndroidTestingRunner;
44import android.testing.TestableLooper.RunWithLooper;
45import android.view.MotionEvent;
46import android.view.View;
47
Brett Chabot84151d92019-02-27 15:37:59 -080048import androidx.test.filters.SmallTest;
49
Jason Monka716bac2018-12-05 15:48:21 -050050import com.android.systemui.R;
51import com.android.systemui.SysuiTestCase;
52import com.android.systemui.recents.OverviewProxyService;
53import com.android.systemui.shared.recents.IOverviewProxy;
54
Matthew Ng86a436e2018-10-26 16:00:53 -070055import org.junit.Before;
56import org.junit.Test;
57import org.junit.runner.RunWith;
58import org.mockito.ArgumentCaptor;
59import org.mockito.MockitoAnnotations;
60
61/** atest QuickStepControllerTest */
62@RunWith(AndroidTestingRunner.class)
Jason Monka716bac2018-12-05 15:48:21 -050063@RunWithLooper
Matthew Ng86a436e2018-10-26 16:00:53 -070064@SmallTest
65public class QuickStepControllerTest extends SysuiTestCase {
Matthew Ngb9c84282018-12-06 17:15:27 -080066 private static final int NAVBAR_WIDTH = 1000;
67 private static final int NAVBAR_HEIGHT = 300;
Matthew Ngb9c84282018-12-06 17:15:27 -080068
Matthew Ng86a436e2018-10-26 16:00:53 -070069 private QuickStepController mController;
70 private NavigationBarView mNavigationBarView;
71 private StatusBar mStatusBar;
72 private OverviewProxyService mProxyService;
73 private IOverviewProxy mProxy;
74 private Resources mResources;
75
76 @Before
77 public void setup() {
78 MockitoAnnotations.initMocks(this);
79 final ButtonDispatcher backButton = mock(ButtonDispatcher.class);
80 mResources = mock(Resources.class);
81
82 mProxyService = mock(OverviewProxyService.class);
83 mProxy = mock(IOverviewProxy.Stub.class);
84 doReturn(mProxy).when(mProxyService).getProxy();
Matthew Ngd04676d2018-12-04 14:54:48 -080085 doReturn(true).when(mProxyService).shouldShowSwipeUpUI();
Matthew Ng86a436e2018-10-26 16:00:53 -070086 mDependency.injectTestDependency(OverviewProxyService.class, mProxyService);
87
88 mStatusBar = mock(StatusBar.class);
89 doReturn(false).when(mStatusBar).isKeyguardShowing();
90 mContext.putComponent(StatusBar.class, mStatusBar);
91
92 mNavigationBarView = mock(NavigationBarView.class);
93 doReturn(false).when(mNavigationBarView).inScreenPinning();
94 doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
95 doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
96 doReturn(HIT_TARGET_NONE).when(mNavigationBarView).getDownHitTarget();
Matthew Ngb831fb42019-01-30 11:20:48 -080097 doReturn(WINDOW_TARGET_BOTTOM).when(mNavigationBarView).getWindowTarget();
Matthew Ng86a436e2018-10-26 16:00:53 -070098 doReturn(backButton).when(mNavigationBarView).getBackButton();
99 doReturn(mResources).when(mNavigationBarView).getResources();
Matthew Ng0548fbc2019-01-11 12:24:13 -0800100 doReturn(mContext).when(mNavigationBarView).getContext();
Matthew Ng86a436e2018-10-26 16:00:53 -0700101
102 mController = new QuickStepController(mContext);
103 mController.setComponents(mNavigationBarView);
104 mController.setBarState(false /* isRTL */, NAV_BAR_BOTTOM);
105 }
106
107 @Test
108 public void testNoActionsNoGestures() throws Exception {
109 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
110 assertFalse(mController.onInterceptTouchEvent(ev));
111 verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
112 assertNull(mController.getCurrentAction());
113 }
114
115 @Test
Matthew Ngd04676d2018-12-04 14:54:48 -0800116 public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
117 doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
118 mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800119 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
120 null /* rightEdgeSwipe */);
Matthew Ngd04676d2018-12-04 14:54:48 -0800121
122 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
123 assertFalse(mController.onInterceptTouchEvent(ev));
124 verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
125 assertNull(mController.getCurrentAction());
126 }
127
128 @Test
Matthew Ng86a436e2018-10-26 16:00:53 -0700129 public void testHasActionDetectGesturesTouchdown() throws Exception {
130 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
131
132 // Add enabled gesture action
133 NavigationGestureAction action = mockAction(true);
134 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800135 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
136 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700137
138 assertFalse(mController.onInterceptTouchEvent(ev));
139 verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
140 verify(action, times(1)).reset();
141 verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
142 verify(mProxy, times(1)).onMotionEvent(ev);
143 assertNull(mController.getCurrentAction());
144 }
145
146 @Test
147 public void testProxyDisconnectedNoGestures() throws Exception {
148 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
149
150 // Add enabled gesture action
151 mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800152 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
153 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700154
155 // Set the gesture on deadzone
156 doReturn(null).when(mProxyService).getProxy();
157
158 assertFalse(mController.onInterceptTouchEvent(ev));
159 verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
160 assertNull(mController.getCurrentAction());
161 }
162
163 @Test
164 public void testNoActionsNoGesturesOverDeadzone() throws Exception {
165 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
166
167 // Touched over deadzone
168 doReturn(HIT_TARGET_DEAD_ZONE).when(mNavigationBarView).getDownHitTarget();
169
170 assertTrue(mController.onInterceptTouchEvent(ev));
171 verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
172 assertNull(mController.getCurrentAction());
173 }
174
175 @Test
176 public void testOnTouchIgnoredDownEventAfterOnIntercept() {
177 mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800178 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
179 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700180
181 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
182 assertFalse(touch(ev));
183 verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
184
185 // OnTouch event for down is ignored, so requestUnbufferedDispatch ran once from before
186 assertFalse(mNavigationBarView.onTouchEvent(ev));
187 verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
188 }
189
190 @Test
191 public void testGesturesCallCorrectAction() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800192 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
193 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
194
Matthew Ng86a436e2018-10-26 16:00:53 -0700195 NavigationGestureAction swipeUp = mockAction(true);
196 NavigationGestureAction swipeDown = mockAction(true);
197 NavigationGestureAction swipeLeft = mockAction(true);
198 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800199 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
200 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700201
202 // Swipe Up
203 assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
204 // Swipe Down
205 assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
206 // Swipe Left
Matthew Ngb9c84282018-12-06 17:15:27 -0800207 assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700208 // Swipe Right
Matthew Ngb9c84282018-12-06 17:15:27 -0800209 assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 5);
Matthew Ng86a436e2018-10-26 16:00:53 -0700210 }
211
212 @Test
213 public void testGesturesCallCorrectActionLandscape() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800214 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
215 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
216
Matthew Ng86a436e2018-10-26 16:00:53 -0700217 NavigationGestureAction swipeUp = mockAction(true);
218 NavigationGestureAction swipeDown = mockAction(true);
219 NavigationGestureAction swipeLeft = mockAction(true);
220 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800221 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
222 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700223
224 // In landscape
225 mController.setBarState(false /* isRTL */, NAV_BAR_RIGHT);
226
227 // Swipe Up
228 assertGestureTriggersAction(swipeRight, 1, 100, 5, 1);
229 // Swipe Down
Matthew Ngb9c84282018-12-06 17:15:27 -0800230 assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
Matthew Ng86a436e2018-10-26 16:00:53 -0700231 // Swipe Left
232 assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
233 // Swipe Right
234 assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
235 }
236
237 @Test
238 public void testGesturesCallCorrectActionSeascape() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800239 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
240 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
241
Matthew Ng86a436e2018-10-26 16:00:53 -0700242 mController.setBarState(false /* isRTL */, NAV_BAR_LEFT);
243 NavigationGestureAction swipeUp = mockAction(true);
244 NavigationGestureAction swipeDown = mockAction(true);
245 NavigationGestureAction swipeLeft = mockAction(true);
246 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800247 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
248 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700249
250 // Swipe Up
Matthew Ngb9c84282018-12-06 17:15:27 -0800251 assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700252 // Swipe Down
Matthew Ngb9c84282018-12-06 17:15:27 -0800253 assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
Matthew Ng86a436e2018-10-26 16:00:53 -0700254 // Swipe Left
255 assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
256 // Swipe Right
257 assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
258 }
259
260 @Test
261 public void testGesturesCallCorrectActionRTL() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800262 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
263 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700264 mController.setBarState(true /* isRTL */, NAV_BAR_BOTTOM);
265
266 // The swipe gestures below are for LTR, so RTL in portrait will be swapped
267 NavigationGestureAction swipeUp = mockAction(true);
268 NavigationGestureAction swipeDown = mockAction(true);
269 NavigationGestureAction swipeLeft = mockAction(true);
270 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800271 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
272 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700273
274 // Swipe Up in RTL
275 assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
276 // Swipe Down in RTL
277 assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
278 // Swipe Left in RTL
Matthew Ngb9c84282018-12-06 17:15:27 -0800279 assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700280 // Swipe Right in RTL
Matthew Ngb9c84282018-12-06 17:15:27 -0800281 assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 0);
Matthew Ng86a436e2018-10-26 16:00:53 -0700282 }
283
284 @Test
285 public void testGesturesCallCorrectActionLandscapeRTL() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800286 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
287 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700288 mController.setBarState(true /* isRTL */, NAV_BAR_RIGHT);
289
290 // The swipe gestures below are for LTR, so RTL in landscape will be swapped
291 NavigationGestureAction swipeUp = mockAction(true);
292 NavigationGestureAction swipeDown = mockAction(true);
293 NavigationGestureAction swipeLeft = mockAction(true);
294 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800295 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
296 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700297
298 // Swipe Up
Matthew Ngb9c84282018-12-06 17:15:27 -0800299 assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700300 // Swipe Down
Matthew Ngb9c84282018-12-06 17:15:27 -0800301 assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
Matthew Ng86a436e2018-10-26 16:00:53 -0700302 // Swipe Left
303 assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
304 // Swipe Right
305 assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
306 }
307
308 @Test
309 public void testGesturesCallCorrectActionSeascapeRTL() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800310 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
311 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700312 mController.setBarState(true /* isRTL */, NAV_BAR_LEFT);
313
314 // The swipe gestures below are for LTR, so RTL in seascape will be swapped
315 NavigationGestureAction swipeUp = mockAction(true);
316 NavigationGestureAction swipeDown = mockAction(true);
317 NavigationGestureAction swipeLeft = mockAction(true);
318 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800319 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
320 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700321
322 // Swipe Up
Matthew Ngb9c84282018-12-06 17:15:27 -0800323 assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700324 // Swipe Down
Matthew Ngb9c84282018-12-06 17:15:27 -0800325 assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
Matthew Ng86a436e2018-10-26 16:00:53 -0700326 // Swipe Left
327 assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
328 // Swipe Right
329 assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
330 }
331
332 @Test
333 public void testActionPreventByPinnedState() throws Exception {
334 // Screen is pinned
335 doReturn(true).when(mNavigationBarView).inScreenPinning();
336
337 // Add enabled gesture action
338 NavigationGestureAction action = mockAction(true);
339 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800340 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
341 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700342
343 // Touch down to begin swipe
344 MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
345 assertFalse(touch(downEvent));
346 verify(mProxy, never()).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
347 verify(mProxy, never()).onMotionEvent(downEvent);
348
349 // Move to start gesture, but pinned so it should not trigger action
350 MotionEvent moveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
351 assertFalse(touch(moveEvent));
352 assertNull(mController.getCurrentAction());
353 verify(mNavigationBarView, times(1)).showPinningEscapeToast();
354 verify(action, never()).onGestureStart(moveEvent);
355 }
356
357 @Test
358 public void testActionPreventedNotificationsShown() throws Exception {
359 NavigationGestureAction action = mockAction(true);
360 doReturn(false).when(action).canRunWhenNotificationsShowing();
361 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800362 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
363 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700364
365 // Show the notifications
366 doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
367
368 // Swipe up
369 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
370 assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
371 assertNull(mController.getCurrentAction());
372 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
373
374 // Hide the notifications
375 doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
376
377 // Swipe up
378 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
379 assertTrue(touch(MotionEvent.ACTION_MOVE, 1, 1));
380 assertEquals(action, mController.getCurrentAction());
381 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
382 }
383
384 @Test
385 public void testActionCannotPerform() throws Exception {
386 NavigationGestureAction action = mockAction(true);
387 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800388 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
389 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700390
391 // Cannot perform action
392 doReturn(false).when(action).canPerformAction();
393
394 // Swipe up
395 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
396 assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
397 assertNull(mController.getCurrentAction());
398 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
399
400 // Cannot perform action
401 doReturn(true).when(action).canPerformAction();
402
403 // Swipe up
404 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
405 assertTrue(touch(MotionEvent.ACTION_MOVE, 1, 1));
406 assertEquals(action, mController.getCurrentAction());
407 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
408 }
409
410 @Test
411 public void testQuickScrub() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800412 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
413 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700414 QuickScrubAction action = spy(new QuickScrubAction(mNavigationBarView, mProxyService));
415 mController.setGestureActions(null /* swipeUpAction */, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800416 null /* swipeLeftAction */, action, null /* leftEdgeSwipe */,
417 null /* rightEdgeSwipe */);
418 int x = NAVBAR_WIDTH / 2;
Matthew Ng86a436e2018-10-26 16:00:53 -0700419 int y = 20;
420
421 // Set the layout and other padding to make sure the scrub fraction is calculated correctly
Matthew Ngb9c84282018-12-06 17:15:27 -0800422 action.onLayout(true, 0, 0, NAVBAR_WIDTH, NAVBAR_HEIGHT);
Matthew Ng86a436e2018-10-26 16:00:53 -0700423 doReturn(0).when(mNavigationBarView).getPaddingLeft();
424 doReturn(0).when(mNavigationBarView).getPaddingRight();
425 doReturn(0).when(mNavigationBarView).getPaddingStart();
426 doReturn(0).when(mResources)
427 .getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
428
429 // Quickscrub disabled, so the action should be disabled
430 doReturn(false).when(mNavigationBarView).isQuickScrubEnabled();
431 assertFalse(action.isEnabled());
432 doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
433
434 // Touch down
Matthew Ngb9c84282018-12-06 17:15:27 -0800435 MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, x, y);
Matthew Ng86a436e2018-10-26 16:00:53 -0700436 assertFalse(touch(downEvent));
437 assertNull(mController.getCurrentAction());
438 verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
439 verify(mProxy, times(1)).onMotionEvent(downEvent);
440
441 // Move to start trigger action from gesture
Matthew Ngb9c84282018-12-06 17:15:27 -0800442 MotionEvent moveEvent1 = event(MotionEvent.ACTION_MOVE, x + 100, y);
Matthew Ng86a436e2018-10-26 16:00:53 -0700443 assertTrue(touch(moveEvent1));
444 assertEquals(action, mController.getCurrentAction());
445 verify(action, times(1)).onGestureStart(moveEvent1);
446 verify(mProxy, times(1)).onQuickScrubStart();
447 verify(mProxyService, times(1)).notifyQuickScrubStarted();
448 verify(mNavigationBarView, times(1)).updateSlippery();
Matthew Nga62b62082018-12-03 15:58:52 -0800449 verify(mProxy, never()).onMotionEvent(moveEvent1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700450
451 // Move again for scrub
Matthew Ngb9c84282018-12-06 17:15:27 -0800452 float fraction = 3f / 4;
453 x = (int) (NAVBAR_WIDTH * fraction);
454 MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, x, y);
Matthew Ng86a436e2018-10-26 16:00:53 -0700455 assertTrue(touch(moveEvent2));
456 assertEquals(action, mController.getCurrentAction());
Matthew Ngb9c84282018-12-06 17:15:27 -0800457 verify(action, times(1)).onGestureMove(x, y);
458 verify(mProxy, times(1)).onQuickScrubProgress(fraction);
Matthew Nga62b62082018-12-03 15:58:52 -0800459 verify(mProxy, never()).onMotionEvent(moveEvent2);
Matthew Ng86a436e2018-10-26 16:00:53 -0700460
461 // Action up
462 MotionEvent upEvent = event(MotionEvent.ACTION_UP, 1, y);
463 assertFalse(touch(upEvent));
464 assertNull(mController.getCurrentAction());
465 verify(action, times(1)).onGestureEnd();
466 verify(mProxy, times(1)).onQuickScrubEnd();
Matthew Nga62b62082018-12-03 15:58:52 -0800467 verify(mProxy, never()).onMotionEvent(upEvent);
Matthew Ng86a436e2018-10-26 16:00:53 -0700468 }
469
470 @Test
471 public void testQuickStep() throws Exception {
472 QuickStepAction action = new QuickStepAction(mNavigationBarView, mProxyService);
473 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800474 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
475 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700476
477 // Notifications are up, should prevent quickstep
478 doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
479
480 // Swipe up
481 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
482 assertNull(mController.getCurrentAction());
483 assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
484 assertNull(mController.getCurrentAction());
485 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
486 doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
487
488 // Quickstep disabled, so the action should be disabled
489 doReturn(false).when(mNavigationBarView).isQuickStepSwipeUpEnabled();
490 assertFalse(action.isEnabled());
491 doReturn(true).when(mNavigationBarView).isQuickStepSwipeUpEnabled();
492
493 // Swipe up should call proxy events
494 MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
495 assertFalse(touch(downEvent));
496 assertNull(mController.getCurrentAction());
497 verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
498 verify(mProxy, times(1)).onMotionEvent(downEvent);
499
500 MotionEvent moveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
501 assertTrue(touch(moveEvent));
502 assertEquals(action, mController.getCurrentAction());
503 verify(mProxy, times(1)).onQuickStep(moveEvent);
504 verify(mProxyService, times(1)).notifyQuickStepStarted();
505 }
506
507 @Test
508 public void testLongPressPreventDetection() throws Exception {
509 NavigationGestureAction action = mockAction(true);
510 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800511 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
512 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700513
514 // Start the drag up
515 assertFalse(touch(MotionEvent.ACTION_DOWN, 100, 1));
516 assertNull(mController.getCurrentAction());
517
518 // Long press something on the navigation bar such as Home button
519 mNavigationBarView.onNavigationButtonLongPress(mock(View.class));
520
521 // Swipe right will not start any gestures
522 MotionEvent motionMoveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
523 assertFalse(touch(motionMoveEvent));
524 assertNull(mController.getCurrentAction());
525 verify(action, never()).startGesture(motionMoveEvent);
526
527 // Touch up
528 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
529 verify(action, never()).endGesture();
530 }
531
532 @Test
533 public void testHitTargetDragged() throws Exception {
534 ButtonDispatcher button = mock(ButtonDispatcher.class);
Matthew Ngb9c84282018-12-06 17:15:27 -0800535 FakeLocationView buttonView = spy(new FakeLocationView(mContext, NAVBAR_WIDTH / 2,
536 NAVBAR_HEIGHT / 2));
Matthew Ng86a436e2018-10-26 16:00:53 -0700537 doReturn(buttonView).when(button).getCurrentView();
538
539 NavigationGestureAction action = mockAction(true);
Matthew Ngb9c84282018-12-06 17:15:27 -0800540 mController.setGestureActions(action, action, action, action, action, action);
Matthew Ng86a436e2018-10-26 16:00:53 -0700541
542 // Setup getting the hit target
543 doReturn(HIT_TARGET_HOME).when(action).requiresTouchDownHitTarget();
Matthew Ngb9c84282018-12-06 17:15:27 -0800544 doReturn(true).when(action).allowHitTargetToMoveOverDrag();
Matthew Ng86a436e2018-10-26 16:00:53 -0700545 doReturn(HIT_TARGET_HOME).when(mNavigationBarView).getDownHitTarget();
546 doReturn(button).when(mNavigationBarView).getHomeButton();
Matthew Ngb9c84282018-12-06 17:15:27 -0800547 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
548 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700549
550 // Portrait
551 assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_BOTTOM);
552
553 // Portrait RTL
554 assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_BOTTOM);
555
556 // Landscape
557 assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_RIGHT);
558
559 // Landscape RTL
560 assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_RIGHT);
561
562 // Seascape
563 assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_LEFT);
564
565 // Seascape RTL
566 assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT);
567 }
568
569 private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL,
570 int navPos) {
571 mController.setBarState(isRTL, navPos);
572
573 // Swipe up
574 assertGestureDragsHitTarget(buttonView, 10 /* x1 */, 200 /* y1 */, 0 /* x2 */, 0 /* y2 */,
575 0 /* dx */, -1 /* dy */);
576 // Swipe left
577 assertGestureDragsHitTarget(buttonView, 200 /* x1 */, 10 /* y1 */, 0 /* x2 */, 0 /* y2 */,
578 -1 /* dx */, 0 /* dy */);
579 // Swipe right
580 assertGestureDragsHitTarget(buttonView, 0 /* x1 */, 0 /* y1 */, 200 /* x2 */, 10 /* y2 */,
581 1 /* dx */, 0 /* dy */);
582 // Swipe down
583 assertGestureDragsHitTarget(buttonView, 0 /* x1 */, 0 /* y1 */, 10 /* x2 */, 200 /* y2 */,
584 0 /* dx */, 1 /* dy */);
585 }
586
587 /**
588 * Asserts the gesture actually moves the hit target
589 * @param buttonView button to check if moved, use Mockito.spy on a real object
590 * @param x1 start x
591 * @param x2 start y
592 * @param y1 end x
593 * @param y2 end y
594 * @param dx diff in x, if not 0, its sign determines direction, value does not matter
595 * @param dy diff in y, if not 0, its sign determines direction, value does not matter
596 */
597 private void assertGestureDragsHitTarget(View buttonView, int x1, int y1, int x2, int y2,
598 int dx, int dy) {
599 ArgumentCaptor<Float> captor = ArgumentCaptor.forClass(Float.class);
600 assertFalse(touch(MotionEvent.ACTION_DOWN, x1, y1));
601 assertTrue(touch(MotionEvent.ACTION_MOVE, x2, y2));
602
603 // Verify positions of the button drag
604 if (dx == 0) {
605 verify(buttonView, never()).setTranslationX(anyFloat());
606 } else {
607 verify(buttonView).setTranslationX(captor.capture());
608 if (dx < 0) {
609 assertTrue("Button should have moved left", (float) captor.getValue() < 0);
610 } else {
611 assertTrue("Button should have moved right", (float) captor.getValue() > 0);
612 }
613 }
614 if (dy == 0) {
615 verify(buttonView, never()).setTranslationY(anyFloat());
616 } else {
617 verify(buttonView).setTranslationY(captor.capture());
618 if (dy < 0) {
619 assertTrue("Button should have moved up", (float) captor.getValue() < 0);
620 } else {
621 assertTrue("Button should have moved down", (float) captor.getValue() > 0);
622 }
623 }
624
625 // Touch up
626 assertFalse(touch(MotionEvent.ACTION_UP, x2, y2));
627 verify(buttonView, times(1)).animate();
628
629 // Reset button state
630 reset(buttonView);
631 }
632
Matthew Ng86a436e2018-10-26 16:00:53 -0700633 private MotionEvent event(int action, float x, float y) {
634 final MotionEvent event = mock(MotionEvent.class);
635 doReturn(x).when(event).getX();
636 doReturn(y).when(event).getY();
637 doReturn(action & MotionEvent.ACTION_MASK).when(event).getActionMasked();
638 doReturn(action).when(event).getAction();
639 return event;
640 }
641
642 private boolean touch(int action, float x, float y) {
643 return touch(event(action, x, y));
644 }
645
646 private boolean touch(MotionEvent event) {
647 return mController.onInterceptTouchEvent(event);
648 }
649
650 private NavigationGestureAction mockAction(boolean enabled) {
651 final NavigationGestureAction action = mock(NavigationGestureAction.class);
652 doReturn(enabled).when(action).isEnabled();
653 doReturn(HIT_TARGET_NONE).when(action).requiresTouchDownHitTarget();
654 doReturn(true).when(action).canPerformAction();
655 return action;
656 }
657
658 private void assertGestureTriggersAction(NavigationGestureAction action, int x1, int y1,
659 int x2, int y2) {
660 // Start the drag
661 assertFalse(touch(MotionEvent.ACTION_DOWN, x1, y1));
662 assertNull(mController.getCurrentAction());
663
664 // Swipe
665 MotionEvent motionMoveEvent = event(MotionEvent.ACTION_MOVE, x2, y2);
666 assertTrue(touch(motionMoveEvent));
667 assertEquals(action, mController.getCurrentAction());
668 verify(action, times(1)).startGesture(motionMoveEvent);
669
670 // Move again
671 assertTrue(touch(MotionEvent.ACTION_MOVE, x2, y2));
672 verify(action, times(1)).onGestureMove(x2, y2);
673
674 // Touch up
675 assertFalse(touch(MotionEvent.ACTION_UP, x2, y2));
676 assertNull(mController.getCurrentAction());
677 verify(action, times(1)).endGesture();
678 }
Matthew Ngf1f8a432018-11-08 18:03:29 -0800679
680 static class FakeLocationView extends View {
681 private final int mX;
682 private final int mY;
683
684 public FakeLocationView(Context context, int x, int y) {
685 super(context);
686 mX = x;
687 mY = y;
688 }
689
690 @Override
691 public void getLocationInWindow(int[] outLocation) {
692 outLocation[0] = mX;
693 outLocation[1] = mY;
694 }
695 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700696}