diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5cc5f10e4f..9a04b40a4c 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -293,7 +293,6 @@ public abstract class AbsSwipeUpHandler, private boolean mPassedOverviewThreshold; private boolean mGestureStarted; private boolean mLogDirectionUpOrLeft = true; - private PointF mDownPos; private boolean mIsLikelyToStartNewTask; private final long mTouchTimeMs; @@ -991,10 +990,9 @@ public abstract class AbsSwipeUpHandler, /** * @param endVelocity The velocity in the direction of the nav bar to the middle of the screen. * @param velocity The x and y components of the velocity when the gesture ends. - * @param downPos The x and y value of where the gesture started. */ @UiThread - public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) { + public void onGestureEnded(float endVelocity, PointF velocity) { float flingThreshold = mContext.getResources() .getDimension(R.dimen.quickstep_fling_threshold_speed); boolean isFling = mGestureStarted && !mIsMotionPaused @@ -1006,7 +1004,6 @@ public abstract class AbsSwipeUpHandler, } else { mLogDirectionUpOrLeft = velocity.x < 0; } - mDownPos = downPos; Runnable handleNormalGestureEndCallback = () -> handleNormalGestureEnd(endVelocity, isFling, velocity, /* isCancel= */ false); if (mRecentsView != null) { @@ -1281,7 +1278,7 @@ public abstract class AbsSwipeUpHandler, } private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) { - if (mDp == null || !mDp.isGestureMode || mDownPos == null) { + if (mDp == null || !mDp.isGestureMode) { // We probably never received an animation controller, skip logging. return; } diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index 875b72cb34..61891dff5b 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -231,7 +231,7 @@ public class OverviewCommandHelper { public void onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets) { activityInterface.runOnInitBackgroundStateUI(() -> - interactionHandler.onGestureEnded(0, new PointF(), new PointF())); + interactionHandler.onGestureEnded(0, new PointF())); cmd.removeListener(this); } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java new file mode 100644 index 0000000000..23738224eb --- /dev/null +++ b/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2022 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.quickstep.inputconsumers; + +import static android.view.MotionEvent.AXIS_GESTURE_X_OFFSET; +import static android.view.MotionEvent.AXIS_GESTURE_Y_OFFSET; +import static android.view.MotionEvent.INVALID_POINTER_ID; + +import static com.android.launcher3.Utilities.getTrackpadMotionEventScale; +import static com.android.launcher3.Utilities.isTrackpadMotionEvent; + +import android.content.Context; +import android.graphics.PointF; +import android.view.MotionEvent; + +import com.android.quickstep.util.NavBarPosition; + +/** + * A motion event handler that can tracks the states of a gesture, whether it's from on-screen + * touch or trackpad gesture. + */ +public class MotionEventsHandler { + + private final PointF mDownPos = new PointF(); + private final PointF mLastPos = new PointF(); + private final int mScale; + + private int mActivePointerId = INVALID_POINTER_ID; + + private float mCurrentTrackpadOffsetX = 0; + private float mCurrentTrackpadOffsetY = 0; + + public MotionEventsHandler(Context context) { + mScale = getTrackpadMotionEventScale(context); + } + + public int getActivePointerId() { + return mActivePointerId; + } + + public void onActionDown(MotionEvent ev) { + mActivePointerId = ev.getPointerId(0); + if (isTrackpadMotionEvent(ev)) { + mCurrentTrackpadOffsetX = ev.getAxisValue(AXIS_GESTURE_X_OFFSET); + mCurrentTrackpadOffsetY = ev.getAxisValue(AXIS_GESTURE_Y_OFFSET); + } else { + mDownPos.set(ev.getX(), ev.getY()); + mLastPos.set(mDownPos); + } + } + + public void onActionMove(MotionEvent ev) { + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (isTrackpadMotionEvent(ev)) { + mCurrentTrackpadOffsetX += ev.getAxisValue(AXIS_GESTURE_X_OFFSET, pointerIndex) + * mScale; + mCurrentTrackpadOffsetY += ev.getAxisValue(AXIS_GESTURE_Y_OFFSET, pointerIndex) + * mScale; + } else { + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + } + } + + public void onActionPointerUp(MotionEvent ev) { + int ptrIdx = ev.getActionIndex(); + int ptrId = ev.getPointerId(ptrIdx); + if (ptrId == mActivePointerId && !isTrackpadMotionEvent(ev)) { + final int newPointerIdx = ptrIdx == 0 ? 1 : 0; + mDownPos.set( + ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), + ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); + mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); + mActivePointerId = ev.getPointerId(newPointerIdx); + } + } + + public float getDisplacement(MotionEvent ev, NavBarPosition mNavBarPosition) { + if (mNavBarPosition.isRightEdge()) { + if (isTrackpadMotionEvent(ev)) { + return mCurrentTrackpadOffsetX; + } + return ev.getX() - mDownPos.x; + } else if (mNavBarPosition.isLeftEdge()) { + if (isTrackpadMotionEvent(ev)) { + return -mCurrentTrackpadOffsetX; + } + return mDownPos.x - ev.getX(); + } else { + if (isTrackpadMotionEvent(ev)) { + return mCurrentTrackpadOffsetY; + } + return ev.getY() - mDownPos.y; + } + } + + public float getDisplacementX(MotionEvent ev) { + return isTrackpadMotionEvent(ev) ? mCurrentTrackpadOffsetX : mLastPos.x - mDownPos.x; + } + + public float getDisplacementY(MotionEvent ev) { + return isTrackpadMotionEvent(ev) ? mCurrentTrackpadOffsetY : mLastPos.y - mDownPos.y; + } +} diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 9d269fbc9e..3d972c614f 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -26,6 +26,8 @@ import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.launcher3.PagedView.ACTION_MOVE_ALLOW_EASY_FLING; import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; +import static com.android.launcher3.Utilities.getXVelocity; +import static com.android.launcher3.Utilities.getYVelocity; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS; @@ -115,9 +117,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private final FinishImmediatelyHandler mCleanupHandler = new FinishImmediatelyHandler(); private final boolean mIsDeferredDownTarget; - private final PointF mDownPos = new PointF(); - private final PointF mLastPos = new PointF(); - private int mActivePointerId = INVALID_POINTER_ID; + + private final MotionEventsHandler mMotionEventsHandler; // Distance after which we start dragging the window. private final float mTouchSlop; @@ -166,6 +167,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mVelocityTracker = VelocityTracker.obtain(); mInputMonitorCompat = inputMonitorCompat; mInputEventReceiver = inputEventReceiver; + mMotionEventsHandler = new MotionEventsHandler(base); TaskbarUIController controller = mActivityInterface.getTaskbarController(); mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed(); @@ -236,9 +238,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT, FLAG_CHECK_FOR_RACE_CONDITIONS); - mActivePointerId = ev.getPointerId(0); - mDownPos.set(ev.getX(), ev.getY()); - mLastPos.set(mDownPos); + mMotionEventsHandler.onActionDown(ev); // Start the window animation on down to give more time for launcher to draw if the // user didn't start the gesture over the back button @@ -260,27 +260,20 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC break; } case ACTION_POINTER_UP: { - int ptrIdx = ev.getActionIndex(); - int ptrId = ev.getPointerId(ptrIdx); - if (ptrId == mActivePointerId) { - final int newPointerIdx = ptrIdx == 0 ? 1 : 0; - mDownPos.set( - ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), - ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); - mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); - mActivePointerId = ev.getPointerId(newPointerIdx); - } + mMotionEventsHandler.onActionPointerUp(ev); break; } case ACTION_MOVE: { - int pointerIndex = ev.findPointerIndex(mActivePointerId); + int pointerIndex = ev.findPointerIndex(mMotionEventsHandler.getActivePointerId()); if (pointerIndex == INVALID_POINTER_ID) { break; } - mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); - float displacement = getDisplacement(ev); - float displacementX = mLastPos.x - mDownPos.x; - float displacementY = mLastPos.y - mDownPos.y; + + mMotionEventsHandler.onActionMove(ev); + final float displacement = mMotionEventsHandler.getDisplacement(ev, + mNavBarPosition); + final float displacementX = mMotionEventsHandler.getDisplacementX(ev); + final float displacementY= mMotionEventsHandler.getDisplacementY(ev); if (!mPassedWindowMoveSlop) { if (!mIsDeferredDownTarget) { @@ -359,8 +352,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC case ACTION_CANCEL: case ACTION_UP: { if (DEBUG_FAILED_QUICKSWITCH && !mPassedWindowMoveSlop) { - float displacementX = mLastPos.x - mDownPos.x; - float displacementY = mLastPos.y - mDownPos.y; + final float displacementX = mMotionEventsHandler.getDisplacementX(ev); + final float displacementY = mMotionEventsHandler.getDisplacementY(ev); Log.d("Quickswitch", "mPassedWindowMoveSlop=false" + " disp=" + squaredHypot(displacementX, displacementY) + " slop=" + mSquaredTouchSlop); @@ -423,16 +416,18 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mInteractionHandler.onGestureCancelled(); } else { mVelocityTracker.computeCurrentVelocity(PX_PER_MS); - float velocityX = mVelocityTracker.getXVelocity(mActivePointerId); - float velocityY = mVelocityTracker.getYVelocity(mActivePointerId); + int activePointerId = mMotionEventsHandler.getActivePointerId(); + float velocityX = getXVelocity(mVelocityTracker, ev, activePointerId); + float velocityY = getYVelocity(mVelocityTracker, ev, activePointerId); float velocity = mNavBarPosition.isRightEdge() ? velocityX : mNavBarPosition.isLeftEdge() ? -velocityX : velocityY; - mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement); - mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY), - mDownPos); + mInteractionHandler.updateDisplacement( + mMotionEventsHandler.getDisplacement(ev, mNavBarPosition) + - mStartDisplacement); + mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY)); } } else { // Since we start touch tracking on DOWN, we may reach this state without actually @@ -495,16 +490,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } } - private float getDisplacement(MotionEvent ev) { - if (mNavBarPosition.isRightEdge()) { - return ev.getX() - mDownPos.x; - } else if (mNavBarPosition.isLeftEdge()) { - return mDownPos.x - ev.getX(); - } else { - return ev.getY() - mDownPos.y; - } - } - @Override public boolean allowInterceptByParent() { return !mPassedPilferInputSlop; diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index c11de7f204..3c05fe63eb 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -71,6 +71,7 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; +import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.animation.Interpolator; @@ -181,6 +182,8 @@ public final class Utilities { public static boolean IS_RUNNING_IN_TEST_HARNESS = ActivityManager.isRunningInTestHarness(); + private static final int TRACKPAD_GESTURE_SCALE = 60; + public static void enableRunningInTestHarnessForTests() { IS_RUNNING_IN_TEST_HARNESS = true; } @@ -927,6 +930,38 @@ public final class Utilities { && (event.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN; } + public static int getTrackpadMotionEventScale(Context context) { + return ViewConfiguration.get(context).getScaledTouchSlop() * TRACKPAD_GESTURE_SCALE; + } + + public static float getXVelocity(VelocityTracker velocityTracker, MotionEvent event, + int pointerId) { + // Will be enabled after ag/20353570 is submitted +// if (isTrackpadMotionEvent(event)) { +// return velocityTracker.getAxisVelocity(AXIS_GESTURE_X_OFFSET, pointerId); +// } else { + return velocityTracker.getXVelocity(pointerId); +// } + } + + public static float getXVelocity(VelocityTracker velocityTracker, MotionEvent event) { + return getXVelocity(velocityTracker, event, -1 /* ACTIVE_POINTER_ID */); + } + + public static float getYVelocity(VelocityTracker velocityTracker, MotionEvent event, + int pointerId) { + // Will be enabled after ag/20353570 is submitted +// if (isTrackpadMotionEvent(event)) { +// return velocityTracker.getAxisVelocity(AXIS_GESTURE_Y_OFFSET, pointerId); +// } else { + return velocityTracker.getYVelocity(pointerId); +// } + } + + public static float getYVelocity(VelocityTracker velocityTracker, MotionEvent event) { + return getYVelocity(velocityTracker, event, -1 /* ACTIVE_POINTER_ID */); + } + public static boolean bothNull(@Nullable Object a, @Nullable Object b) { return a == null && b == null; }