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
This commit is contained in:
Riddle Hsu
2024-07-15 15:43:12 +08:00
parent 103191c589
commit cbc84d96ef
4 changed files with 74 additions and 6 deletions
@@ -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);
@@ -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 */);
}
}
@@ -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);
}
@@ -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);