diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 6b0d7a3e87..1f6c506d3a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -26,7 +26,6 @@ import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS; -import static com.android.quickstep.GestureState.STATE_OVERSCROLL_WINDOW_CREATED; import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; @@ -431,6 +430,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC @Override public boolean allowInterceptByParent() { - return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED); + return !mPassedPilferInputSlop; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java index 1941830134..c49b8f2d14 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java @@ -24,11 +24,9 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Utilities.squaredHypot; -import static java.lang.Math.abs; - import android.content.Context; import android.graphics.PointF; -import android.util.Log; +import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -46,31 +44,24 @@ import com.android.systemui.shared.system.InputMonitorCompat; * Input consumer for handling events to pass to an {@code OverscrollPlugin}. */ public class OverscrollInputConsumer extends DelegateInputConsumer { + private static final String TAG = "OverscrollInputConsumer"; - private static final boolean DEBUG_LOGS_ENABLED = false; - private static void debugPrint(String log) { - if (DEBUG_LOGS_ENABLED) { - Log.v(TAG, log); - } - } private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); private final PointF mStartDragPos = new PointF(); private final int mAngleThreshold; - private final int mFlingDistanceThresholdPx; - private final int mFlingVelocityThresholdPx; + private final float mFlingThresholdPx; private int mActivePointerId = -1; private boolean mPassedSlop = false; - // True if we set ourselves as active, meaning we no longer pass events to the delegate. - private boolean mPassedActiveThreshold = false; - private final float mSquaredActiveThreshold; + private final float mSquaredSlop; private final GestureState mGestureState; @Nullable private final OverscrollPlugin mPlugin; + private final GestureDetector mGestureDetector; @Nullable private RecentsView mRecentsView; @@ -81,19 +72,15 @@ public class OverscrollInputConsumer extends DelegateInputConsumer { mAngleThreshold = context.getResources() .getInteger(R.integer.assistant_gesture_corner_deg_threshold); - mFlingDistanceThresholdPx = (int) context.getResources() - .getDimension(R.dimen.gestures_overscroll_fling_threshold); - mFlingVelocityThresholdPx = ViewConfiguration.get(context).getScaledMinimumFlingVelocity(); + mFlingThresholdPx = context.getResources() + .getDimension(R.dimen.gestures_overscroll_fling_threshold); mGestureState = gestureState; mPlugin = plugin; float slop = ViewConfiguration.get(context).getScaledTouchSlop(); mSquaredSlop = slop * slop; - - float dragThreshold = (int) context.getResources() - .getDimension(R.dimen.gestures_overscroll_drag_threshold); - mSquaredActiveThreshold = dragThreshold * dragThreshold; + mGestureDetector = new GestureDetector(context, new FlingGestureListener()); } @Override @@ -103,27 +90,12 @@ public class OverscrollInputConsumer extends DelegateInputConsumer { @Override public void onMotionEvent(MotionEvent ev) { - if (mPlugin == null) { - return; - } - switch (ev.getActionMasked()) { case ACTION_DOWN: { - if (mPlugin.blockOtherGestures()) { - // When an Activity is visible, blocking other gestures prevents the Activity - // from disappearing upon ACTION_DOWN in the navigation bar. (it will reappear - // on ACTION_MOVE or ACTION_UP) - debugPrint("Becoming active on ACTION_DOWN"); - if (mState != STATE_ACTIVE) { - setActive(ev); - } - } mActivePointerId = ev.getPointerId(0); mDownPos.set(ev.getX(), ev.getY()); mLastPos.set(mDownPos); - mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(), - (int) Math.sqrt(mSquaredActiveThreshold), mFlingDistanceThresholdPx, - mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity()); + break; } case ACTION_POINTER_DOWN: { @@ -159,82 +131,53 @@ public class OverscrollInputConsumer extends DelegateInputConsumer { } mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); - float squaredDist = squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y); - - - if (!mPassedSlop) { // Normal gesture, ensure we pass the slop before we start tracking the gesture - if (squaredDist > mSquaredSlop) { - debugPrint("passed slop"); + if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) + > mSquaredSlop) { + mPassedSlop = true; mStartDragPos.set(mLastPos.x, mLastPos.y); if (isOverscrolled()) { - debugPrint("setting STATE_OVERSCROLL_WINDOW_CREATED"); - mGestureState.setState(GestureState.STATE_OVERSCROLL_WINDOW_CREATED); - if (!mPlugin.allowsUnderlyingActivityOverscroll() - && (mState != STATE_ACTIVE)) { - debugPrint("setting active gesture handler to overscroll to " - + "prevent losing active touch when Activity starts"); - setActive(ev); - } - } - } else { - debugPrint("Not past slop"); - } - } - - if (mPassedSlop && !mPassedActiveThreshold && isOverscrolled()) { - if ((squaredDist > mSquaredActiveThreshold)) { - debugPrint("Past slop and past threshold, set active"); - - mPassedActiveThreshold = true; - if (mState != STATE_ACTIVE) { setActive(ev); + + if (mPlugin != null) { + mPlugin.onTouchStart(getDeviceState(), getUnderlyingActivity()); + } + } else { + mState = STATE_DELEGATE_ACTIVE; } } } - if (mPassedSlop && mState != STATE_DELEGATE_ACTIVE && isOverscrolled()) { - debugPrint("Relaying touch event"); - mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(), - (int) Math.sqrt(mSquaredActiveThreshold), mFlingDistanceThresholdPx, - mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity()); + if (mPassedSlop && mState != STATE_DELEGATE_ACTIVE && isOverscrolled() + && mPlugin != null) { + mPlugin.onTouchTraveled(getDistancePx()); } break; } case ACTION_CANCEL: case ACTION_UP: - if (mPassedSlop && isOverscrolled()) { - mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(), - (int) Math.sqrt(mSquaredActiveThreshold), mFlingDistanceThresholdPx, - mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity()); + if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop && mPlugin != null) { + mPlugin.onTouchEnd(getDistancePx()); } mPassedSlop = false; - mPassedActiveThreshold = false; mState = STATE_INACTIVE; break; } + if (mState != STATE_DELEGATE_ACTIVE) { + mGestureDetector.onTouchEvent(ev); + } + if (mState != STATE_ACTIVE) { mDelegate.onMotionEvent(ev); } } private boolean isOverscrolled() { - if (mPlugin.blockOtherGestures()) { - // When an Activity is visible, this `InputConsumer` immediately becomes - // the active gesture handler to prevent the Activity from disappearing on TOUCH_DOWN - // in the navbar. - // - // Returning `true` ensures that case will still result in touches being handled, - // instead of dropping touches until the gesture reaches the thresholds calculated - // below. - return true; - } - if (mRecentsView == null) { BaseDraggingActivity activity = mGestureState.getActivityInterface() .getCreatedActivity(); @@ -253,10 +196,9 @@ public class OverscrollInputConsumer extends DelegateInputConsumer { || mRecentsView.getRunningTaskIndex() <= maxIndex); // Check if the gesture is within our angle threshold of horizontal - float deltaY = abs(mLastPos.y - mDownPos.y); - float deltaX = abs(mDownPos.x - mLastPos.x); - - boolean angleInBounds = (Math.toDegrees(Math.atan2(deltaY, deltaX)) < mAngleThreshold); + float deltaY = Math.abs(mLastPos.y - mDownPos.y); + float deltaX = mDownPos.x - mLastPos.x; // Positive if this is a gesture to the left + boolean angleInBounds = Math.toDegrees(Math.atan2(deltaY, deltaX)) < mAngleThreshold; return atRightMostApp && angleInBounds; } @@ -277,22 +219,35 @@ public class OverscrollInputConsumer extends DelegateInputConsumer { return deviceState; } - private int getHorizontalDistancePx() { - return (int) (mLastPos.x - mDownPos.x); - } - - private int getVerticalDistancePx() { - return (int) (mLastPos.y - mDownPos.y); + private int getDistancePx() { + return (int) Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y); } private String getUnderlyingActivity() { - // Overly defensive, got guidance on code review that something in the chain of - // `mGestureState.getRunningTask().topActivity` can be null and thus cause a null pointer - // exception to be thrown, but we aren't sure which part can be null. - if ((mGestureState == null) || (mGestureState.getRunningTask() == null) - || (mGestureState.getRunningTask().topActivity == null)) { - return ""; - } return mGestureState.getRunningTask().topActivity.flattenToString(); } + + private class FlingGestureListener extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (isValidAngle(velocityX, -velocityY) + && getDistancePx() >= mFlingThresholdPx + && mState != STATE_DELEGATE_ACTIVE) { + + if (mPlugin != null) { + mPlugin.onFling(-velocityX); + } + } + return true; + } + + private boolean isValidAngle(float deltaX, float deltaY) { + float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX)); + // normalize so that angle is measured clockwise from horizontal in the bottom right + // corner and counterclockwise from horizontal in the bottom left corner + + angle = angle > 90 ? 180 - angle : angle; + return (angle < mAngleThreshold); + } + } } diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 6624ff95c5..b06dc6b01d 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -79,7 +79,6 @@ 40dp - 136dp 40dp diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java index 209412a295..544f420811 100644 --- a/quickstep/src/com/android/quickstep/GestureState.java +++ b/quickstep/src/com/android/quickstep/GestureState.java @@ -106,10 +106,6 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL public static final int STATE_RECENTS_ANIMATION_ENDED = getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED"); - // Called when we create an overscroll window when swiping right to left on the most recent app - public static final int STATE_OVERSCROLL_WINDOW_CREATED = - getFlagForIndex("STATE_OVERSCROLL_WINDOW_CREATED"); - // Called when RecentsView stops scrolling and settles on a TaskView. public static final int STATE_RECENTS_SCROLLING_FINISHED = getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED"); diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 69193399f7..78d194bd98 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -110,9 +110,6 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag( "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture"); - public static final BooleanFlag ENABLE_QUICK_CAPTURE_WINDOW = getDebugFlag( - "ENABLE_QUICK_CAPTURE_WINDOW", false, "Use window to host quick capture"); - public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag( "FORCE_LOCAL_OVERSCROLL_PLUGIN", false, "Use a launcher-provided OverscrollPlugin if available"); diff --git a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java index a434d078bc..28a9193bec 100644 --- a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java +++ b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java @@ -15,8 +15,6 @@ */ package com.android.systemui.plugins; -import android.view.MotionEvent; - import com.android.systemui.plugins.annotations.ProvidesInterface; /** @@ -30,7 +28,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface; public interface OverscrollPlugin extends Plugin { String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL"; - int VERSION = 4; + int VERSION = 3; String DEVICE_STATE_LOCKED = "Locked"; String DEVICE_STATE_LAUNCHER = "Launcher"; @@ -43,33 +41,33 @@ public interface OverscrollPlugin extends Plugin { boolean isActive(); /** - * Called when a touch has been recognized as an overscroll gesture. - * @param horizontalDistancePx Horizontal distance from the last finger location to the finger - * location when it first touched the screen. - * @param verticalDistancePx Horizontal distance from the last finger location to the finger - * location when it first touched the screen. - * @param thresholdPx Minimum distance for gesture. - * @param flingDistanceThresholdPx Minimum distance for gesture by fling. - * @param flingVelocityThresholdPx Minimum velocity for gesture by fling. + * Called when a touch is down and has been recognized as an overscroll gesture. + * A call of this method will always result in `onTouchUp` being called, and possibly + * `onFling` as well. + * * @param deviceState String representing the current device state * @param underlyingActivity String representing the currently active Activity */ - void onTouchEvent(MotionEvent event, - int horizontalDistancePx, - int verticalDistancePx, - int thresholdPx, - int flingDistanceThresholdPx, - int flingVelocityThresholdPx, - String deviceState, - String underlyingActivity); + void onTouchStart(String deviceState, String underlyingActivity); /** - * @return `true` if overscroll gesture handling should override all other gestures. + * Called when a touch that was previously recognized has moved. + * + * @param px distance between the position of touch on this update and the position of the + * touch when it was initially recognized. */ - boolean blockOtherGestures(); + void onTouchTraveled(int px); /** - * @return `true` if the overscroll gesture can pan the underlying app. + * Called when a touch that was previously recognized has ended. + * + * @param px distance between the position of touch on this update and the position of the + * touch when it was initially recognized. */ - boolean allowsUnderlyingActivityOverscroll(); + void onTouchEnd(int px); + + /** + * Called when the user starts Compose with a fling. `onTouchUp` will also be called. + */ + void onFling(float velocity); }