From 023f404a12245904aec96fb9158edfca51e45434 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 29 Jan 2019 10:52:31 -0800 Subject: 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 --- .../uioverrides/FlingAndHoldTouchController.java | 2 +- quickstep/res/values/dimens.xml | 1 + .../quickstep/OtherActivityTouchConsumer.java | 6 +++- .../quickstep/WindowTransformSwipeHandler.java | 39 +++++++++++---------- .../quickstep/util/MotionPauseDetector.java | 40 +++++++++++++++++++--- .../com/android/quickstep/views/RecentsView.java | 7 +++- src/com/android/launcher3/PagedView.java | 2 +- 7 files changed, 70 insertions(+), 27 deletions(-) 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 fb83cd32e..b37c2e09d 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 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { @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 04fd59ce0..2626481c3 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -39,6 +39,7 @@ 0.285dp 0.5dp 48dp + 48dp 50dp diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index 5755205ea..a7f5f0b93 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -201,7 +201,11 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC 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 86a8081be..a99fc0f91 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -478,6 +478,7 @@ public class WindowTransformSwipeHandler { SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> { mSyncTransactionApplier = applier; }); + mRecentsView.setEnableFreeScroll(false); mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { if (!mBgLongSwipeMode && !mIsGoingToHome) { updateFinalShift(); @@ -910,7 +911,8 @@ public class WindowTransformSwipeHandler { 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 @@ public class WindowTransformSwipeHandler { 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 @@ public class WindowTransformSwipeHandler { 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 @@ public class WindowTransformSwipeHandler { 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 @@ public class WindowTransformSwipeHandler { 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 7969eec7c..1156b87b3 100644 --- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java +++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java @@ -36,12 +36,15 @@ public class MotionPauseDetector { 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 @@ public class MotionPauseDetector { 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 @@ public class MotionPauseDetector { /** * 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 @@ public class MotionPauseDetector { 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 @@ public class MotionPauseDetector { } } } - 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 @@ public class MotionPauseDetector { mPreviousPosition = null; mPreviousVelocity = null; mFirstPosition = null; + mFirstOrthogonalPosition = null; + mTotalDisplacement.set(0, 0); setOnMotionPauseListener(null); mIsPaused = mHasEverBeenPaused = false; } @@ -142,4 +158,18 @@ public class MotionPauseDetector { 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 c6f293d2b..5b84d2327 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.anim.SpringObjectAnimator; 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 @@ public abstract class RecentsView extends PagedView impl updateEmptyMessage(); } + public OverScroller getScroller() { + return mScroller; + } + public boolean isRtl() { return mIsRtl; } @@ -412,7 +417,7 @@ public abstract class RecentsView extends PagedView impl 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 8f9e7c821..018ec5f17 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1053,7 +1053,7 @@ public abstract class PagedView extends ViewGrou } - protected void setEnableFreeScroll(boolean freeScroll) { + public void setEnableFreeScroll(boolean freeScroll) { boolean wasFreeScroll = mFreeScroll; mFreeScroll = freeScroll; -- cgit v1.2.3