blob: 382dde9ce043b8fb3ee7b586b4dd3780ab1497af [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.phone;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.res.Resources;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.MotionEvent;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.IOverviewProxy;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.MockitoAnnotations;
/** atest QuickStepControllerTest */
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
public class QuickStepControllerTest extends SysuiTestCase {
private static final int NAVBAR_WIDTH = 1000;
private static final int NAVBAR_HEIGHT = 300;
private static final int EDGE_THRESHOLD = 100;
private QuickStepController mController;
private NavigationBarView mNavigationBarView;
private StatusBar mStatusBar;
private OverviewProxyService mProxyService;
private IOverviewProxy mProxy;
private Resources mResources;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
final ButtonDispatcher backButton = mock(ButtonDispatcher.class);
mResources = mock(Resources.class);
doReturn(EDGE_THRESHOLD).when(mResources)
.getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold);
mProxyService = mock(OverviewProxyService.class);
mProxy = mock(IOverviewProxy.Stub.class);
doReturn(mProxy).when(mProxyService).getProxy();
doReturn(true).when(mProxyService).shouldShowSwipeUpUI();
mDependency.injectTestDependency(OverviewProxyService.class, mProxyService);
mStatusBar = mock(StatusBar.class);
doReturn(false).when(mStatusBar).isKeyguardShowing();
mContext.putComponent(StatusBar.class, mStatusBar);
mNavigationBarView = mock(NavigationBarView.class);
doReturn(false).when(mNavigationBarView).inScreenPinning();
doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
doReturn(HIT_TARGET_NONE).when(mNavigationBarView).getDownHitTarget();
doReturn(backButton).when(mNavigationBarView).getBackButton();
doReturn(mResources).when(mNavigationBarView).getResources();
doReturn(mContext).when(mNavigationBarView).getContext();
mController = new QuickStepController(mContext);
mController.setComponents(mNavigationBarView);
mController.setBarState(false /* isRTL */, NAV_BAR_BOTTOM);
}
@Test
public void testNoActionsNoGestures() throws Exception {
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
assertFalse(mController.onInterceptTouchEvent(ev));
verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
assertNull(mController.getCurrentAction());
}
@Test
public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
assertFalse(mController.onInterceptTouchEvent(ev));
verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
assertNull(mController.getCurrentAction());
}
@Test
public void testHasActionDetectGesturesTouchdown() throws Exception {
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
// Add enabled gesture action
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
assertFalse(mController.onInterceptTouchEvent(ev));
verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
verify(action, times(1)).reset();
verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
verify(mProxy, times(1)).onMotionEvent(ev);
assertNull(mController.getCurrentAction());
}
@Test
public void testProxyDisconnectedNoGestures() throws Exception {
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
// Add enabled gesture action
mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
// Set the gesture on deadzone
doReturn(null).when(mProxyService).getProxy();
assertFalse(mController.onInterceptTouchEvent(ev));
verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
assertNull(mController.getCurrentAction());
}
@Test
public void testNoActionsNoGesturesOverDeadzone() throws Exception {
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
// Touched over deadzone
doReturn(HIT_TARGET_DEAD_ZONE).when(mNavigationBarView).getDownHitTarget();
assertTrue(mController.onInterceptTouchEvent(ev));
verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
assertNull(mController.getCurrentAction());
}
@Test
public void testOnTouchIgnoredDownEventAfterOnIntercept() {
mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
assertFalse(touch(ev));
verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
// OnTouch event for down is ignored, so requestUnbufferedDispatch ran once from before
assertFalse(mNavigationBarView.onTouchEvent(ev));
verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
}
@Test
public void testGesturesCallCorrectAction() throws Exception {
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
NavigationGestureAction swipeLeftFromEdge = mockAction(true);
NavigationGestureAction swipeRightFromEdge = mockAction(true);
mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
swipeRightFromEdge);
// Swipe Up
assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
// Swipe Down
assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
// Swipe Left
assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 5);
// Swipe Left from Edge
assertGestureTriggersAction(swipeLeftFromEdge, NAVBAR_WIDTH, 1, 5, 1);
// Swipe Right from Edge
assertGestureTriggersAction(swipeRightFromEdge, 0, 1, NAVBAR_WIDTH, 5);
}
@Test
public void testGesturesCallCorrectActionLandscape() throws Exception {
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
NavigationGestureAction swipeLeftFromEdge = mockAction(true);
NavigationGestureAction swipeRightFromEdge = mockAction(true);
mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
swipeRightFromEdge);
// In landscape
mController.setBarState(false /* isRTL */, NAV_BAR_RIGHT);
// Swipe Up
assertGestureTriggersAction(swipeRight, 1, 100, 5, 1);
// Swipe Down
assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
// Swipe Left
assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
// Swipe Up from Edge
assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0);
// Swipe Down from Edge
assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH);
}
@Test
public void testGesturesCallCorrectActionSeascape() throws Exception {
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
mController.setBarState(false /* isRTL */, NAV_BAR_LEFT);
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
NavigationGestureAction swipeLeftFromEdge = mockAction(true);
NavigationGestureAction swipeRightFromEdge = mockAction(true);
mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
swipeRightFromEdge);
// Swipe Up
assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
// Swipe Down
assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
// Swipe Left
assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
// Swipe Up from Edge
assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0);
// Swipe Down from Edge
assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH);
}
@Test
public void testGesturesCallCorrectActionRTL() throws Exception {
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
mController.setBarState(true /* isRTL */, NAV_BAR_BOTTOM);
// The swipe gestures below are for LTR, so RTL in portrait will be swapped
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
NavigationGestureAction swipeLeftFromEdge = mockAction(true);
NavigationGestureAction swipeRightFromEdge = mockAction(true);
mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
swipeRightFromEdge);
// Swipe Up in RTL
assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
// Swipe Down in RTL
assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
// Swipe Left in RTL
assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, 5, 1);
// Swipe Right in RTL
assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 0);
// Swipe Left from Edge
assertGestureTriggersAction(swipeRightFromEdge, NAVBAR_WIDTH, 1, 5, 1);
// Swipe Right from Edge
assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, NAVBAR_WIDTH, 5);
}
@Test
public void testGesturesCallCorrectActionLandscapeRTL() throws Exception {
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
mController.setBarState(true /* isRTL */, NAV_BAR_RIGHT);
// The swipe gestures below are for LTR, so RTL in landscape will be swapped
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
NavigationGestureAction swipeLeftFromEdge = mockAction(true);
NavigationGestureAction swipeRightFromEdge = mockAction(true);
mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
swipeRightFromEdge);
// Swipe Up
assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
// Swipe Down
assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
// Swipe Left
assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
// Swipe Up from Edge
assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0);
// Swipe Down from Edge
assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH);
}
@Test
public void testGesturesCallCorrectActionSeascapeRTL() throws Exception {
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
mController.setBarState(true /* isRTL */, NAV_BAR_LEFT);
// The swipe gestures below are for LTR, so RTL in seascape will be swapped
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
NavigationGestureAction swipeLeftFromEdge = mockAction(true);
NavigationGestureAction swipeRightFromEdge = mockAction(true);
mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
swipeRightFromEdge);
// Swipe Up
assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, 1);
// Swipe Down
assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
// Swipe Left
assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
// Swipe Right
assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
// Swipe Up from Edge
assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0);
// Swipe Down from Edge
assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH);
}
@Test
public void testActionPreventByPinnedState() throws Exception {
// Screen is pinned
doReturn(true).when(mNavigationBarView).inScreenPinning();
// Add enabled gesture action
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
// Touch down to begin swipe
MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
assertFalse(touch(downEvent));
verify(mProxy, never()).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
verify(mProxy, never()).onMotionEvent(downEvent);
// Move to start gesture, but pinned so it should not trigger action
MotionEvent moveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
assertFalse(touch(moveEvent));
assertNull(mController.getCurrentAction());
verify(mNavigationBarView, times(1)).showPinningEscapeToast();
verify(action, never()).onGestureStart(moveEvent);
}
@Test
public void testActionPreventedNotificationsShown() throws Exception {
NavigationGestureAction action = mockAction(true);
doReturn(false).when(action).canRunWhenNotificationsShowing();
mController.setGestureActions(action, null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
// Show the notifications
doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
// Swipe up
assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
assertNull(mController.getCurrentAction());
assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
// Hide the notifications
doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
// Swipe up
assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
assertTrue(touch(MotionEvent.ACTION_MOVE, 1, 1));
assertEquals(action, mController.getCurrentAction());
assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
}
@Test
public void testActionCannotPerform() throws Exception {
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
// Cannot perform action
doReturn(false).when(action).canPerformAction();
// Swipe up
assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
assertNull(mController.getCurrentAction());
assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
// Cannot perform action
doReturn(true).when(action).canPerformAction();
// Swipe up
assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
assertTrue(touch(MotionEvent.ACTION_MOVE, 1, 1));
assertEquals(action, mController.getCurrentAction());
assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
}
@Test
public void testQuickScrub() throws Exception {
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
QuickScrubAction action = spy(new QuickScrubAction(mNavigationBarView, mProxyService));
mController.setGestureActions(null /* swipeUpAction */, null /* swipeDownAction */,
null /* swipeLeftAction */, action, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
int x = NAVBAR_WIDTH / 2;
int y = 20;
// Set the layout and other padding to make sure the scrub fraction is calculated correctly
action.onLayout(true, 0, 0, NAVBAR_WIDTH, NAVBAR_HEIGHT);
doReturn(0).when(mNavigationBarView).getPaddingLeft();
doReturn(0).when(mNavigationBarView).getPaddingRight();
doReturn(0).when(mNavigationBarView).getPaddingStart();
doReturn(0).when(mResources)
.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
// Quickscrub disabled, so the action should be disabled
doReturn(false).when(mNavigationBarView).isQuickScrubEnabled();
assertFalse(action.isEnabled());
doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
// Touch down
MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, x, y);
assertFalse(touch(downEvent));
assertNull(mController.getCurrentAction());
verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
verify(mProxy, times(1)).onMotionEvent(downEvent);
// Move to start trigger action from gesture
MotionEvent moveEvent1 = event(MotionEvent.ACTION_MOVE, x + 100, y);
assertTrue(touch(moveEvent1));
assertEquals(action, mController.getCurrentAction());
verify(action, times(1)).onGestureStart(moveEvent1);
verify(mProxy, times(1)).onQuickScrubStart();
verify(mProxyService, times(1)).notifyQuickScrubStarted();
verify(mNavigationBarView, times(1)).updateSlippery();
verify(mProxy, never()).onMotionEvent(moveEvent1);
// Move again for scrub
float fraction = 3f / 4;
x = (int) (NAVBAR_WIDTH * fraction);
MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, x, y);
assertTrue(touch(moveEvent2));
assertEquals(action, mController.getCurrentAction());
verify(action, times(1)).onGestureMove(x, y);
verify(mProxy, times(1)).onQuickScrubProgress(fraction);
verify(mProxy, never()).onMotionEvent(moveEvent2);
// Action up
MotionEvent upEvent = event(MotionEvent.ACTION_UP, 1, y);
assertFalse(touch(upEvent));
assertNull(mController.getCurrentAction());
verify(action, times(1)).onGestureEnd();
verify(mProxy, times(1)).onQuickScrubEnd();
verify(mProxy, never()).onMotionEvent(upEvent);
}
@Test
public void testQuickStep() throws Exception {
QuickStepAction action = new QuickStepAction(mNavigationBarView, mProxyService);
mController.setGestureActions(action, null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
// Notifications are up, should prevent quickstep
doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
// Swipe up
assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
assertNull(mController.getCurrentAction());
assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
assertNull(mController.getCurrentAction());
assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
// Quickstep disabled, so the action should be disabled
doReturn(false).when(mNavigationBarView).isQuickStepSwipeUpEnabled();
assertFalse(action.isEnabled());
doReturn(true).when(mNavigationBarView).isQuickStepSwipeUpEnabled();
// Swipe up should call proxy events
MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
assertFalse(touch(downEvent));
assertNull(mController.getCurrentAction());
verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
verify(mProxy, times(1)).onMotionEvent(downEvent);
MotionEvent moveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
assertTrue(touch(moveEvent));
assertEquals(action, mController.getCurrentAction());
verify(mProxy, times(1)).onQuickStep(moveEvent);
verify(mProxyService, times(1)).notifyQuickStepStarted();
}
@Test
public void testLongPressPreventDetection() throws Exception {
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, null /* swipeDownAction */,
null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
null /* rightEdgeSwipe */);
// Start the drag up
assertFalse(touch(MotionEvent.ACTION_DOWN, 100, 1));
assertNull(mController.getCurrentAction());
// Long press something on the navigation bar such as Home button
mNavigationBarView.onNavigationButtonLongPress(mock(View.class));
// Swipe right will not start any gestures
MotionEvent motionMoveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
assertFalse(touch(motionMoveEvent));
assertNull(mController.getCurrentAction());
verify(action, never()).startGesture(motionMoveEvent);
// Touch up
assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
verify(action, never()).endGesture();
}
@Test
public void testHitTargetDragged() throws Exception {
ButtonDispatcher button = mock(ButtonDispatcher.class);
FakeLocationView buttonView = spy(new FakeLocationView(mContext, NAVBAR_WIDTH / 2,
NAVBAR_HEIGHT / 2));
doReturn(buttonView).when(button).getCurrentView();
NavigationGestureAction action = mockAction(true);
mController.setGestureActions(action, action, action, action, action, action);
// Setup getting the hit target
doReturn(HIT_TARGET_HOME).when(action).requiresTouchDownHitTarget();
doReturn(true).when(action).allowHitTargetToMoveOverDrag();
doReturn(HIT_TARGET_HOME).when(mNavigationBarView).getDownHitTarget();
doReturn(button).when(mNavigationBarView).getHomeButton();
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
// Portrait
assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_BOTTOM);
// Portrait RTL
assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_BOTTOM);
// Landscape
assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_RIGHT);
// Landscape RTL
assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_RIGHT);
// Seascape
assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_LEFT);
// Seascape RTL
assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT);
}
@Test
public void testNoEdgeActionsTriggerNormalActions() {
doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
NavigationGestureAction swipeUp = mockAction(true);
NavigationGestureAction swipeDown = mockAction(true);
NavigationGestureAction swipeLeft = mockAction(true);
NavigationGestureAction swipeRight = mockAction(true);
mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
null /* swipeLeftFromEdgeAction */,
null /* swipeLeftFromEdgeAction */);
// Swipe Left from Edge
assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH, 1, 5, 1);
// Swipe Right from Edge
assertGestureTriggersAction(swipeRight, 0, 1, NAVBAR_WIDTH, 5);
}
private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL,
int navPos) {
mController.setBarState(isRTL, navPos);
// Swipe up
assertGestureDragsHitTarget(buttonView, 10 /* x1 */, 200 /* y1 */, 0 /* x2 */, 0 /* y2 */,
0 /* dx */, -1 /* dy */);
// Swipe left
assertGestureDragsHitTarget(buttonView, 200 /* x1 */, 10 /* y1 */, 0 /* x2 */, 0 /* y2 */,
-1 /* dx */, 0 /* dy */);
// Swipe right
assertGestureDragsHitTarget(buttonView, 0 /* x1 */, 0 /* y1 */, 200 /* x2 */, 10 /* y2 */,
1 /* dx */, 0 /* dy */);
// Swipe down
assertGestureDragsHitTarget(buttonView, 0 /* x1 */, 0 /* y1 */, 10 /* x2 */, 200 /* y2 */,
0 /* dx */, 1 /* dy */);
}
/**
* Asserts the gesture actually moves the hit target
* @param buttonView button to check if moved, use Mockito.spy on a real object
* @param x1 start x
* @param x2 start y
* @param y1 end x
* @param y2 end y
* @param dx diff in x, if not 0, its sign determines direction, value does not matter
* @param dy diff in y, if not 0, its sign determines direction, value does not matter
*/
private void assertGestureDragsHitTarget(View buttonView, int x1, int y1, int x2, int y2,
int dx, int dy) {
ArgumentCaptor<Float> captor = ArgumentCaptor.forClass(Float.class);
assertFalse(touch(MotionEvent.ACTION_DOWN, x1, y1));
assertTrue(touch(MotionEvent.ACTION_MOVE, x2, y2));
// Verify positions of the button drag
if (dx == 0) {
verify(buttonView, never()).setTranslationX(anyFloat());
} else {
verify(buttonView).setTranslationX(captor.capture());
if (dx < 0) {
assertTrue("Button should have moved left", (float) captor.getValue() < 0);
} else {
assertTrue("Button should have moved right", (float) captor.getValue() > 0);
}
}
if (dy == 0) {
verify(buttonView, never()).setTranslationY(anyFloat());
} else {
verify(buttonView).setTranslationY(captor.capture());
if (dy < 0) {
assertTrue("Button should have moved up", (float) captor.getValue() < 0);
} else {
assertTrue("Button should have moved down", (float) captor.getValue() > 0);
}
}
// Touch up
assertFalse(touch(MotionEvent.ACTION_UP, x2, y2));
verify(buttonView, times(1)).animate();
// Reset button state
reset(buttonView);
}
private MotionEvent event(int action, float x, float y) {
final MotionEvent event = mock(MotionEvent.class);
doReturn(x).when(event).getX();
doReturn(y).when(event).getY();
doReturn(action & MotionEvent.ACTION_MASK).when(event).getActionMasked();
doReturn(action).when(event).getAction();
return event;
}
private boolean touch(int action, float x, float y) {
return touch(event(action, x, y));
}
private boolean touch(MotionEvent event) {
return mController.onInterceptTouchEvent(event);
}
private NavigationGestureAction mockAction(boolean enabled) {
final NavigationGestureAction action = mock(NavigationGestureAction.class);
doReturn(enabled).when(action).isEnabled();
doReturn(HIT_TARGET_NONE).when(action).requiresTouchDownHitTarget();
doReturn(true).when(action).canPerformAction();
return action;
}
private void assertGestureTriggersAction(NavigationGestureAction action, int x1, int y1,
int x2, int y2) {
// Start the drag
assertFalse(touch(MotionEvent.ACTION_DOWN, x1, y1));
assertNull(mController.getCurrentAction());
// Swipe
MotionEvent motionMoveEvent = event(MotionEvent.ACTION_MOVE, x2, y2);
assertTrue(touch(motionMoveEvent));
assertEquals(action, mController.getCurrentAction());
verify(action, times(1)).startGesture(motionMoveEvent);
// Move again
assertTrue(touch(MotionEvent.ACTION_MOVE, x2, y2));
verify(action, times(1)).onGestureMove(x2, y2);
// Touch up
assertFalse(touch(MotionEvent.ACTION_UP, x2, y2));
assertNull(mController.getCurrentAction());
verify(action, times(1)).endGesture();
}
static class FakeLocationView extends View {
private final int mX;
private final int mY;
public FakeLocationView(Context context, int x, int y) {
super(context);
mX = x;
mY = y;
}
@Override
public void getLocationInWindow(int[] outLocation) {
outLocation[0] = mX;
outLocation[1] = mY;
}
}
}