From cbc84d96ef5c9bbcae8184f9795ebc2348b940e2 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Mon, 15 Jul 2024 15:43:12 +0800 Subject: [PATCH] Avoid changing transform when touching in progress After swiping from a landscape app to portrait home, the touch orientation may be changed if there the touch event comes in a short time (e.g. click app icon immediately): DOWN MOVE... SimpleOrientationTouchTransformer#onDisplayInfoChanged MOVE... (start to transform with inconsistent orientation) UP That could disturb the gesture detection: OverviewInputConsumer#onMotionEvent > BaseDragLayer#proxyTouchEvent onInterceptTouchEvent, findControllerToHandleTouch > PortraitStatesTouchController#onControllerInterceptTouchEvent > BaseSwipeDetector#onTouchEvent which may show the app drawer unexpectedly. http://recall/-/b2qm27pJZxFIWQccA9qE9Q/ggLXlE5kf7AWMOjUc0FCUU Bug: 351755391 Flag: EXEMPT bugfix Test: atest NexusLauncherTests: \ com.android.quickstep.OrientationTouchTransformerTest# \ testSimpleOrientationTouchTransformer Change-Id: Ic0e9d8292606837feb4775663abb60229c90e9c5 --- .../android/quickstep/OrientationRectF.java | 4 ++- .../SimpleOrientationTouchTransformer.java | 34 +++++++++++++++++-- .../quickstep/util/InputConsumerProxy.java | 13 +++++-- .../OrientationTouchTransformerTest.java | 29 ++++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/OrientationRectF.java b/quickstep/src/com/android/quickstep/OrientationRectF.java index aa01b05b47..2b7ecb26e4 100644 --- a/quickstep/src/com/android/quickstep/OrientationRectF.java +++ b/quickstep/src/com/android/quickstep/OrientationRectF.java @@ -67,13 +67,15 @@ public class OrientationRectF extends RectF { } public boolean applyTransform(MotionEvent event, int deltaRotation, boolean forceTransform) { + if (deltaRotation == 0) { + return contains(event.getX(), event.getY()); + } mTmpMatrix.reset(); postDisplayRotation(deltaRotation, mHeight, mWidth, mTmpMatrix); if (forceTransform) { if (DEBUG) { Log.d(TAG, "Transforming rotation due to forceTransform, " + "deltaRotation: " + deltaRotation - + "mRotation: " + mRotation + " this: " + this); } event.applyTransform(mTmpMatrix); diff --git a/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java index 29a57fcfc1..526464330d 100644 --- a/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java @@ -34,12 +34,18 @@ public class SimpleOrientationTouchTransformer implements private final Context mContext; private OrientationRectF mOrientationRectF; + private OrientationRectF mTouchingOrientationRectF; + private int mViewRotation; public SimpleOrientationTouchTransformer(Context context) { + this(context, DisplayController.INSTANCE.get(context)); + } + + @androidx.annotation.VisibleForTesting + public SimpleOrientationTouchTransformer(Context context, DisplayController displayController) { mContext = context; - DisplayController.INSTANCE.get(context).addChangeListener(this); - onDisplayInfoChanged(context, DisplayController.INSTANCE.get(context).getInfo(), - CHANGE_ALL); + displayController.addChangeListener(this); + onDisplayInfoChanged(context, displayController.getInfo(), CHANGE_ALL); } @Override @@ -56,7 +62,29 @@ public class SimpleOrientationTouchTransformer implements info.rotation); } + /** + * Called when the touch is started. This preserves the touching orientation until the touch is + * done (i.e. ACTION_CANCEL or ACTION_UP). So the transform won't produce inconsistent position + * if display is changed during the touch. + */ + public void updateTouchingOrientation(int viewRotation) { + mViewRotation = viewRotation; + mTouchingOrientationRectF = new OrientationRectF(mOrientationRectF.left, + mOrientationRectF.top, mOrientationRectF.right, mOrientationRectF.bottom, + mOrientationRectF.getRotation()); + } + + /** Called when the touch is finished. */ + public void clearTouchingOrientation() { + mTouchingOrientationRectF = null; + } + public void transform(MotionEvent ev, int rotation) { + if (mTouchingOrientationRectF != null) { + mTouchingOrientationRectF.applyTransformToRotation(ev, mViewRotation, + true /* forceTransform */); + return; + } mOrientationRectF.applyTransformToRotation(ev, rotation, true /* forceTransform */); } } diff --git a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java index cb44a1a0d8..fcf9ab1ad4 100644 --- a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java +++ b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java @@ -108,19 +108,28 @@ public class InputConsumerProxy { return false; } + final SimpleOrientationTouchTransformer touchTransformer = + SimpleOrientationTouchTransformer.INSTANCE.get(mContext); + final int viewRotation = mRotationSupplier.get(); + final boolean needTransform = viewRotation != ev.getSurfaceRotation(); if (action == ACTION_DOWN) { mTouchInProgress = true; + if (needTransform) { + touchTransformer.updateTouchingOrientation(viewRotation); + } initInputConsumerIfNeeded(/* isFromTouchDown= */ true); } else if (action == ACTION_CANCEL || action == ACTION_UP) { // Finish any pending actions mTouchInProgress = false; + touchTransformer.clearTouchingOrientation(); if (mDestroyPending) { destroy(); } } if (mInputConsumer != null) { - SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev, - mRotationSupplier.get()); + if (needTransform) { + touchTransformer.transform(ev, viewRotation); + } mInputConsumer.onMotionEvent(ev); } diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java index 298dd6cae0..f5d082dd5c 100644 --- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java +++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java @@ -21,6 +21,7 @@ import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.launcher3.util.NavigationMode.NO_BUTTON; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -288,6 +289,34 @@ public class OrientationTouchTransformerTest { assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY())); } + @Test + public void testSimpleOrientationTouchTransformer() { + final DisplayController displayController = mock(DisplayController.class); + doReturn(mInfo).when(displayController).getInfo(); + final SimpleOrientationTouchTransformer transformer = + new SimpleOrientationTouchTransformer(getApplicationContext(), displayController); + final MotionEvent move1 = generateMotionEvent(MotionEvent.ACTION_MOVE, 100, 10); + transformer.transform(move1, Surface.ROTATION_90); + // The position is transformed to 90 degree. + assertEquals(10, move1.getX(), 0f /* delta */); + assertEquals(NORMAL_SCREEN_SIZE.getWidth() - 100, move1.getY(), 0f /* delta */); + + // If the touching state is specified, the position is still transformed to 90 degree even + // if the given rotation is changed. + final MotionEvent move2 = generateMotionEvent(MotionEvent.ACTION_MOVE, 100, 10); + transformer.updateTouchingOrientation(Surface.ROTATION_90); + transformer.transform(move2, Surface.ROTATION_0); + assertEquals(move1.getX(), move2.getX(), 0f /* delta */); + assertEquals(move1.getY(), move2.getY(), 0f /* delta */); + + // If the touching state is cleared, it restores to use the given rotation. + final MotionEvent move3 = generateMotionEvent(MotionEvent.ACTION_MOVE, 100, 10); + transformer.clearTouchingOrientation(); + transformer.transform(move3, Surface.ROTATION_0); + assertEquals(100, move3.getX(), 0f /* delta */); + assertEquals(10, move3.getY(), 0f /* delta */); + } + private DisplayController.Info createDisplayInfo(Size screenSize, int rotation) { Point displaySize = new Point(screenSize.getWidth(), screenSize.getHeight()); RotationUtils.rotateSize(displaySize, rotation);