Make quick switch ("hook") more reliable
- Add max orthogonal displacement to MotionPauseDetector to avoid
pausing when swiping left or right
- When gesture in ambiguous between swipe up for home or swipe over
for the new task, base the decision on the faster velocity component
- Disable recents freescroll mode when dispatching motion from the nav
bar. This way recents handles it naturally and we don't need custom
logic to snap to the next page at the end of the gesture.
- Fix a bug where you couldn't hook to start a new task when SWIPE_HOME
was disabled
Bug: 111926330
Change-Id: If63aa2bb32b57c3f401c5df8b3f6f4efec97b1fa
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
index fb83cd3..b37c2e0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
@@ -57,7 +57,7 @@
@Override
public boolean onDrag(float displacement) {
- mMotionPauseDetector.addPosition(displacement);
+ mMotionPauseDetector.addPosition(displacement, 0);
return super.onDrag(displacement);
}
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 04fd59c..2626481 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -39,6 +39,7 @@
<dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
<dimen name="motion_pause_detector_speed_fast">0.5dp</dimen>
<dimen name="motion_pause_detector_min_displacement">48dp</dimen>
+ <dimen name="motion_pause_detector_max_orthogonal_displacement">48dp</dimen>
<!-- Launcher app transition -->
<dimen name="content_trans_y">50dp</dimen>
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 5755205..a7f5f0b 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -201,7 +201,11 @@
dispatchMotion(ev, displacement - mStartDisplacement, null);
if (FeatureFlags.SWIPE_HOME.get()) {
- mMotionPauseDetector.addPosition(displacement);
+ boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
+ float orthogonalDisplacement = !isLandscape
+ ? ev.getX() - mDownPos.x
+ : ev.getY() - mDownPos.y;
+ mMotionPauseDetector.addPosition(displacement, orthogonalDisplacement);
}
}
break;
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 86a8081..a99fc0f 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -478,6 +478,7 @@
SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> {
mSyncTransactionApplier = applier;
});
+ mRecentsView.setEnableFreeScroll(false);
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
if (!mBgLongSwipeMode && !mIsGoingToHome) {
updateFinalShift();
@@ -910,7 +911,8 @@
Interpolator interpolator = DEACCEL;
final int nextPage = mRecentsView != null ? mRecentsView.getNextPage() : -1;
final int runningTaskIndex = mRecentsView != null ? mRecentsView.getRunningTaskIndex() : -1;
- boolean goingToNewTask = mRecentsView != null && nextPage != runningTaskIndex;
+ boolean goingToNewTask = mRecentsView != null && nextPage != runningTaskIndex
+ && mRecentsView.getTaskViewAt(nextPage) != null;
final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
if (!isFling) {
if (SWIPE_HOME.get()) {
@@ -922,7 +924,11 @@
endTarget = currentShift < MIN_PROGRESS_FOR_OVERVIEW ? LAST_TASK : HOME;
}
} else {
- endTarget = reachedOverviewThreshold && mGestureStarted ? RECENTS : LAST_TASK;
+ endTarget = reachedOverviewThreshold && mGestureStarted
+ ? RECENTS
+ : goingToNewTask
+ ? NEW_TASK
+ : LAST_TASK;
}
endShift = endTarget.endShift;
long expectedDuration = Math.abs(Math.round((endShift - currentShift)
@@ -932,7 +938,9 @@
interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
} else {
if (SWIPE_HOME.get() && endVelocity < 0 && !mIsShelfPeeking) {
- endTarget = HOME;
+ // If swiping at a diagonal, base end target on the faster velocity.
+ endTarget = goingToNewTask && Math.abs(velocityX) > Math.abs(endVelocity)
+ ? NEW_TASK : HOME;
} else if (endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold)) {
// If user scrolled to a new task, only go to recents if they already passed
// the overview threshold. Otherwise, we'll snap to the new task and launch it.
@@ -970,27 +978,21 @@
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
} else if (endTarget == RECENTS) {
mRecentsAnimationWrapper.enableTouchProxy();
+ if (mRecentsView != null) {
+ duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+ }
if (SWIPE_HOME.get()) {
setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
}
} else if (endTarget == NEW_TASK) {
- // We aren't goingToRecents, and user scrolled/flung to a new task; snap to the closest
- // task in that direction and launch it (in startNewTask()).
- int taskToLaunch = runningTaskIndex + (nextPage > runningTaskIndex ? 1 : -1);
- if (taskToLaunch >= mRecentsView.getTaskViewCount()) {
+ // Let RecentsView handle the scrolling to the task, which we launch in startNewTask().
+ if (mRecentsView != null) {
+ duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+ }
+ } else if (endTarget == LAST_TASK) {
+ if (mRecentsView != null && nextPage != runningTaskIndex) {
// Scrolled to Clear all button, snap back to current task and resume it.
mRecentsView.snapToPage(runningTaskIndex, Math.toIntExact(duration));
- goingToNewTask = false;
- } else {
- float distance = Math.abs(mRecentsView.getScrollForPage(taskToLaunch)
- - mRecentsView.getScrollX());
- int durationX = (int) Math.abs(distance / velocityXPxPerMs);
- if (durationX > MAX_SWIPE_DURATION) {
- durationX = Math.toIntExact(MAX_SWIPE_DURATION);
- }
- interpolator = Interpolators.scrollInterpolatorForVelocity(velocityXPxPerMs);
- mRecentsView.snapToPage(taskToLaunch, durationX, interpolator);
- duration = Math.max(duration, durationX);
}
}
animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
@@ -1195,6 +1197,7 @@
mLayoutListener.finish();
mActivityControlHelper.getAlphaProperty(mActivity).setValue(1);
+ mRecentsView.setEnableFreeScroll(true);
mRecentsView.setRunningTaskIconScaledDown(false);
mRecentsView.setOnScrollChangeListener(null);
mQuickScrubController.cancelActiveQuickscrub();
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 7969eec..1156b87 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -36,12 +36,15 @@
private final float mSpeedSomewhatFast;
private final float mSpeedFast;
private final float mMinDisplacementForPause;
+ private final float mMaxOrthogonalDisplacementForPause;
private Long mPreviousTime = null;
private Float mPreviousPosition = null;
private Float mPreviousVelocity = null;
+ private TotalDisplacement mTotalDisplacement = new TotalDisplacement();
private Float mFirstPosition = null;
+ private Float mFirstOrthogonalPosition = null;
private OnMotionPauseListener mOnMotionPauseListener;
private boolean mIsPaused;
@@ -54,6 +57,8 @@
mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
+ mMaxOrthogonalDisplacementForPause = res.getDimension(
+ R.dimen.motion_pause_detector_max_orthogonal_displacement);
}
/**
@@ -70,20 +75,26 @@
/**
* Computes velocity and acceleration to determine whether the motion is paused.
* @param position The x or y component of the motion being tracked.
+ * @param orthogonalPosition The x or y component (opposite of {@param position}) of the motion.
*
* TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
*/
- public void addPosition(float position) {
+ public void addPosition(float position, float orthogonalPosition) {
if (mFirstPosition == null) {
mFirstPosition = position;
}
+ if (mFirstOrthogonalPosition == null) {
+ mFirstOrthogonalPosition = orthogonalPosition;
+ }
long time = SystemClock.uptimeMillis();
if (mPreviousTime != null && mPreviousPosition != null) {
long changeInTime = Math.max(1, time - mPreviousTime);
float changeInPosition = position - mPreviousPosition;
float velocity = changeInPosition / changeInTime;
if (mPreviousVelocity != null) {
- checkMotionPaused(velocity, mPreviousVelocity, Math.abs(position - mFirstPosition));
+ mTotalDisplacement.set(Math.abs(position - mFirstPosition),
+ Math.abs(orthogonalPosition - mFirstOrthogonalPosition));
+ checkMotionPaused(velocity, mPreviousVelocity, mTotalDisplacement);
}
mPreviousVelocity = velocity;
}
@@ -91,7 +102,8 @@
mPreviousPosition = position;
}
- private void checkMotionPaused(float velocity, float prevVelocity, float totalDisplacement) {
+ private void checkMotionPaused(float velocity, float prevVelocity,
+ TotalDisplacement totalDisplacement) {
float speed = Math.abs(velocity);
float previousSpeed = Math.abs(prevVelocity);
boolean isPaused;
@@ -113,8 +125,10 @@
}
}
}
- boolean passedMinDisplacement = totalDisplacement >= mMinDisplacementForPause;
- isPaused &= passedMinDisplacement;
+ boolean passedMinDisplacement = totalDisplacement.primary >= mMinDisplacementForPause;
+ boolean passedMaxOrthogonalDisplacement =
+ totalDisplacement.orthogonal >= mMaxOrthogonalDisplacementForPause;
+ isPaused &= passedMinDisplacement && !passedMaxOrthogonalDisplacement;
if (mIsPaused != isPaused) {
mIsPaused = isPaused;
if (mIsPaused) {
@@ -131,6 +145,8 @@
mPreviousPosition = null;
mPreviousVelocity = null;
mFirstPosition = null;
+ mFirstOrthogonalPosition = null;
+ mTotalDisplacement.set(0, 0);
setOnMotionPauseListener(null);
mIsPaused = mHasEverBeenPaused = false;
}
@@ -142,4 +158,18 @@
public interface OnMotionPauseListener {
void onMotionPauseChanged(boolean isPaused);
}
+
+ /**
+ * Contains the displacement from the first tracked position,
+ * along both the primary and orthogonal axes.
+ */
+ private class TotalDisplacement {
+ public float primary;
+ public float orthogonal;
+
+ public void set(float primaryDisplacement, float orthogonalDisplacement) {
+ this.primary = primaryDisplacement;
+ this.orthogonal = orthogonalDisplacement;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c6f293d..5b84d23 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -83,6 +83,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
@@ -339,6 +340,10 @@
updateEmptyMessage();
}
+ public OverScroller getScroller() {
+ return mScroller;
+ }
+
public boolean isRtl() {
return mIsRtl;
}
@@ -412,7 +417,7 @@
public TaskView getTaskView(int taskId) {
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView tv = (TaskView) getChildAt(i);
- if (tv.getTask().key != null && tv.getTask().key.id == taskId) {
+ if (tv.getTask() != null && tv.getTask().key != null && tv.getTask().key.id == taskId) {
return tv;
}
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 8f9e7c8..018ec5f 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1053,7 +1053,7 @@
}
- protected void setEnableFreeScroll(boolean freeScroll) {
+ public void setEnableFreeScroll(boolean freeScroll) {
boolean wasFreeScroll = mFreeScroll;
mFreeScroll = freeScroll;