Compose overscroll gesture updates
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
This commit is contained in:
parent
c7d601e923
commit
a9156a05c4
|
@ -484,7 +484,9 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat);
|
||||
}
|
||||
|
||||
if (mOverscrollPlugin != null) {
|
||||
if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()
|
||||
&& (mOverscrollPlugin != null)
|
||||
&& mOverscrollPlugin.isActive()) {
|
||||
// Put the overscroll gesture as higher priority than the Assistant or base gestures
|
||||
base = new OverscrollInputConsumer(this, newGestureState, base, mInputMonitorCompat,
|
||||
mOverscrollPlugin);
|
||||
|
|
|
@ -26,14 +26,17 @@ import static com.android.launcher3.Utilities.squaredHypot;
|
|||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.GestureState;
|
||||
import com.android.quickstep.InputConsumer;
|
||||
import com.android.quickstep.views.LauncherRecentsView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.plugins.OverscrollPlugin;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
|
@ -47,12 +50,12 @@ public class OverscrollInputConsumer<T extends BaseDraggingActivity> 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<T extends BaseDraggingActivity> 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<T extends BaseDraggingActivity> 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<T extends BaseDraggingActivity> 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<T extends BaseDraggingActivity> 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<T extends BaseDraggingActivity> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -377,6 +377,11 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
|
|||
addView(mRecentsExtraViewContainer, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRecentsExtraCard() {
|
||||
return mRecentsExtraViewContainer != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentAlpha(float alpha) {
|
||||
super.setContentAlpha(alpha);
|
||||
|
|
|
@ -830,6 +830,11 @@ public abstract class RecentsView<T extends BaseActivity> 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;
|
||||
|
|
|
@ -77,4 +77,7 @@
|
|||
|
||||
<!-- Distance to move elements when swiping up to go home from launcher -->
|
||||
<dimen name="home_pullback_distance">28dp</dimen>
|
||||
|
||||
<!-- Overscroll Gesture -->
|
||||
<dimen name="gestures_overscroll_fling_threshold">40dp</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue