diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java index 395833ffea..6becf0f07e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java @@ -30,6 +30,8 @@ import android.view.ViewConfiguration; import android.view.Window; import android.view.WindowManager; +import androidx.annotation.VisibleForTesting; + import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; @@ -50,12 +52,13 @@ public class StatusBarTouchController implements TouchController { private final Launcher mLauncher; private final SystemUiProxy mSystemUiProxy; - private final float mTouchSlop; + @VisibleForTesting final float mTouchSlop; private int mLastAction; private final SparseArray mDownEvents; /* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/ - private boolean mCanIntercept; + @VisibleForTesting + boolean mCanIntercept; public StatusBarTouchController(Launcher l) { mLauncher = l; @@ -82,9 +85,9 @@ public class StatusBarTouchController implements TouchController { @Override public final boolean onControllerInterceptTouchEvent(MotionEvent ev) { - int action = ev.getActionMasked(); - int idx = ev.getActionIndex(); - int pid = ev.getPointerId(idx); + final int action = ev.getActionMasked(); + final int idx = ev.getActionIndex(); + final int pid = ev.getPointerId(idx); if (action == ACTION_DOWN) { mCanIntercept = canInterceptTouch(ev); if (!mCanIntercept) { @@ -92,14 +95,14 @@ public class StatusBarTouchController implements TouchController { } mDownEvents.clear(); mDownEvents.put(pid, new PointF(ev.getX(), ev.getY())); - } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { + } else if (action == MotionEvent.ACTION_POINTER_DOWN) { // Check!! should only set it only when threshold is not entered. mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx))); } if (!mCanIntercept) { return false; } - if (action == ACTION_MOVE) { + if (action == ACTION_MOVE && mDownEvents.contains(pid)) { float dy = ev.getY(idx) - mDownEvents.get(pid).y; float dx = ev.getX(idx) - mDownEvents.get(pid).x; // Currently input dispatcher will not do touch transfer if there are more than @@ -126,7 +129,6 @@ public class StatusBarTouchController implements TouchController { mLauncher.getStatsLogManager().logger() .log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN); setWindowSlippery(false); - return true; } return true; } @@ -140,7 +142,8 @@ public class StatusBarTouchController implements TouchController { * Touches can slide out of the window but they cannot necessarily slide * back in (unless the other window with touch focus permits it). */ - private void setWindowSlippery(boolean enable) { + @VisibleForTesting + void setWindowSlippery(boolean enable) { Window w = mLauncher.getWindow(); WindowManager.LayoutParams wlp = w.getAttributes(); if (enable) { diff --git a/quickstep/tests/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt b/quickstep/tests/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt new file mode 100644 index 0000000000..b2f13c7c65 --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchControllerTest.kt @@ -0,0 +1,152 @@ +/* + * Copyright (C) 202 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.launcher3.uioverrides.touchcontrollers + +import android.view.MotionEvent +import android.view.WindowManager +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.launcher3.Launcher +import com.android.launcher3.ui.AbstractLauncherUiTest +import com.android.launcher3.ui.TaplTestsLauncher3 +import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class StatusBarTouchControllerTest : AbstractLauncherUiTest() { + @Before + @Throws(Exception::class) + fun setup() { + TaplTestsLauncher3.initialize(this) + } + + @Test + fun interceptActionDown_canIntercept() { + executeOnLauncher { launcher: Launcher? -> + val underTest = StatusBarTouchController(launcher) + assertFalse(underTest.mCanIntercept) + val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + + underTest.onControllerInterceptTouchEvent(downEvent) + + assertTrue(underTest.mCanIntercept) + } + } + + @Test + fun interceptActionMove_handledAndSetSlippery() { + executeOnLauncher { launcher: Launcher -> + val underTest = StatusBarTouchController(launcher) + val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + underTest.onControllerInterceptTouchEvent(downEvent) + val w = launcher.window + assertEquals(0, w.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY) + val moveEvent = + MotionEvent.obtain( + 2, + 2, + MotionEvent.ACTION_MOVE, + underTest.mTouchSlop, + underTest.mTouchSlop + 10, + 0 + ) + + val handled = underTest.onControllerInterceptTouchEvent(moveEvent) + + assertTrue(handled) + assertEquals( + WindowManager.LayoutParams.FLAG_SLIPPERY, + w.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY + ) + } + } + + @Test + fun interceptActionMove_not_handled() { + executeOnLauncher { launcher: Launcher? -> + val underTest = StatusBarTouchController(launcher) + val downEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + underTest.onControllerInterceptTouchEvent(downEvent) + val moveEvent = + MotionEvent.obtain( + 2, + 2, + MotionEvent.ACTION_MOVE, + underTest.mTouchSlop + 10, + underTest.mTouchSlop, + 0 + ) + + val handled = underTest.onControllerInterceptTouchEvent(moveEvent) + + assertFalse(handled) + } + } + + @Test + fun interceptActionMoveAsFirstGestureEvent_notCrashedNorHandled() { + executeOnLauncher { launcher: Launcher? -> + val underTest = StatusBarTouchController(launcher) + underTest.mCanIntercept = true + val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_MOVE, 10f, 10f, 0) + + val handled = underTest.onControllerInterceptTouchEvent(moveEvent) + + assertFalse(handled) + } + } + + @Test + fun handleActionUp_setNotSlippery() { + executeOnLauncher { launcher: Launcher -> + val underTest = StatusBarTouchController(launcher) + underTest.mCanIntercept = true + underTest.setWindowSlippery(true) + val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_UP, 10f, 10f, 0) + + val handled = underTest.onControllerTouchEvent(moveEvent) + + assertTrue(handled) + assertEquals( + 0, + launcher.window.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY + ) + } + } + + @Test + fun handleActionCancel_setNotSlippery() { + executeOnLauncher { launcher: Launcher -> + val underTest = StatusBarTouchController(launcher) + underTest.mCanIntercept = true + underTest.setWindowSlippery(true) + val moveEvent = MotionEvent.obtain(2, 2, MotionEvent.ACTION_CANCEL, 10f, 10f, 0) + + val handled = underTest.onControllerTouchEvent(moveEvent) + + assertTrue(handled) + assertEquals( + 0, + launcher.window.attributes.flags and WindowManager.LayoutParams.FLAG_SLIPPERY + ) + } + } +}