From a9156a05c418e6505d7ccc4636bbb2b70628181a Mon Sep 17 00:00:00 2001 From: James O'Leary Date: Fri, 20 Dec 2019 14:24:10 -0500 Subject: [PATCH] Compose overscroll gesture updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes for the latest Compose prototype: 1. Pass the identity of the underlying app to the Overscroll plugin. 2. Enable the Compose gesture over an app when there's a Recents extra card plugin active (otherwise the current app won't count as the rightmost one). Some changes to the gesture: - Angle decreased from 35° to 25° to remove overlap with Assistant gesture - Distance increased from 8 to 110 dp. 110 dp is 2x the Assistant gesture and roughly the same as scrubbing into an app from Home. - Fling detection added; uses same distance threshold, 110 dp. - If a touch was recognized as another gesture, the touch will not be reinterpreted as a Compose gesture, no matter what touch movement occurs - Fixes issue where Assistant + Compose could both be triggered - Fixes issue where scrubbing apps to the left, then back to the right, would bring in Compose. i.e. if a touch down + touch movement starts bringing in Assistant UI elements, then, the user moves their touch below the Assistant angle, the Compose gesture will not start being recognized - Gesture length required for fling lowered from 110 dp to 40 dp, per tuning with PM. Bug: b/146508473 Change-Id: I414573d1a92684d1d992837a5f1df522346ec211 --- .../quickstep/TouchInteractionService.java | 4 +- .../OverscrollInputConsumer.java | 77 +++++++++++++++++-- .../quickstep/views/LauncherRecentsView.java | 5 ++ .../android/quickstep/views/RecentsView.java | 5 ++ quickstep/res/values/dimens.xml | 3 + .../launcher3/config/FeatureFlags.java | 2 +- .../systemui/plugins/OverscrollPlugin.java | 41 ++++++++-- 7 files changed, 122 insertions(+), 15 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index bafb2ef7a6..29df5cceba 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -484,7 +484,9 @@ public class TouchInteractionService extends Service implements PluginListener extends Del private static final String TAG = "OverscrollInputConsumer"; - private static final int ANGLE_THRESHOLD = 35; // Degrees - private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); private final PointF mStartDragPos = new PointF(); + private final int mAngleThreshold; + private final float mFlingThresholdPx; private int mActivePointerId = -1; private boolean mPassedSlop = false; @@ -60,19 +63,28 @@ public class OverscrollInputConsumer extends Del private final Context mContext; private final GestureState mGestureState; - @Nullable private final OverscrollPlugin mPlugin; + @Nullable + private final OverscrollPlugin mPlugin; + private final GestureDetector mGestureDetector; private RecentsView mRecentsView; public OverscrollInputConsumer(Context context, GestureState gestureState, InputConsumer delegate, InputMonitorCompat inputMonitor, OverscrollPlugin plugin) { super(delegate, inputMonitor); + + mAngleThreshold = context.getResources() + .getInteger(R.integer.assistant_gesture_corner_deg_threshold); + mFlingThresholdPx = context.getResources() + .getDimension(R.dimen.gestures_overscroll_fling_threshold); mContext = context; mGestureState = gestureState; mPlugin = plugin; float slop = ViewConfiguration.get(context).getScaledTouchSlop(); + mSquaredSlop = slop * slop; + mGestureDetector = new GestureDetector(context, new FlingGestureListener()); gestureState.getActivityInterface().createActivityInitListener(this::onActivityInit) .register(); @@ -139,21 +151,29 @@ public class OverscrollInputConsumer extends Del mPassedSlop = true; mStartDragPos.set(mLastPos.x, mLastPos.y); - if (isOverscrolled()) { setActive(ev); + + if (mPlugin != null) { + mPlugin.onTouchStart(getDeviceState(), getUnderlyingActivity()); + } } else { mState = STATE_DELEGATE_ACTIVE; } } } + if (mPassedSlop && mState != STATE_DELEGATE_ACTIVE && isOverscrolled() + && mPlugin != null) { + mPlugin.onTouchTraveled(getDistancePx()); + } + break; } case ACTION_CANCEL: case ACTION_UP: if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop && mPlugin != null) { - mPlugin.onOverscroll(getDeviceState()); + mPlugin.onTouchEnd(getDistancePx()); } mPassedSlop = false; @@ -161,6 +181,10 @@ public class OverscrollInputConsumer extends Del break; } + if (mState != STATE_DELEGATE_ACTIVE) { + mGestureDetector.onTouchEvent(ev); + } + if (mState != STATE_ACTIVE) { mDelegate.onMotionEvent(ev); } @@ -168,12 +192,19 @@ public class OverscrollInputConsumer extends Del private boolean isOverscrolled() { // Make sure there isn't an app to quick switch to on our right - boolean atRightMostApp = (mRecentsView == null || mRecentsView.getRunningTaskIndex() <= 0); + int maxIndex = 0; + if ((mRecentsView instanceof LauncherRecentsView) + && ((LauncherRecentsView) mRecentsView).hasRecentsExtraCard()) { + maxIndex = 1; + } + + boolean atRightMostApp = (mRecentsView == null + || mRecentsView.getRunningTaskIndex() <= maxIndex); // Check if the gesture is within our angle threshold of horizontal 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)) < ANGLE_THRESHOLD; + boolean angleInBounds = Math.toDegrees(Math.atan2(deltaY, deltaX)) < mAngleThreshold; return atRightMostApp && angleInBounds; } @@ -193,4 +224,36 @@ public class OverscrollInputConsumer extends Del return deviceState; } + + private int getDistancePx() { + return (int) Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y); + } + + private String getUnderlyingActivity() { + 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/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 82fbbc6802..1bbb3f5d18 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -377,6 +377,11 @@ public class LauncherRecentsView extends RecentsView implements StateL addView(mRecentsExtraViewContainer, 0); } + @Override + public boolean hasRecentsExtraCard() { + return mRecentsExtraViewContainer != null; + } + @Override public void setContentAlpha(float alpha) { super.setContentAlpha(alpha); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index bcaa12646e..47bc31ad98 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -830,6 +830,11 @@ public abstract class RecentsView extends PagedView impl public abstract void startHome(); + /** `true` if there is a +1 space available in overview. */ + public boolean hasRecentsExtraCard() { + return false; + } + public void reset() { setCurrentTask(-1); mIgnoreResetTaskId = -1; diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 9ff1350eff..24be859762 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -77,4 +77,7 @@ 28dp + + + 40dp diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 80c7056d3a..58e36f210b 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -114,7 +114,7 @@ public final class FeatureFlags { "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list"); public static final TogglableFlag ENABLE_QUICK_CAPTURE_GESTURE = new TogglableFlag( - "ENABLE_QUICK_CAPTURE_GESTURE", false, "Swipe from right to left to quick capture"); + "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture"); public static final TogglableFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = new TogglableFlag( "ASSISTANT_GIVES_LAUNCHER_FOCUS", false, diff --git a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java index 60eb30418e..28a9193bec 100644 --- a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java +++ b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java @@ -24,11 +24,11 @@ import com.android.systemui.plugins.annotations.ProvidesInterface; * the user to a more recent app). */ @ProvidesInterface(action = com.android.systemui.plugins.OverscrollPlugin.ACTION, - version = com.android.systemui.plugins.OverlayPlugin.VERSION) + version = com.android.systemui.plugins.OverscrollPlugin.VERSION) public interface OverscrollPlugin extends Plugin { String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL"; - int VERSION = 1; + int VERSION = 3; String DEVICE_STATE_LOCKED = "Locked"; String DEVICE_STATE_LAUNCHER = "Launcher"; @@ -36,9 +36,38 @@ public interface OverscrollPlugin extends Plugin { String DEVICE_STATE_UNKNOWN = "Unknown"; /** - * Called when the user completed a right to left swipe in the gesture area. - * - * @param deviceState One of the DEVICE_STATE_* constants. + * @return true if the plugin is active and will accept overscroll gestures */ - void onOverscroll(String deviceState); + boolean isActive(); + + /** + * 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 onTouchStart(String deviceState, String underlyingActivity); + + /** + * 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. + */ + void onTouchTraveled(int px); + + /** + * 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. + */ + void onTouchEnd(int px); + + /** + * Called when the user starts Compose with a fling. `onTouchUp` will also be called. + */ + void onFling(float velocity); }