Fix NPE of handling ACTION_MOVE in StatusBarTouchController and added unit test
Fix: 282945183
Test: N/A
Change-Id: I96680f04a6946129b14c365e2300f408dfe8f0c3
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 395833f..6becf0f 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.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 @@
private final Launcher mLauncher;
private final SystemUiProxy mSystemUiProxy;
- private final float mTouchSlop;
+ @VisibleForTesting final float mTouchSlop;
private int mLastAction;
private final SparseArray<PointF> 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 @@
@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 @@
}
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 @@
mLauncher.getStatsLogManager().logger()
.log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN);
setWindowSlippery(false);
- return true;
}
return true;
}
@@ -140,7 +142,8 @@
* 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 0000000..b2f13c7
--- /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
+ )
+ }
+ }
+}