From dac05b0390727732a511bf93b6ac1aa982c8ccbf Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Thu, 24 Apr 2025 13:17:38 +0100 Subject: [PATCH] Run task reflow after dismiss earlier and increase spring constants. This allows the user to dismiss multiple tasks in a row more quickly. Fix: 409485085 Test: Manual. Fling to dismiss tasks as quickly as possible. Flag: com.android.launcher3.enable_expressive_dismiss_task_motion Change-Id: I9a0d05c1a47b5bc0ef72cde055379e4df50b48b4 --- .../TaskViewDismissTouchController.kt | 1 - .../TaskViewTouchControllerDeprecated.java | 3 +- .../fallback/FallbackRecentsView.java | 2 +- .../quickstep/views/RecentsDismissUtils.kt | 42 +++++----- .../android/quickstep/views/RecentsView.java | 80 +++++++------------ res/values/config.xml | 2 +- 6 files changed, 56 insertions(+), 74 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt index 37015dbb34..b23dc11f24 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt @@ -252,7 +252,6 @@ CONTAINER : RecentsViewContainer { taskBeingDragged, velocity, isDismissing, - dismissLength, dismissThreshold, finalPosition, /* shouldRemoveTaskView= */ isDismissing, diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java index 3905557c8d..b57eba547f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchControllerDeprecated.java @@ -246,8 +246,7 @@ public class TaskViewTouchControllerDeprecated< pa = new PendingAnimation(maxDuration); mRecentsView.createTaskDismissAnimation(pa, mTaskBeingDragged, true /* animateTaskView */, true /* removeTask */, maxDuration, - false /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */, - null /* gridEndData */); + false /* dismissingForSplitSelection*/, null /* gridEndData */); mEndDisplacement = -secondaryTaskDimension; } else { diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index a2c2f0b19f..0168b2c98a 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -143,7 +143,7 @@ public class FallbackRecentsView setCurrentTask(-1)); AnimatorPlaybackController controller = pendingAnimation.createPlaybackController(); controller.dispatchOnStart(); diff --git a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt index 4266c6f2df..b0d3c0e011 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt +++ b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt @@ -36,8 +36,6 @@ import com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY import com.android.quickstep.views.TaskView.Companion.GRID_END_TRANSLATION_X import com.android.systemui.shared.system.InteractionJankMonitorWrapper import com.google.android.msdl.data.model.MSDLToken -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicInteger import kotlin.math.abs import kotlin.math.max import kotlin.math.roundToInt @@ -67,7 +65,6 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) { draggedTaskView, velocity = 0f, isDismissing = true, - dismissLength = 0, dismissThreshold = 0, finalPosition = 0f, shouldRemoveTaskView, @@ -91,7 +88,6 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) { draggedTaskView, velocity, isDismissing = true, - dismissLength, dismissThreshold, finalPosition, shouldRemoveTaskView, @@ -111,14 +107,18 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) { draggedTaskView: TaskView?, velocity: Float, isDismissing: Boolean, - dismissLength: Int, dismissThreshold: Int, finalPosition: Float, shouldRemoveTaskView: Boolean, isSplitSelection: Boolean, onEndRunnable: () -> Unit = {}, ): SpringAnimation? { - val toRunOnEnd = { + var toRunOnEndStarted = false + val toRunOnEnd = toRunOnEnd@{ + // Enforce idempotence. + if (toRunOnEndStarted) return@toRunOnEnd + toRunOnEndStarted = true + if (isDismissing) { if (!recentsView.showAsGrid() || enableGridOnlyOverview()) { runTaskGridReflowSpringAnimation( @@ -150,8 +150,9 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) { FloatPropertyCompat.createFloatPropertyCompat( draggedTaskView.secondaryDismissTranslationProperty ) - val minVelocity = - recentsView.pagedOrientationHandler.getSecondaryDimension(draggedTaskView).toFloat() + val dismissedTaskSecondaryDimension = + recentsView.pagedOrientationHandler.getSecondaryDimension(draggedTaskView) + val minVelocity = dismissedTaskSecondaryDimension.toFloat() val startVelocity = abs(velocity).coerceAtLeast(minVelocity) * velocity.sign var previousDisplacement = taskDismissFloatProperty.getValue(draggedTaskView) // Animate dragged task towards dismissal or rest state. @@ -159,7 +160,7 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) { SpringAnimation(draggedTaskView, taskDismissFloatProperty) .setSpring(createExpressiveDismissSpringForce()) .setStartVelocity(startVelocity) - .addUpdateListener { animation, currentDisplacement, _ -> + .addUpdateListener { _, currentDisplacement, _ -> // Play haptic as task crosses dismiss threshold from above or below. val previousBeyondThreshold = abs(previousDisplacement) >= abs(dismissThreshold) val currentBeyondThreshold = abs(currentDisplacement) >= abs(dismissThreshold) @@ -176,8 +177,13 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) { } recentsView.redrawLiveTile() } - if (isDismissing && abs(currentDisplacement) >= abs(dismissLength)) { - animation.cancel() + // If the dismissing task has moved enough to fit another task in its place, + // start animating any other tasks that will fill the gap it left behind. + if ( + isDismissing && + abs(currentDisplacement) >= abs(dismissedTaskSecondaryDimension) + ) { + toRunOnEnd() } } .addEndListener { _, _, _, _ -> toRunOnEnd() } @@ -1168,22 +1174,22 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) { /** Tracks multiple springs so end runnable only runs once the last spring has ended. */ private class SpringEndManager(val endRunnable: () -> Unit) { - val runningSpringCount = AtomicInteger(0) + var runningSpringCount = 0 fun addSpring(spring: SpringAnimation, endValue: Float) { - runningSpringCount.incrementAndGet() - val canSpringEnd = AtomicBoolean(false) + runningSpringCount++ + var canSpringEnd = false spring.addUpdateListener { _, value, _ -> // Do not allow end listener to fire until we have reached the end value. - if (abs(value - endValue) < spring.minimumVisibleChange && !canSpringEnd.get()) { - canSpringEnd.set(true) + if (!canSpringEnd && abs(value - endValue) < spring.minimumVisibleChange) { + canSpringEnd = true } } spring.addEndListener { _, _, _, _ -> - if (!canSpringEnd.get()) { + if (!canSpringEnd) { return@addEndListener } - if (runningSpringCount.decrementAndGet() == 0) { + if (--runningSpringCount == 0) { endRunnable() } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index cd39e8d5b1..ee2d6f1a42 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -3706,15 +3706,13 @@ public abstract class RecentsView< * @param duration duration of the animation * @param dismissingForSplitSelection task dismiss animation is used for entering split * selection state from app icon - * @param isExpressiveDismiss runs expressive animations controlled via - * {@link RecentsDismissUtils} * @param gridEndData used to compute any gap left between the end of the * grid and the clear all button. */ public void createTaskDismissAnimation(PendingAnimation anim, @Nullable TaskView dismissedTaskView, boolean animateTaskView, boolean shouldRemoveTask, long duration, - boolean dismissingForSplitSelection, boolean isExpressiveDismiss, + boolean dismissingForSplitSelection, @Nullable RecentsDismissUtils.GridEndData gridEndData) { if (mPendingAnimation != null) { mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd(); @@ -3796,9 +3794,9 @@ public abstract class RecentsView< boolean isClearAllHidden = isClearAllHidden(); if (gridEndData == null) { - gridEndData = mDismissUtils.getGridEndData(dismissedTaskView, isExpressiveDismiss, - isFocusedTaskDismissed, nextFocusedTaskView, isStagingFocusedTask, - nextFocusedTaskFromTop, nextFocusedTaskWidth); + gridEndData = mDismissUtils.getGridEndData(dismissedTaskView, + false /* isExpressiveDismiss */, isFocusedTaskDismissed, nextFocusedTaskView, + isStagingFocusedTask, nextFocusedTaskFromTop, nextFocusedTaskWidth); } float longGridRowWidthDiff = gridEndData.getGridEndOffset(); boolean snapToLastTask = gridEndData.getSnapToLastTask(); @@ -3812,7 +3810,7 @@ public abstract class RecentsView< // After dismissal, animate translation of the remaining tasks to fill any gap left // between the end of the grid and the clear all button. Only animate if the clear // all button is visible or would become visible after dismissal. - if (!isExpressiveDismiss && longGridRowWidthDiff != 0) { + if (longGridRowWidthDiff != 0) { // If we need to animate the grid to compensate the clear all gap, we split the // second half of the dismiss pending animation (in which the non-dismissed tasks // slide into place) in half again, making the first quarter the existing @@ -3873,9 +3871,7 @@ public abstract class RecentsView< slidingTranslation += mIsRtl ? newClearAllShortTotalWidthTranslation : -newClearAllShortTotalWidthTranslation; } - if (!isExpressiveDismiss) { - mTaskViewsDismissPrimaryTranslations.clear(); - } + mTaskViewsDismissPrimaryTranslations.clear(); int lastTaskViewIndex = indexOfChild(mUtils.getLastTaskView()); for (int i = 0; i < count; i++) { View child = getChildAt(i); @@ -3890,16 +3886,14 @@ public abstract class RecentsView< lastTaskViewIndex); int scrollDiff = newScroll[i] - oldScroll[i] + offset; if (scrollDiff != 0) { - if (!isExpressiveDismiss) { - translateTaskWhenDismissed( - child, - Math.abs(i - dismissedIndex), - scrollDiff, - anim, - splitTimings); - if (child instanceof TaskView taskView) { - mTaskViewsDismissPrimaryTranslations.put(taskView, scrollDiffPerPage); - } + translateTaskWhenDismissed( + child, + Math.abs(i - dismissedIndex), + scrollDiff, + anim, + splitTimings); + if (child instanceof TaskView taskView) { + mTaskViewsDismissPrimaryTranslations.put(taskView, scrollDiffPerPage); } needsCurveUpdates = true; } @@ -3985,22 +3979,19 @@ public abstract class RecentsView< : finalTranslation + (mIsRtl ? -mLastComputedTaskSize.right : mLastComputedTaskSize.right); } - // Expressive dismiss will animate the translations of taskViews itself. - if (!isExpressiveDismiss) { - Animator dismissAnimator = ObjectAnimator.ofFloat(taskView, - taskView.getPrimaryDismissTranslationProperty(), - startTranslation, finalTranslation); - dismissAnimator.setInterpolator( - clampToProgress(dismissInterpolator, animationStartProgress, - animationEndProgress)); - anim.add(dismissAnimator); - mTaskViewsDismissPrimaryTranslations.put(taskView, (int) finalTranslation); - } + Animator dismissAnimator = ObjectAnimator.ofFloat(taskView, + taskView.getPrimaryDismissTranslationProperty(), + startTranslation, finalTranslation); + dismissAnimator.setInterpolator( + clampToProgress(dismissInterpolator, animationStartProgress, + animationEndProgress)); + anim.add(dismissAnimator); + mTaskViewsDismissPrimaryTranslations.put(taskView, (int) finalTranslation); distanceFromDismissedTask++; } } } - if (dismissingForSplitSelection && !isExpressiveDismiss) { + if (dismissingForSplitSelection) { createInitialSplitSelectAnimation(anim); } @@ -4551,22 +4542,11 @@ public abstract class RecentsView< PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION); createTaskDismissAnimation(pa, taskView, animateTaskView, removeTask, DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/, - false /* isExpressiveDismiss */, null /* gridEndData */); + null /* gridEndData */); runDismissAnimation(pa); } } - protected void expressiveDismissTaskView(TaskView taskView, Function0 onEndRunnable, - int dismissDuration, boolean shouldRemoveTaskView, - @Nullable RecentsDismissUtils.GridEndData gridEndData) { - PendingAnimation pa = new PendingAnimation(dismissDuration); - createTaskDismissAnimation(pa, taskView, false /* animateTaskView */, shouldRemoveTaskView, - dismissDuration, false /* dismissingForSplitSelection*/, - true /* isExpressiveDismiss */, gridEndData); - pa.addEndListener((success) -> onEndRunnable.invoke()); - runDismissAnimation(pa); - } - @SuppressWarnings("unused") private void dismissAllTasks(View view) { runDismissAnimation(createAllTasksDismissAnimation(DISMISS_TASK_DURATION)); @@ -5310,8 +5290,7 @@ public abstract class RecentsView< runExpressiveSplit(builder, mSplitHiddenTaskView); } else { createTaskDismissAnimation(builder, mSplitHiddenTaskView, true, false, duration, - true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */, - null /* gridEndData */); + true /* dismissingForSplitSelection*/, null /* gridEndData */); } } else { // Splitting from Home @@ -5324,8 +5303,7 @@ public abstract class RecentsView< runExpressiveSplit(builder, /* taskView= */ null); } else { createTaskDismissAnimation(builder, null, true, false, duration, - true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */, - null /* gridEndData */); + true /* dismissingForSplitSelection*/, null /* gridEndData */); } } else { createInitialSplitSelectAnimation(builder); @@ -7003,11 +6981,11 @@ public abstract class RecentsView< * spring in response to the perceived impact of the settling task. */ public SpringAnimation runTaskDismissSettlingSpringAnimation(TaskView draggedTaskView, - float velocity, boolean isDismissing, int dismissLength, int dismissThreshold, - float finalPosition, boolean shouldRemoveTaskView, boolean isSplitSelection, + float velocity, boolean isDismissing, int dismissThreshold, float finalPosition, + boolean shouldRemoveTaskView, boolean isSplitSelection, @NonNull Function0 onEndRunnable) { return mDismissUtils.createTaskDismissSettlingSpringAnimation(draggedTaskView, velocity, - isDismissing, dismissLength, dismissThreshold, finalPosition, shouldRemoveTaskView, + isDismissing, dismissThreshold, finalPosition, shouldRemoveTaskView, isSplitSelection, onEndRunnable); } diff --git a/res/values/config.xml b/res/values/config.xml index 8c1bfa7605..0c9d864ce0 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -114,7 +114,7 @@ 0.6 900 0.8 - 900 + 3000 1 1600