Merge "Moved assistant gesture to corners" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
index 109a4c5..5494052 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -22,59 +22,70 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
-import android.view.Display;
import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.ViewConfiguration;
-import android.view.WindowManager;
+import com.android.launcher3.anim.Interpolators;
+import com.android.quickstep.util.MotionPauseDetector;
import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.launcher3.R;
+import com.android.systemui.shared.system.NavigationBarCompat;
/**
* Touch consumer for handling events to launch assistant from launcher
*/
public class AssistantTouchConsumer implements InputConsumer {
private static final String TAG = "AssistantTouchConsumer";
+ private static final long RETRACT_ANIMATION_DURATION_MS = 300;
+
+ /* The assistant touch consume competes with quick switch InputConsumer gesture. The delegate
+ * can be chosen to run if the angle passing the slop is lower than the threshold angle. When
+ * this occurs, the state changes to {@link #STATE_DELEGATE_ACTIVE} where the next incoming
+ * motion events are handled by the delegate instead of the assistant touch consumer. If the
+ * angle is higher than the threshold, the state will change to {@link #STATE_ASSISTANT_ACTIVE}.
+ */
+ private static final int STATE_INACTIVE = 0;
+ private static final int STATE_ASSISTANT_ACTIVE = 1;
+ private static final int STATE_DELEGATE_ACTIVE = 2;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
+ private final PointF mStartDragPos = new PointF();
+
private int mActivePointerId = -1;
-
- private final int mDisplayRotation;
- private final Rect mStableInsets = new Rect();
-
- private final float mDragSlop;
- private final float mTouchSlop;
- private final float mThreshold;
-
- private float mStartDisplacement;
- private boolean mPassedDragSlop;
- private boolean mPassedTouchSlop;
- private long mPassedTouchSlopTime;
+ private boolean mPassedSlop;
private boolean mLaunchedAssistant;
+ private float mDistance;
+ private float mTimeFraction;
+ private long mDragTime;
private float mLastProgress;
+ private int mState;
+ private final float mDistThreshold;
+ private final long mTimeThreshold;
+ private final int mAngleThreshold;
+ private final float mSlop;
+ private final MotionPauseDetector mMotionPauseDetector;
private final ISystemUiProxy mSysUiProxy;
+ private final InputConsumer mConsumerDelegate;
- public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy) {
+ public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
+ InputConsumer delegate) {
+ final Resources res = context.getResources();
mSysUiProxy = systemUiProxy;
-
- mDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
- mTouchSlop = NavigationBarCompat.getQuickStepTouchSlopPx();
- mThreshold = context.getResources().getDimension(R.dimen.gestures_assistant_threshold);
-
- Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
- mDisplayRotation = display.getRotation();
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+ mConsumerDelegate = delegate;
+ mMotionPauseDetector = new MotionPauseDetector(context);
+ mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
+ mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
+ mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
+ mSlop = NavigationBarCompat.getQuickScrubTouchSlopPx();
+ mState = STATE_INACTIVE;
}
@Override
@@ -83,14 +94,28 @@
}
@Override
+ public boolean isActive() {
+ return mState != STATE_INACTIVE;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
// TODO add logging
+
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
- mLastProgress = -1;
+ mTimeFraction = 0;
+
+ // Detect when the gesture decelerates to start the assistant
+ mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
+ if (isPaused && mState == STATE_ASSISTANT_ACTIVE) {
+ mTimeFraction = 1;
+ updateAssistantProgress();
+ }
+ });
break;
}
case ACTION_POINTER_UP: {
@@ -107,94 +132,100 @@
break;
}
case ACTION_MOVE: {
+ if (mState == STATE_DELEGATE_ACTIVE) {
+ break;
+ }
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
break;
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- float displacement = getDisplacement(ev);
- if (!mPassedDragSlop) {
- // Normal gesture, ensure we pass the drag slop before we start tracking
- // the gesture
- if (Math.abs(displacement) > mDragSlop) {
- mPassedDragSlop = true;
- mStartDisplacement = displacement;
- mPassedTouchSlopTime = SystemClock.uptimeMillis();
- }
- }
+ if (!mPassedSlop) {
+ // Normal gesture, ensure we pass the slop before we start tracking the gesture
+ if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) > mSlop) {
+ mPassedSlop = true;
+ mStartDragPos.set(mLastPos.x, mLastPos.y);
+ mDragTime = SystemClock.uptimeMillis();
- if (!mPassedTouchSlop) {
- if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) >=
- mTouchSlop) {
- mPassedTouchSlop = true;
- if (!mPassedDragSlop) {
- mPassedDragSlop = true;
- mStartDisplacement = displacement;
- mPassedTouchSlopTime = SystemClock.uptimeMillis();
+ // Determine if angle is larger than threshold for assistant detection
+ float angle = (float) Math.toDegrees(
+ Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x));
+ angle = angle > 90 ? 180 - angle : angle;
+ if (angle > mAngleThreshold) {
+ mState = STATE_ASSISTANT_ACTIVE;
+
+ if (mConsumerDelegate != null) {
+ // Send cancel event
+ MotionEvent event = MotionEvent.obtain(ev);
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ mConsumerDelegate.onMotionEvent(event);
+ }
+ } else {
+ mState = STATE_DELEGATE_ACTIVE;
}
}
- }
-
- if (mPassedDragSlop) {
- // Move
- float distance = mStartDisplacement - displacement;
- if (distance >= 0) {
- onAssistantProgress(distance / mThreshold);
+ } else {
+ // Movement
+ mDistance = (float) Math.hypot(mLastPos.x - mStartDragPos.x,
+ mLastPos.y - mStartDragPos.y);
+ mMotionPauseDetector.addPosition(mDistance, 0);
+ if (mDistance >= 0) {
+ final long diff = SystemClock.uptimeMillis() - mDragTime;
+ mTimeFraction = Math.min(diff * 1f / mTimeThreshold, 1);
+ updateAssistantProgress();
}
}
break;
}
case ACTION_CANCEL:
- break;
- case ACTION_UP: {
- if (ev.getEventTime() - mPassedTouchSlopTime < ViewConfiguration.getTapTimeout()) {
- onAssistantProgress(1);
+ case ACTION_UP:
+ if (mState != STATE_DELEGATE_ACTIVE && !mLaunchedAssistant) {
+ ValueAnimator animator = ValueAnimator.ofFloat(mLastProgress, 0)
+ .setDuration(RETRACT_ANIMATION_DURATION_MS);
+ animator.addUpdateListener(valueAnimator -> {
+ float progress = (float) valueAnimator.getAnimatedValue();
+ try {
+ mSysUiProxy.onAssistantProgress(progress);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to send SysUI start/send assistant progress: "
+ + progress, e);
+ }
+ });
+ animator.setInterpolator(Interpolators.DEACCEL_2);
+ animator.start();
}
-
+ mMotionPauseDetector.clear();
break;
- }
+ }
+
+ if (mState != STATE_ASSISTANT_ACTIVE && mConsumerDelegate != null) {
+ mConsumerDelegate.onMotionEvent(ev);
}
}
- private void onAssistantProgress(float progress) {
- if (mLastProgress == progress) {
- return;
- }
- try {
- mSysUiProxy.onAssistantProgress(Math.max(0, Math.min(1, progress)));
- if (progress >= 1 && !mLaunchedAssistant) {
- mSysUiProxy.startAssistant(new Bundle());
- mLaunchedAssistant = true;
- }
+ private void updateAssistantProgress() {
+ if (!mLaunchedAssistant) {
+ float progress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction;
mLastProgress = progress;
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to notify SysUI to start/send assistant progress: " + progress, e);
+ try {
+ mSysUiProxy.onAssistantProgress(progress);
+
+ if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
+ mSysUiProxy.startAssistant(new Bundle());
+ mLaunchedAssistant = true;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + progress, e);
+ }
}
}
- private boolean isNavBarOnRight() {
- return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
- }
-
- private boolean isNavBarOnLeft() {
- return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
- }
-
- private float getDisplacement(MotionEvent ev) {
- float eventX = ev.getX();
- float eventY = ev.getY();
- float displacement = eventY - mDownPos.y;
- if (isNavBarOnRight()) {
- displacement = eventX - mDownPos.x;
- } else if (isNavBarOnLeft()) {
- displacement = mDownPos.x - eventX;
- }
- return displacement;
- }
-
- static boolean withinTouchRegion(Context context, float x) {
- return x > context.getResources().getDisplayMetrics().widthPixels
- - context.getResources().getDimension(R.dimen.gestures_assistant_width);
+ static boolean withinTouchRegion(Context context, MotionEvent ev) {
+ final Resources res = context.getResources();
+ final int width = res.getDisplayMetrics().widthPixels;
+ final int height = res.getDisplayMetrics().heightPixels;
+ final int size = res.getDimensionPixelSize(R.dimen.gestures_assistant_size);
+ return (ev.getX() > width - size || ev.getX() < size) && ev.getY() > height - size;
}
}
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 c281d2c..8fe0461 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -311,31 +311,36 @@
mSwipeSharedState.clearAllState();
}
+ final ActivityControlHelper activityControl =
+ mOverviewComponentObserver.getActivityControlHelper();
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
return InputConsumer.NO_OP;
} else if (mAssistantAvailable && mOverviewInteractionState.isSwipeUpGestureEnabled()
&& FeatureFlags.ENABLE_ASSISTANT_GESTURE.get()
- && AssistantTouchConsumer.withinTouchRegion(this, event.getX())) {
- return new AssistantTouchConsumer(this, mRecentsModel.getSystemUiProxy());
- } else if (mSwipeSharedState.goingToLauncher ||
- mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
- return OverviewInputConsumer.newInstance(
- mOverviewComponentObserver.getActivityControlHelper(), false);
+ && AssistantTouchConsumer.withinTouchRegion(this, event)) {
+ return new AssistantTouchConsumer(this, mISystemUiProxy, !activityControl.isResumed()
+ ? createOtherActivityInputConsumer(event, runningTaskInfo) : null);
+ } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
+ return OverviewInputConsumer.newInstance(activityControl, false);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
- mOverviewComponentObserver.getActivityControlHelper().isInLiveTileMode()) {
- return OverviewInputConsumer.newInstance(
- mOverviewComponentObserver.getActivityControlHelper(), false);
+ activityControl.isInLiveTileMode()) {
+ return OverviewInputConsumer.newInstance(activityControl, false);
} else {
- ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
- return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
- mOverviewComponentObserver.getOverviewIntent(), activityControl,
- shouldDefer, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer,
- this::onConsumerInactive, mSwipeSharedState);
+ return createOtherActivityInputConsumer(event, runningTaskInfo);
}
}
+ private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event,
+ RunningTaskInfo runningTaskInfo) {
+ final ActivityControlHelper activityControl =
+ mOverviewComponentObserver.getActivityControlHelper();
+ boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
+ return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
+ mOverviewComponentObserver.getOverviewIntent(), activityControl,
+ shouldDefer, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer,
+ this::onConsumerInactive, mSwipeSharedState);
+ }
+
/**
* To be called by the consumer when it's no longer active.
*/
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 3fbfcdd..a966698 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -26,4 +26,8 @@
determines how many thumbnails will be fetched in the background. -->
<integer name="recentsThumbnailCacheSize">3</integer>
<integer name="recentsIconCacheSize">12</integer>
+
+ <!-- Assistant Gesture -->
+ <integer name="assistant_gesture_min_time_threshold">200</integer>
+ <integer name="assistant_gesture_corner_deg_threshold">30</integer>
</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index f5e5dd3..9c97c8c 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -66,6 +66,6 @@
<dimen name="shelf_surface_offset">24dp</dimen>
<!-- Assistant Gestures -->
- <dimen name="gestures_assistant_width">70dp</dimen>
- <dimen name="gestures_assistant_threshold">200dp</dimen>
+ <dimen name="gestures_assistant_size">28dp</dimen>
+ <dimen name="gestures_assistant_drag_threshold">70dp</dimen>
</resources>