Translate recents slightly while dragging after pausing
There's a lot of resistance, but feels better than nothing
responding to your movement.
Bug: 143361609
Change-Id: I9d7e06279ebdbaa0317909ce96d6f001dbe9699a
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index a993f3f..2ac2d2d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -21,11 +21,13 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
+import android.graphics.PointF;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
@@ -37,6 +39,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.views.RecentsView;
/**
* Touch controller which handles swipe and hold from the nav bar to go to Overview. Swiping above
@@ -45,11 +48,22 @@
*/
public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController {
+
+ // How much of the movement to use for translating overview after swipe and hold.
+ private static final float OVERVIEW_MOVEMENT_FACTOR = 0.25f;
+ private static final long TRANSLATION_ANIM_MIN_DURATION_MS = 80;
+ private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f;
+
+ private final RecentsView mRecentsView;
+
private boolean mDidTouchStartInNavBar;
private boolean mReachedOverview;
+ // The last recorded displacement before we reached overview.
+ private PointF mStartDisplacement = new PointF();
public NoButtonNavbarToOverviewTouchController(Launcher l) {
super(l);
+ mRecentsView = l.getOverviewPanel();
}
@Override
@@ -125,12 +139,20 @@
}
@Override
- public boolean onDrag(float displacement, MotionEvent event) {
+ public boolean onDrag(float yDisplacement, float xDisplacement, MotionEvent event) {
if (mMotionPauseDetector.isPaused()) {
+ if (!mReachedOverview) {
+ mStartDisplacement.set(xDisplacement, yDisplacement);
+ } else {
+ mRecentsView.setTranslationX((xDisplacement - mStartDisplacement.x)
+ * OVERVIEW_MOVEMENT_FACTOR);
+ mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y)
+ * OVERVIEW_MOVEMENT_FACTOR);
+ }
// Stay in Overview.
return true;
}
- return super.onDrag(displacement, event);
+ return super.onDrag(yDisplacement, xDisplacement, event);
}
@Override
@@ -164,7 +186,19 @@
anim.start();
}
} else {
- maybeSwipeInteractionToOverviewComplete();
+ if (mReachedOverview) {
+ float distanceDp = dpiFromPx(Math.max(
+ Math.abs(mRecentsView.getTranslationX()),
+ Math.abs(mRecentsView.getTranslationY())));
+ long duration = (long) Math.max(TRANSLATION_ANIM_MIN_DURATION_MS,
+ distanceDp / TRANSLATION_ANIM_VELOCITY_DP_PER_MS);
+ mRecentsView.animate()
+ .translationX(0)
+ .translationY(0)
+ .setInterpolator(ACCEL_DEACCEL)
+ .setDuration(duration)
+ .withEndAction(this::maybeSwipeInteractionToOverviewComplete);
+ }
}
}
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
index f2ebc45..9d406f3 100644
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -54,8 +54,8 @@
}
@Override
- boolean canScrollStart(PointF displacement, float touchSlop) {
- return Math.abs(displacement.y) >= Math.max(Math.abs(displacement.x), touchSlop);
+ float extractOrthogonalDirection(PointF direction) {
+ return direction.x;
}
};
@@ -80,9 +80,10 @@
}
@Override
- boolean canScrollStart(PointF displacement, float touchSlop) {
- return Math.abs(displacement.x) >= Math.max(Math.abs(displacement.y), touchSlop);
+ float extractOrthogonalDirection(PointF direction) {
+ return direction.y;
}
+
};
private final Direction mDir;
@@ -126,7 +127,9 @@
@Override
protected boolean shouldScrollStart(PointF displacement) {
// Reject cases where the angle or slop condition is not met.
- if (!mDir.canScrollStart(displacement, mTouchSlop)) {
+ float minDisplacement = Math.max(mTouchSlop,
+ Math.abs(mDir.extractOrthogonalDirection(displacement)));
+ if (Math.abs(mDir.extractDirection(displacement)) < minDisplacement) {
return false;
}
@@ -150,7 +153,8 @@
@Override
protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
- mListener.onDrag(mDir.extractDirection(displacement), event);
+ mListener.onDrag(mDir.extractDirection(displacement),
+ mDir.extractOrthogonalDirection(displacement), event);
}
@Override
@@ -164,13 +168,16 @@
/** @param start whether this was the original drag start, as opposed to a recatch. */
void onDragStart(boolean start);
- // TODO remove
boolean onDrag(float displacement);
default boolean onDrag(float displacement, MotionEvent event) {
return onDrag(displacement);
}
+ default boolean onDrag(float displacement, float orthogonalDisplacement, MotionEvent ev) {
+ return onDrag(displacement, ev);
+ }
+
void onDragEnd(float velocity);
}
@@ -183,8 +190,7 @@
/** Returns the part of the given {@link PointF} that is relevant to this direction. */
abstract float extractDirection(PointF point);
- /** Reject cases where the angle or slop condition is not met. */
- abstract boolean canScrollStart(PointF displacement, float touchSlop);
+ abstract float extractOrthogonalDirection(PointF point);
}
}
diff --git a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
index 6d463b5..b0ece77 100644
--- a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
@@ -22,15 +22,16 @@
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyFloat;
-import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.ViewConfiguration;
import androidx.test.InstrumentationRegistry;
@@ -158,7 +159,7 @@
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
- verify(mMockListener).onDrag(anyFloat(), anyObject());
+ verify(mMockListener).onDrag(anyFloat(), anyFloat(), any(MotionEvent.class));
}
@Test