From ce9cbda1b799847afdd2b896f93fca0ffb5bbc57 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 18 Dec 2018 16:50:15 -0800 Subject: [PATCH] Scroll recents side to side during swipe up from an app - Swiping straight up and down works the same as before. - We dispatch the MotionEvents to RecentsView so that it scrolls if you swipe side to side as you swipe up. - As RecentsView scrolls, we offset the window to match the position of the first task - If you scroll or fling towards the previous app, it will settle on it and launch it. Bug: 111699315 Change-Id: I1ff160cfc3ca66302a2506d18a4788015de75b94 --- .../uioverrides/TaskViewTouchController.java | 2 +- .../quickstep/ActivityControlHelper.java | 5 +- .../quickstep/OtherActivityTouchConsumer.java | 23 ++- .../quickstep/RecentsAnimationWrapper.java | 6 +- .../WindowTransformSwipeHandler.java | 159 ++++++++++++++---- .../quickstep/util/ClipAnimationHelper.java | 17 +- .../android/quickstep/views/RecentsView.java | 18 +- 7 files changed, 179 insertions(+), 51 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java index 753f73a167..bef3e54435 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java @@ -49,7 +49,7 @@ public abstract class TaskViewTouchController private static final String TAG = "OverviewSwipeController"; // Progress after which the transition is assumed to be a success in case user does not fling - private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; + public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; protected final T mActivity; private final SwipeDetector mDetector; diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index 4646fd7f42..14022837b1 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -44,6 +44,8 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.view.View; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; @@ -77,9 +79,6 @@ import java.util.Objects; import java.util.function.BiPredicate; import java.util.function.Consumer; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; - /** * Utility class which abstracts out the logical differences between Launcher and RecentsActivity. */ diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index 95be188a0e..e27af2aecb 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -21,11 +21,9 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.INVALID_POINTER_ID; - import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; -import static com.android.systemui.shared.system.ActivityManagerWrapper - .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.annotation.TargetApi; @@ -192,7 +190,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC if (mPassedInitialSlop && mInteractionHandler != null) { // Move - mInteractionHandler.updateDisplacement(displacement - mStartDisplacement); + dispatchMotion(ev, displacement - mStartDisplacement); } break; } @@ -207,6 +205,14 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } } + private void dispatchMotion(MotionEvent ev, float displacement) { + mInteractionHandler.updateDisplacement(displacement); + boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight(); + if (!isLandscape) { + mInteractionHandler.dispatchMotionEventToRecentsView(ev); + } + } + private void notifyGestureStarted() { if (mInteractionHandler == null) { return; @@ -297,15 +303,16 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC */ private void finishTouchTracking(MotionEvent ev) { if (mPassedInitialSlop && mInteractionHandler != null) { - mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement); + dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement); mVelocityTracker.computeCurrentVelocity(1000, ViewConfiguration.get(this).getScaledMaximumFlingVelocity()); - float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId) - : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId) + float velocityX = mVelocityTracker.getXVelocity(mActivePointerId); + float velocity = isNavBarOnRight() ? velocityX + : isNavBarOnLeft() ? -velocityX : mVelocityTracker.getYVelocity(mActivePointerId); - mInteractionHandler.onGestureEnded(velocity); + mInteractionHandler.onGestureEnded(velocity, velocityX); } else { // Since we start touch tracking on DOWN, we may reach this state without actually // starting the gesture. In that case, just cleanup immediately. diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java index 2f3cb5f373..042afea0b4 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -97,8 +97,8 @@ public class RecentsAnimationWrapper { } /** - * @param onFinishComplete A callback that runs after the animation controller has finished - * on the background thread. + * @param onFinishComplete A callback that runs on the main thread after the animation + * controller has finished on the background thread. */ public void finish(boolean toHome, Runnable onFinishComplete) { if (!toHome) { @@ -128,7 +128,7 @@ public class RecentsAnimationWrapper { controller.finish(toHome); if (onFinishComplete != null) { - onFinishComplete.run(); + mMainThreadExecutor.execute(onFinishComplete); } } } diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index e951750d8d..ac21517173 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -46,10 +46,12 @@ import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver.OnDrawListener; import android.view.WindowManager; import android.view.animation.Interpolator; + import androidx.annotation.AnyThread; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; @@ -109,7 +111,7 @@ public class WindowTransformSwipeHandler { // Interaction finish states private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5; - private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6; + private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 6; private static final int STATE_HANDLER_INVALIDATED = 1 << 7; private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 8; @@ -127,7 +129,8 @@ public class WindowTransformSwipeHandler { private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 17; private static final int STATE_RESUME_LAST_TASK = 1 << 18; - private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 19; + private static final int STATE_START_NEW_TASK = 1 << 19; + private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 20; private static final int LAUNCHER_UI_STATES = @@ -153,7 +156,7 @@ public class WindowTransformSwipeHandler { "STATE_ACTIVITY_MULTIPLIER_COMPLETE", "STATE_APP_CONTROLLER_RECEIVED", "STATE_SCALED_CONTROLLER_RECENTS", - "STATE_SCALED_CONTROLLER_APP", + "STATE_SCALED_CONTROLLER_LAST_TASK", "STATE_HANDLER_INVALIDATED", "STATE_GESTURE_STARTED_QUICKSTEP", "STATE_GESTURE_STARTED_QUICKSCRUB", @@ -166,6 +169,7 @@ public class WindowTransformSwipeHandler { "STATE_SCREENSHOT_CAPTURED", "STATE_SCREENSHOT_VIEW_SHOWN", "STATE_RESUME_LAST_TASK", + "STATE_START_NEW_TASK", "STATE_ASSIST_DATA_RECEIVED", }; @@ -173,7 +177,7 @@ public class WindowTransformSwipeHandler { public static final long MIN_SWIPE_DURATION = 80; public static final long MIN_OVERSHOOT_DURATION = 120; - public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f; + public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f; private static final float SWIPE_DURATION_MULTIPLIER = Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW)); @@ -190,6 +194,7 @@ public class WindowTransformSwipeHandler { // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely // visible. private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); + private boolean mDispatchedDownEvent; // To avoid UI jump when gesture is started, we offset the animation by the threshold. private float mShiftAtGestureStart = 0; @@ -298,10 +303,12 @@ public class WindowTransformSwipeHandler { mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED, this::sendRemoteAnimationsToAnimationFactory); - mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP, + mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_LAST_TASK, this::resumeLastTaskForQuickstep); mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED, this::resumeLastTask); + mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_APP_CONTROLLER_RECEIVED, + this::startNewTask); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED | STATE_ACTIVITY_MULTIPLIER_COMPLETE @@ -327,7 +334,7 @@ public class WindowTransformSwipeHandler { mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED, this::invalidateHandlerWithLauncher); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED - | STATE_SCALED_CONTROLLER_APP, + | STATE_SCALED_CONTROLLER_LAST_TASK, this::notifyTransitionCancelled); mStateCallback.addCallback(QUICK_SCRUB_START_UI_STATE, this::onQuickScrubStartUi); @@ -410,6 +417,9 @@ public class WindowTransformSwipeHandler { SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> { mSyncTransactionApplier = applier; }); + mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { + updateFinalShift(); + }); mQuickScrubController = mRecentsView.getQuickScrubController(); mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity); @@ -535,15 +545,39 @@ public class WindowTransformSwipeHandler { } else { offsetX = res.getDimensionPixelSize(R.dimen.recents_page_spacing) + tempRect.rect.width(); - float distanceToReachEdge = mDp.widthPx / 2 + tempRect.rect.width() / 2 + - res.getDimensionPixelSize(R.dimen.recents_page_spacing); - float interpolation = Math.min(1, offsetX / distanceToReachEdge); - scale = TaskView.getCurveScaleForInterpolation(interpolation); + scale = getTaskCurveScaleForOffsetX(offsetX, tempRect.rect.width()); } mClipAnimationHelper.offsetTarget(scale, Utilities.isRtl(res) ? -offsetX : offsetX, offsetY, QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR); } + private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) { + float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 + + mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing); + float interpolation = Math.min(1, offsetX / distanceToReachEdge); + return TaskView.getCurveScaleForInterpolation(interpolation); + } + + @WorkerThread + public void dispatchMotionEventToRecentsView(MotionEvent event) { + if (mRecentsView == null) { + return; + } + // Pass the motion events to RecentsView to allow scrolling during swipe up. + if (mDispatchedDownEvent) { + mRecentsView.dispatchTouchEvent(event); + } else { + // The first event we dispatch should be ACTION_DOWN. + mDispatchedDownEvent = true; + MotionEvent downEvent = MotionEvent.obtain(event); + downEvent.setAction(MotionEvent.ACTION_DOWN); + int flags = downEvent.getEdgeFlags(); + downEvent.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR); + mRecentsView.dispatchTouchEvent(downEvent); + downEvent.recycle(); + } + } + @WorkerThread public void updateDisplacement(float displacement) { // We are moving in the negative x/y direction @@ -588,11 +622,21 @@ public class WindowTransformSwipeHandler { RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController(); if (controller != null) { + float offsetX = 0; + if (mRecentsView != null && mInteractionType == INTERACTION_NORMAL) { + int startScroll = mRecentsView.getScrollForPage(mRecentsView.indexOfChild( + mRecentsView.getRunningTaskView())); + offsetX = startScroll - mRecentsView.getScrollX(); + offsetX *= mRecentsView.getScaleX(); + } + float offsetScale = getTaskCurveScaleForOffsetX(offsetX, + mClipAnimationHelper.getTargetRect().width()); SyncRtSurfaceTransactionApplierCompat syncTransactionApplier = Looper.myLooper() == mMainThreadHandler.getLooper() ? mSyncTransactionApplier : null; - mTransformParams.setProgress(shift).setSyncTransactionApplier(syncTransactionApplier); + mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale) + .setSyncTransactionApplier(syncTransactionApplier); mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, mTransformParams); @@ -621,10 +665,17 @@ public class WindowTransformSwipeHandler { HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } } - // Update insets of the next previous task, as we might switch to it. - TaskView nextTaskView = mRecentsView == null ? null : mRecentsView.getNextTaskView(); - if (mInteractionType == INTERACTION_NORMAL && nextTaskView != null) { - nextTaskView.setFullscreenProgress(1 - mCurrentShift.value); + // Update insets of the adjacent tasks, as we might switch to them. + int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex(); + if (mInteractionType == INTERACTION_NORMAL && runningTaskIndex >= 0) { + TaskView nextTaskView = mRecentsView.getTaskViewAt(runningTaskIndex + 1); + TaskView prevTaskView = mRecentsView.getTaskViewAt(runningTaskIndex - 1); + if (nextTaskView != null) { + nextTaskView.setFullscreenProgress(1 - mCurrentShift.value); + } + if (prevTaskView != null) { + prevTaskView.setFullscreenProgress(1 - mCurrentShift.value); + } } if (mLauncherTransitionController == null || mLauncherTransitionController @@ -714,7 +765,7 @@ public class WindowTransformSwipeHandler { } @WorkerThread - public void onGestureEnded(float endVelocity) { + public void onGestureEnded(float endVelocity, float velocityX) { float flingThreshold = mContext.getResources() .getDimension(R.dimen.quickstep_fling_threshold_velocity); boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold; @@ -723,9 +774,9 @@ public class WindowTransformSwipeHandler { mLogAction = isFling ? Touch.FLING : Touch.SWIPE; if (mBgLongSwipeMode) { - executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling)); + executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling, velocityX)); } else { - handleNormalGestureEnd(endVelocity, isFling); + handleNormalGestureEnd(endVelocity, isFling, velocityX); } } @@ -742,16 +793,21 @@ public class WindowTransformSwipeHandler { mTouchInteractionLog); } - private void handleNormalGestureEnd(float endVelocity, boolean isFling) { + private void handleNormalGestureEnd(float endVelocity, boolean isFling, float velocityX) { float velocityPxPerMs = endVelocity / 1000; + float velocityXPxPerMs = velocityX / 1000; long duration = MAX_SWIPE_DURATION; float currentShift = mCurrentShift.value; final boolean goingToHome; float endShift; final float startShift; 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; + final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW; if (!isFling) { - goingToHome = currentShift >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted; + goingToHome = reachedOverviewThreshold && mGestureStarted; endShift = goingToHome ? 1 : 0; long expectedDuration = Math.abs(Math.round((endShift - currentShift) * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER)); @@ -759,7 +815,9 @@ public class WindowTransformSwipeHandler { startShift = currentShift; interpolator = goingToHome ? OVERSHOOT_1_2 : DEACCEL; } else { - goingToHome = endVelocity < 0; + // If user scrolled to a new task, only go to home (overview) if they already passed + // the overview threshold. Otherwise, we'll snap to the new task and launch it. + goingToHome = endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold); endShift = goingToHome ? 1 : 0; startShift = Utilities.boundToRange(currentShift - velocityPxPerMs * SINGLE_FRAME_MS / mTransitionDragLength, 0, 1); @@ -786,9 +844,29 @@ public class WindowTransformSwipeHandler { } if (goingToHome) { mRecentsAnimationWrapper.enableTouchProxy(); + } else if (goingToNewTask) { + // We aren't goingToHome, 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()) { + // 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, goingToHome); + animateToProgress(startShift, endShift, duration, interpolator, goingToHome, + goingToNewTask); } private void doLogGesture(boolean toLauncher) { @@ -814,22 +892,26 @@ public class WindowTransformSwipeHandler { /** Animates to the given progress, where 0 is the current app and 1 is overview. */ private void animateToProgress(float start, float end, long duration, - Interpolator interpolator, boolean goingToHome) { + Interpolator interpolator, boolean goingToHome, boolean goingToNewTask) { mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration, - interpolator, goingToHome)); + interpolator, goingToHome, goingToNewTask)); } private void animateToProgressInternal(float start, float end, long duration, - Interpolator interpolator, boolean goingToHome) { + Interpolator interpolator, boolean goingToHome, boolean goingToNewTask) { mIsGoingToHome = goingToHome; ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration); anim.setInterpolator(interpolator); anim.addListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { + int recentsState = STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT + | STATE_SCREENSHOT_VIEW_SHOWN; setStateOnUiThread(mIsGoingToHome - ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT - | STATE_SCREENSHOT_VIEW_SHOWN) : STATE_SCALED_CONTROLLER_APP); + ? recentsState + : goingToNewTask + ? STATE_START_NEW_TASK + : STATE_SCALED_CONTROLLER_LAST_TASK); } }); anim.start(); @@ -872,6 +954,18 @@ public class WindowTransformSwipeHandler { mTouchInteractionLog.finishRecentsAnimation(false); } + @UiThread + private void startNewTask() { + // Launch the task user scrolled to (mRecentsView.getNextPage()). + mRecentsAnimationWrapper.finish(true /* toHome */, () -> { + mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false, + result -> setStateOnUiThread(STATE_HANDLER_INVALIDATED), + mMainThreadHandler); + }); + mTouchInteractionLog.finishRecentsAnimation(false); + doLogGesture(false /* toLauncher */); + } + public void reset() { if (mInteractionType != INTERACTION_QUICK_SCRUB) { // Only invalidate the handler if we are not quick scrubbing, otherwise, it will be @@ -889,6 +983,10 @@ public class WindowTransformSwipeHandler { mActivityInitListener.unregister(); mTaskSnapshot = null; + + if (mRecentsView != null) { + mRecentsView.setOnScrollChangeListener(null); + } } private void invalidateHandlerWithLauncher() { @@ -999,7 +1097,8 @@ public class WindowTransformSwipeHandler { long duration = FeatureFlags.QUICK_SWITCH.get() ? QUICK_SWITCH_FROM_APP_START_DURATION : QUICK_SCRUB_FROM_APP_START_DURATION; - animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */); + animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */, + false /* goingToNewTask */); } private void onQuickScrubStartUi() { @@ -1152,10 +1251,10 @@ public class WindowTransformSwipeHandler { setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha); } - private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) { + private void onLongSwipeGestureFinishUi(float velocity, boolean isFling, float velocityX) { if (!mUiLongSwipeMode || mLongSwipeController == null) { mUiLongSwipeMode = false; - handleNormalGestureEnd(velocity, isFling); + handleNormalGestureEnd(velocity, isFling, velocityX); return; } mUiLongSwipeMode = false; diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java index 431517a5eb..6a8482b59b 100644 --- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java +++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java @@ -154,10 +154,11 @@ public class ClipAnimationHelper { public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params) { RectF currentRect; mTmpRectF.set(mTargetRect); - Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale); + Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale * params.offsetScale); float offsetYProgress = mOffsetYInterpolator.getInterpolation(params.progress); float progress = mInterpolator.getInterpolation(params.progress); currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF); + currentRect.offset(params.offsetX, 0); synchronized (mTargetOffset) { // Stay lined up with the center of the target, since it moves for quick scrub. @@ -353,10 +354,14 @@ public class ClipAnimationHelper { public static class TransformParams { float progress; + float offsetX; + float offsetScale; SyncRtSurfaceTransactionApplierCompat syncTransactionApplier; public TransformParams() { progress = 0; + offsetX = 0; + offsetScale = 1; } public TransformParams setProgress(float progress) { @@ -364,6 +369,16 @@ public class ClipAnimationHelper { return this; } + public TransformParams setOffsetX(float offsetX) { + this.offsetX = offsetX; + return this; + } + + public TransformParams setOffsetScale(float offsetScale) { + this.offsetScale = offsetScale; + return this; + } + public TransformParams setSyncTransactionApplier( SyncRtSurfaceTransactionApplierCompat applier) { this.syncTransactionApplier = applier; diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 34b57482ee..e2a4dda18e 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -21,9 +21,11 @@ import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.uioverrides.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; -import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; + +import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR; import android.animation.Animator; import android.animation.AnimatorSet; @@ -60,6 +62,7 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ListView; + import androidx.annotation.Nullable; import com.android.launcher3.BaseActivity; @@ -421,7 +424,8 @@ public abstract class RecentsView extends PagedView impl final boolean clearAllButtonDeadZoneConsumed = mClearAllButton.getAlpha() == 1 && mClearAllButtonDeadZoneRect.contains(x, y); - if (!clearAllButtonDeadZoneConsumed + final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; + if (!clearAllButtonDeadZoneConsumed && !cameFromNavBar && !mTaskViewDeadZoneRect.contains(x + getScrollX(), y)) { mTouchDownToStartHome = true; } @@ -732,6 +736,11 @@ public abstract class RecentsView extends PagedView impl return getTaskView(mRunningTaskId); } + public int getRunningTaskIndex() { + TaskView tv = getRunningTaskView(); + return tv == null ? -1 : indexOfChild(tv); + } + /** * Hides the tile associated with {@link #mRunningTaskId} */ @@ -756,8 +765,7 @@ public abstract class RecentsView extends PagedView impl setRunningTaskIconScaledDown(runningTaskIconScaledDown); setRunningTaskHidden(runningTaskTileHidden); - TaskView tv = getRunningTaskView(); - setCurrentPage(tv == null ? 0 : indexOfChild(tv)); + setCurrentPage(getRunningTaskIndex()); // Load the tasks (if the loading is already mTaskListChangeId = mModel.getTasks(this::applyLoadPlan); @@ -1346,7 +1354,7 @@ public abstract class RecentsView extends PagedView impl : 0); // Passing the threshold from taskview to fullscreen app will vibrate - final boolean passed = animator.getAnimatedFraction() >= MIN_PROGRESS_FOR_OVERVIEW; + final boolean passed = animator.getAnimatedFraction() >= SUCCESS_TRANSITION_PROGRESS; if (passed != passedOverviewThreshold[0]) { passedOverviewThreshold[0] = passed; performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,