blob: dbf00a379c5fd5edc31c75f1a164b9f66fd309a1 [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;
43import android.support.test.filters.SmallTest;
44import android.testing.AndroidTestingRunner;
45import android.testing.TestableLooper.RunWithLooper;
46import android.view.MotionEvent;
47import android.view.View;
48
Jason Monka716bac2018-12-05 15:48:21 -050049import com.android.systemui.R;
50import com.android.systemui.SysuiTestCase;
51import com.android.systemui.recents.OverviewProxyService;
52import com.android.systemui.shared.recents.IOverviewProxy;
53
Matthew Ng86a436e2018-10-26 16:00:53 -070054import org.junit.Before;
55import org.junit.Test;
56import org.junit.runner.RunWith;
57import org.mockito.ArgumentCaptor;
58import org.mockito.MockitoAnnotations;
59
60/** atest QuickStepControllerTest */
61@RunWith(AndroidTestingRunner.class)
Jason Monka716bac2018-12-05 15:48:21 -050062@RunWithLooper
Matthew Ng86a436e2018-10-26 16:00:53 -070063@SmallTest
64public class QuickStepControllerTest extends SysuiTestCase {
Matthew Ngb9c84282018-12-06 17:15:27 -080065 private static final int NAVBAR_WIDTH = 1000;
66 private static final int NAVBAR_HEIGHT = 300;
Matthew Ngb9c84282018-12-06 17:15:27 -080067
Matthew Ng86a436e2018-10-26 16:00:53 -070068 private QuickStepController mController;
69 private NavigationBarView mNavigationBarView;
70 private StatusBar mStatusBar;
71 private OverviewProxyService mProxyService;
72 private IOverviewProxy mProxy;
73 private Resources mResources;
74
75 @Before
76 public void setup() {
77 MockitoAnnotations.initMocks(this);
78 final ButtonDispatcher backButton = mock(ButtonDispatcher.class);
79 mResources = mock(Resources.class);
80
81 mProxyService = mock(OverviewProxyService.class);
82 mProxy = mock(IOverviewProxy.Stub.class);
83 doReturn(mProxy).when(mProxyService).getProxy();
Matthew Ngd04676d2018-12-04 14:54:48 -080084 doReturn(true).when(mProxyService).shouldShowSwipeUpUI();
Matthew Ng86a436e2018-10-26 16:00:53 -070085 mDependency.injectTestDependency(OverviewProxyService.class, mProxyService);
86
87 mStatusBar = mock(StatusBar.class);
88 doReturn(false).when(mStatusBar).isKeyguardShowing();
89 mContext.putComponent(StatusBar.class, mStatusBar);
90
91 mNavigationBarView = mock(NavigationBarView.class);
92 doReturn(false).when(mNavigationBarView).inScreenPinning();
93 doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
94 doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
95 doReturn(HIT_TARGET_NONE).when(mNavigationBarView).getDownHitTarget();
Matthew Ngb831fb42019-01-30 11:20:48 -080096 doReturn(WINDOW_TARGET_BOTTOM).when(mNavigationBarView).getWindowTarget();
Matthew Ng86a436e2018-10-26 16:00:53 -070097 doReturn(backButton).when(mNavigationBarView).getBackButton();
98 doReturn(mResources).when(mNavigationBarView).getResources();
Matthew Ng0548fbc2019-01-11 12:24:13 -080099 doReturn(mContext).when(mNavigationBarView).getContext();
Matthew Ng86a436e2018-10-26 16:00:53 -0700100
101 mController = new QuickStepController(mContext);
102 mController.setComponents(mNavigationBarView);
103 mController.setBarState(false /* isRTL */, NAV_BAR_BOTTOM);
104 }
105
106 @Test
107 public void testNoActionsNoGestures() throws Exception {
108 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
109 assertFalse(mController.onInterceptTouchEvent(ev));
110 verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
111 assertNull(mController.getCurrentAction());
112 }
113
114 @Test
Matthew Ngd04676d2018-12-04 14:54:48 -0800115 public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
116 doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
117 mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800118 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
119 null /* rightEdgeSwipe */);
Matthew Ngd04676d2018-12-04 14:54:48 -0800120
121 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
122 assertFalse(mController.onInterceptTouchEvent(ev));
123 verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
124 assertNull(mController.getCurrentAction());
125 }
126
127 @Test
Matthew Ng86a436e2018-10-26 16:00:53 -0700128 public void testHasActionDetectGesturesTouchdown() throws Exception {
129 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
130
131 // Add enabled gesture action
132 NavigationGestureAction action = mockAction(true);
133 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800134 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
135 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700136
137 assertFalse(mController.onInterceptTouchEvent(ev));
138 verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
139 verify(action, times(1)).reset();
140 verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
141 verify(mProxy, times(1)).onMotionEvent(ev);
142 assertNull(mController.getCurrentAction());
143 }
144
145 @Test
146 public void testProxyDisconnectedNoGestures() throws Exception {
147 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
148
149 // Add enabled gesture action
150 mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800151 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
152 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700153
154 // Set the gesture on deadzone
155 doReturn(null).when(mProxyService).getProxy();
156
157 assertFalse(mController.onInterceptTouchEvent(ev));
158 verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
159 assertNull(mController.getCurrentAction());
160 }
161
162 @Test
163 public void testNoActionsNoGesturesOverDeadzone() throws Exception {
164 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
165
166 // Touched over deadzone
167 doReturn(HIT_TARGET_DEAD_ZONE).when(mNavigationBarView).getDownHitTarget();
168
169 assertTrue(mController.onInterceptTouchEvent(ev));
170 verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
171 assertNull(mController.getCurrentAction());
172 }
173
174 @Test
175 public void testOnTouchIgnoredDownEventAfterOnIntercept() {
176 mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800177 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
178 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700179
180 MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
181 assertFalse(touch(ev));
182 verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
183
184 // OnTouch event for down is ignored, so requestUnbufferedDispatch ran once from before
185 assertFalse(mNavigationBarView.onTouchEvent(ev));
186 verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
187 }
188
189 @Test
190 public void testGesturesCallCorrectAction() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800191 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
192 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
193
Matthew Ng86a436e2018-10-26 16:00:53 -0700194 NavigationGestureAction swipeUp = mockAction(true);
195 NavigationGestureAction swipeDown = mockAction(true);
196 NavigationGestureAction swipeLeft = mockAction(true);
197 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800198 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
199 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700200
201 // Swipe Up
202 assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
203 // Swipe Down
204 assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
205 // Swipe Left
Matthew Ngb9c84282018-12-06 17:15:27 -0800206 assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700207 // Swipe Right
Matthew Ngb9c84282018-12-06 17:15:27 -0800208 assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 5);
Matthew Ng86a436e2018-10-26 16:00:53 -0700209 }
210
211 @Test
212 public void testGesturesCallCorrectActionLandscape() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800213 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
214 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
215
Matthew Ng86a436e2018-10-26 16:00:53 -0700216 NavigationGestureAction swipeUp = mockAction(true);
217 NavigationGestureAction swipeDown = mockAction(true);
218 NavigationGestureAction swipeLeft = mockAction(true);
219 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800220 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
221 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700222
223 // In landscape
224 mController.setBarState(false /* isRTL */, NAV_BAR_RIGHT);
225
226 // Swipe Up
227 assertGestureTriggersAction(swipeRight, 1, 100, 5, 1);
228 // Swipe Down
Matthew Ngb9c84282018-12-06 17:15:27 -0800229 assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
Matthew Ng86a436e2018-10-26 16:00:53 -0700230 // Swipe Left
231 assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
232 // Swipe Right
233 assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
234 }
235
236 @Test
237 public void testGesturesCallCorrectActionSeascape() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800238 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
239 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
240
Matthew Ng86a436e2018-10-26 16:00:53 -0700241 mController.setBarState(false /* isRTL */, NAV_BAR_LEFT);
242 NavigationGestureAction swipeUp = mockAction(true);
243 NavigationGestureAction swipeDown = mockAction(true);
244 NavigationGestureAction swipeLeft = mockAction(true);
245 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800246 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
247 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700248
249 // Swipe Up
Matthew Ngb9c84282018-12-06 17:15:27 -0800250 assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700251 // Swipe Down
Matthew Ngb9c84282018-12-06 17:15:27 -0800252 assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
Matthew Ng86a436e2018-10-26 16:00:53 -0700253 // Swipe Left
254 assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
255 // Swipe Right
256 assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
257 }
258
259 @Test
260 public void testGesturesCallCorrectActionRTL() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800261 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
262 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700263 mController.setBarState(true /* isRTL */, NAV_BAR_BOTTOM);
264
265 // The swipe gestures below are for LTR, so RTL in portrait will be swapped
266 NavigationGestureAction swipeUp = mockAction(true);
267 NavigationGestureAction swipeDown = mockAction(true);
268 NavigationGestureAction swipeLeft = mockAction(true);
269 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800270 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
271 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700272
273 // Swipe Up in RTL
274 assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
275 // Swipe Down in RTL
276 assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
277 // Swipe Left in RTL
Matthew Ngb9c84282018-12-06 17:15:27 -0800278 assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700279 // Swipe Right in RTL
Matthew Ngb9c84282018-12-06 17:15:27 -0800280 assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 0);
Matthew Ng86a436e2018-10-26 16:00:53 -0700281 }
282
283 @Test
284 public void testGesturesCallCorrectActionLandscapeRTL() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800285 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
286 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700287 mController.setBarState(true /* isRTL */, NAV_BAR_RIGHT);
288
289 // The swipe gestures below are for LTR, so RTL in landscape will be swapped
290 NavigationGestureAction swipeUp = mockAction(true);
291 NavigationGestureAction swipeDown = mockAction(true);
292 NavigationGestureAction swipeLeft = mockAction(true);
293 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800294 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
295 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700296
297 // Swipe Up
Matthew Ngb9c84282018-12-06 17:15:27 -0800298 assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700299 // Swipe Down
Matthew Ngb9c84282018-12-06 17:15:27 -0800300 assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
Matthew Ng86a436e2018-10-26 16:00:53 -0700301 // Swipe Left
302 assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
303 // Swipe Right
304 assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
305 }
306
307 @Test
308 public void testGesturesCallCorrectActionSeascapeRTL() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800309 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
310 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700311 mController.setBarState(true /* isRTL */, NAV_BAR_LEFT);
312
313 // The swipe gestures below are for LTR, so RTL in seascape will be swapped
314 NavigationGestureAction swipeUp = mockAction(true);
315 NavigationGestureAction swipeDown = mockAction(true);
316 NavigationGestureAction swipeLeft = mockAction(true);
317 NavigationGestureAction swipeRight = mockAction(true);
Matthew Ngb831fb42019-01-30 11:20:48 -0800318 mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
319 null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700320
321 // Swipe Up
Matthew Ngb9c84282018-12-06 17:15:27 -0800322 assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, 1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700323 // Swipe Down
Matthew Ngb9c84282018-12-06 17:15:27 -0800324 assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
Matthew Ng86a436e2018-10-26 16:00:53 -0700325 // Swipe Left
326 assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
327 // Swipe Right
328 assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
329 }
330
331 @Test
332 public void testActionPreventByPinnedState() throws Exception {
333 // Screen is pinned
334 doReturn(true).when(mNavigationBarView).inScreenPinning();
335
336 // Add enabled gesture action
337 NavigationGestureAction action = mockAction(true);
338 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800339 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
340 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700341
342 // Touch down to begin swipe
343 MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
344 assertFalse(touch(downEvent));
345 verify(mProxy, never()).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
346 verify(mProxy, never()).onMotionEvent(downEvent);
347
348 // Move to start gesture, but pinned so it should not trigger action
349 MotionEvent moveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
350 assertFalse(touch(moveEvent));
351 assertNull(mController.getCurrentAction());
352 verify(mNavigationBarView, times(1)).showPinningEscapeToast();
353 verify(action, never()).onGestureStart(moveEvent);
354 }
355
356 @Test
357 public void testActionPreventedNotificationsShown() throws Exception {
358 NavigationGestureAction action = mockAction(true);
359 doReturn(false).when(action).canRunWhenNotificationsShowing();
360 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800361 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
362 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700363
364 // Show the notifications
365 doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
366
367 // Swipe up
368 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
369 assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
370 assertNull(mController.getCurrentAction());
371 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
372
373 // Hide the notifications
374 doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
375
376 // Swipe up
377 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
378 assertTrue(touch(MotionEvent.ACTION_MOVE, 1, 1));
379 assertEquals(action, mController.getCurrentAction());
380 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
381 }
382
383 @Test
384 public void testActionCannotPerform() throws Exception {
385 NavigationGestureAction action = mockAction(true);
386 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800387 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
388 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700389
390 // Cannot perform action
391 doReturn(false).when(action).canPerformAction();
392
393 // Swipe up
394 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
395 assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
396 assertNull(mController.getCurrentAction());
397 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
398
399 // Cannot perform action
400 doReturn(true).when(action).canPerformAction();
401
402 // Swipe up
403 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
404 assertTrue(touch(MotionEvent.ACTION_MOVE, 1, 1));
405 assertEquals(action, mController.getCurrentAction());
406 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
407 }
408
409 @Test
410 public void testQuickScrub() throws Exception {
Matthew Ngb9c84282018-12-06 17:15:27 -0800411 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
412 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700413 QuickScrubAction action = spy(new QuickScrubAction(mNavigationBarView, mProxyService));
414 mController.setGestureActions(null /* swipeUpAction */, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800415 null /* swipeLeftAction */, action, null /* leftEdgeSwipe */,
416 null /* rightEdgeSwipe */);
417 int x = NAVBAR_WIDTH / 2;
Matthew Ng86a436e2018-10-26 16:00:53 -0700418 int y = 20;
419
420 // Set the layout and other padding to make sure the scrub fraction is calculated correctly
Matthew Ngb9c84282018-12-06 17:15:27 -0800421 action.onLayout(true, 0, 0, NAVBAR_WIDTH, NAVBAR_HEIGHT);
Matthew Ng86a436e2018-10-26 16:00:53 -0700422 doReturn(0).when(mNavigationBarView).getPaddingLeft();
423 doReturn(0).when(mNavigationBarView).getPaddingRight();
424 doReturn(0).when(mNavigationBarView).getPaddingStart();
425 doReturn(0).when(mResources)
426 .getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
427
428 // Quickscrub disabled, so the action should be disabled
429 doReturn(false).when(mNavigationBarView).isQuickScrubEnabled();
430 assertFalse(action.isEnabled());
431 doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
432
433 // Touch down
Matthew Ngb9c84282018-12-06 17:15:27 -0800434 MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, x, y);
Matthew Ng86a436e2018-10-26 16:00:53 -0700435 assertFalse(touch(downEvent));
436 assertNull(mController.getCurrentAction());
437 verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
438 verify(mProxy, times(1)).onMotionEvent(downEvent);
439
440 // Move to start trigger action from gesture
Matthew Ngb9c84282018-12-06 17:15:27 -0800441 MotionEvent moveEvent1 = event(MotionEvent.ACTION_MOVE, x + 100, y);
Matthew Ng86a436e2018-10-26 16:00:53 -0700442 assertTrue(touch(moveEvent1));
443 assertEquals(action, mController.getCurrentAction());
444 verify(action, times(1)).onGestureStart(moveEvent1);
445 verify(mProxy, times(1)).onQuickScrubStart();
446 verify(mProxyService, times(1)).notifyQuickScrubStarted();
447 verify(mNavigationBarView, times(1)).updateSlippery();
Matthew Nga62b62082018-12-03 15:58:52 -0800448 verify(mProxy, never()).onMotionEvent(moveEvent1);
Matthew Ng86a436e2018-10-26 16:00:53 -0700449
450 // Move again for scrub
Matthew Ngb9c84282018-12-06 17:15:27 -0800451 float fraction = 3f / 4;
452 x = (int) (NAVBAR_WIDTH * fraction);
453 MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, x, y);
Matthew Ng86a436e2018-10-26 16:00:53 -0700454 assertTrue(touch(moveEvent2));
455 assertEquals(action, mController.getCurrentAction());
Matthew Ngb9c84282018-12-06 17:15:27 -0800456 verify(action, times(1)).onGestureMove(x, y);
457 verify(mProxy, times(1)).onQuickScrubProgress(fraction);
Matthew Nga62b62082018-12-03 15:58:52 -0800458 verify(mProxy, never()).onMotionEvent(moveEvent2);
Matthew Ng86a436e2018-10-26 16:00:53 -0700459
460 // Action up
461 MotionEvent upEvent = event(MotionEvent.ACTION_UP, 1, y);
462 assertFalse(touch(upEvent));
463 assertNull(mController.getCurrentAction());
464 verify(action, times(1)).onGestureEnd();
465 verify(mProxy, times(1)).onQuickScrubEnd();
Matthew Nga62b62082018-12-03 15:58:52 -0800466 verify(mProxy, never()).onMotionEvent(upEvent);
Matthew Ng86a436e2018-10-26 16:00:53 -0700467 }
468
469 @Test
470 public void testQuickStep() throws Exception {
471 QuickStepAction action = new QuickStepAction(mNavigationBarView, mProxyService);
472 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800473 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
474 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700475
476 // Notifications are up, should prevent quickstep
477 doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
478
479 // Swipe up
480 assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
481 assertNull(mController.getCurrentAction());
482 assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
483 assertNull(mController.getCurrentAction());
484 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
485 doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
486
487 // Quickstep disabled, so the action should be disabled
488 doReturn(false).when(mNavigationBarView).isQuickStepSwipeUpEnabled();
489 assertFalse(action.isEnabled());
490 doReturn(true).when(mNavigationBarView).isQuickStepSwipeUpEnabled();
491
492 // Swipe up should call proxy events
493 MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
494 assertFalse(touch(downEvent));
495 assertNull(mController.getCurrentAction());
496 verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
497 verify(mProxy, times(1)).onMotionEvent(downEvent);
498
499 MotionEvent moveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
500 assertTrue(touch(moveEvent));
501 assertEquals(action, mController.getCurrentAction());
502 verify(mProxy, times(1)).onQuickStep(moveEvent);
503 verify(mProxyService, times(1)).notifyQuickStepStarted();
504 }
505
506 @Test
507 public void testLongPressPreventDetection() throws Exception {
508 NavigationGestureAction action = mockAction(true);
509 mController.setGestureActions(action, null /* swipeDownAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800510 null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
511 null /* rightEdgeSwipe */);
Matthew Ng86a436e2018-10-26 16:00:53 -0700512
513 // Start the drag up
514 assertFalse(touch(MotionEvent.ACTION_DOWN, 100, 1));
515 assertNull(mController.getCurrentAction());
516
517 // Long press something on the navigation bar such as Home button
518 mNavigationBarView.onNavigationButtonLongPress(mock(View.class));
519
520 // Swipe right will not start any gestures
521 MotionEvent motionMoveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
522 assertFalse(touch(motionMoveEvent));
523 assertNull(mController.getCurrentAction());
524 verify(action, never()).startGesture(motionMoveEvent);
525
526 // Touch up
527 assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
528 verify(action, never()).endGesture();
529 }
530
531 @Test
532 public void testHitTargetDragged() throws Exception {
533 ButtonDispatcher button = mock(ButtonDispatcher.class);
Matthew Ngb9c84282018-12-06 17:15:27 -0800534 FakeLocationView buttonView = spy(new FakeLocationView(mContext, NAVBAR_WIDTH / 2,
535 NAVBAR_HEIGHT / 2));
Matthew Ng86a436e2018-10-26 16:00:53 -0700536 doReturn(buttonView).when(button).getCurrentView();
537
538 NavigationGestureAction action = mockAction(true);
Matthew Ngb9c84282018-12-06 17:15:27 -0800539 mController.setGestureActions(action, action, action, action, action, action);
Matthew Ng86a436e2018-10-26 16:00:53 -0700540
541 // Setup getting the hit target
542 doReturn(HIT_TARGET_HOME).when(action).requiresTouchDownHitTarget();
Matthew Ngb9c84282018-12-06 17:15:27 -0800543 doReturn(true).when(action).allowHitTargetToMoveOverDrag();
Matthew Ng86a436e2018-10-26 16:00:53 -0700544 doReturn(HIT_TARGET_HOME).when(mNavigationBarView).getDownHitTarget();
545 doReturn(button).when(mNavigationBarView).getHomeButton();
Matthew Ngb9c84282018-12-06 17:15:27 -0800546 doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
547 doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
Matthew Ng86a436e2018-10-26 16:00:53 -0700548
549 // Portrait
550 assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_BOTTOM);
551
552 // Portrait RTL
553 assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_BOTTOM);
554
555 // Landscape
556 assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_RIGHT);
557
558 // Landscape RTL
559 assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_RIGHT);
560
561 // Seascape
562 assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_LEFT);
563
564 // Seascape RTL
565 assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT);
566 }
567
568 private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL,
569 int navPos) {
570 mController.setBarState(isRTL, navPos);
571
572 // Swipe up
573 assertGestureDragsHitTarget(buttonView, 10 /* x1 */, 200 /* y1 */, 0 /* x2 */, 0 /* y2 */,
574 0 /* dx */, -1 /* dy */);
575 // Swipe left
576 assertGestureDragsHitTarget(buttonView, 200 /* x1 */, 10 /* y1 */, 0 /* x2 */, 0 /* y2 */,
577 -1 /* dx */, 0 /* dy */);
578 // Swipe right
579 assertGestureDragsHitTarget(buttonView, 0 /* x1 */, 0 /* y1 */, 200 /* x2 */, 10 /* y2 */,
580 1 /* dx */, 0 /* dy */);
581 // Swipe down
582 assertGestureDragsHitTarget(buttonView, 0 /* x1 */, 0 /* y1 */, 10 /* x2 */, 200 /* y2 */,
583 0 /* dx */, 1 /* dy */);
584 }
585
586 /**
587 * Asserts the gesture actually moves the hit target
588 * @param buttonView button to check if moved, use Mockito.spy on a real object
589 * @param x1 start x
590 * @param x2 start y
591 * @param y1 end x
592 * @param y2 end y
593 * @param dx diff in x, if not 0, its sign determines direction, value does not matter
594 * @param dy diff in y, if not 0, its sign determines direction, value does not matter
595 */
596 private void assertGestureDragsHitTarget(View buttonView, int x1, int y1, int x2, int y2,
597 int dx, int dy) {
598 ArgumentCaptor<Float> captor = ArgumentCaptor.forClass(Float.class);
599 assertFalse(touch(MotionEvent.ACTION_DOWN, x1, y1));
600 assertTrue(touch(MotionEvent.ACTION_MOVE, x2, y2));
601
602 // Verify positions of the button drag
603 if (dx == 0) {
604 verify(buttonView, never()).setTranslationX(anyFloat());
605 } else {
606 verify(buttonView).setTranslationX(captor.capture());
607 if (dx < 0) {
608 assertTrue("Button should have moved left", (float) captor.getValue() < 0);
609 } else {
610 assertTrue("Button should have moved right", (float) captor.getValue() > 0);
611 }
612 }
613 if (dy == 0) {
614 verify(buttonView, never()).setTranslationY(anyFloat());
615 } else {
616 verify(buttonView).setTranslationY(captor.capture());
617 if (dy < 0) {
618 assertTrue("Button should have moved up", (float) captor.getValue() < 0);
619 } else {
620 assertTrue("Button should have moved down", (float) captor.getValue() > 0);
621 }
622 }
623
624 // Touch up
625 assertFalse(touch(MotionEvent.ACTION_UP, x2, y2));
626 verify(buttonView, times(1)).animate();
627
628 // Reset button state
629 reset(buttonView);
630 }
631
Matthew Ng86a436e2018-10-26 16:00:53 -0700632 private MotionEvent event(int action, float x, float y) {
633 final MotionEvent event = mock(MotionEvent.class);
634 doReturn(x).when(event).getX();
635 doReturn(y).when(event).getY();
636 doReturn(action & MotionEvent.ACTION_MASK).when(event).getActionMasked();
637 doReturn(action).when(event).getAction();
638 return event;
639 }
640
641 private boolean touch(int action, float x, float y) {
642 return touch(event(action, x, y));
643 }
644
645 private boolean touch(MotionEvent event) {
646 return mController.onInterceptTouchEvent(event);
647 }
648
649 private NavigationGestureAction mockAction(boolean enabled) {
650 final NavigationGestureAction action = mock(NavigationGestureAction.class);
651 doReturn(enabled).when(action).isEnabled();
652 doReturn(HIT_TARGET_NONE).when(action).requiresTouchDownHitTarget();
653 doReturn(true).when(action).canPerformAction();
654 return action;
655 }
656
657 private void assertGestureTriggersAction(NavigationGestureAction action, int x1, int y1,
658 int x2, int y2) {
659 // Start the drag
660 assertFalse(touch(MotionEvent.ACTION_DOWN, x1, y1));
661 assertNull(mController.getCurrentAction());
662
663 // Swipe
664 MotionEvent motionMoveEvent = event(MotionEvent.ACTION_MOVE, x2, y2);
665 assertTrue(touch(motionMoveEvent));
666 assertEquals(action, mController.getCurrentAction());
667 verify(action, times(1)).startGesture(motionMoveEvent);
668
669 // Move again
670 assertTrue(touch(MotionEvent.ACTION_MOVE, x2, y2));
671 verify(action, times(1)).onGestureMove(x2, y2);
672
673 // Touch up
674 assertFalse(touch(MotionEvent.ACTION_UP, x2, y2));
675 assertNull(mController.getCurrentAction());
676 verify(action, times(1)).endGesture();
677 }
Matthew Ngf1f8a432018-11-08 18:03:29 -0800678
679 static class FakeLocationView extends View {
680 private final int mX;
681 private final int mY;
682
683 public FakeLocationView(Context context, int x, int y) {
684 super(context);
685 mX = x;
686 mY = y;
687 }
688
689 @Override
690 public void getLocationInWindow(int[] outLocation) {
691 outLocation[0] = mX;
692 outLocation[1] = mY;
693 }
694 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700695}