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
This commit is contained in:
Pat Manning
2025-04-24 13:17:38 +01:00
parent 5783a8ef92
commit dac05b0390
6 changed files with 56 additions and 74 deletions
@@ -252,7 +252,6 @@ CONTAINER : RecentsViewContainer {
taskBeingDragged,
velocity,
isDismissing,
dismissLength,
dismissThreshold,
finalPosition,
/* shouldRemoveTaskView= */ isDismissing,
@@ -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 {
@@ -143,7 +143,7 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
PendingAnimation pendingAnimation = new PendingAnimation(TASK_DISMISS_DURATION);
createTaskDismissAnimation(pendingAnimation, homeTaskView, true, false,
TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/,
false /* isExpressiveDismiss */, null /* gridEndData */);
null /* gridEndData */);
pendingAnimation.addEndListener(e -> setCurrentTask(-1));
AnimatorPlaybackController controller = pendingAnimation.createPlaybackController();
controller.dispatchOnStart();
@@ -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()
}
}
@@ -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<Unit> 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<Unit> onEndRunnable) {
return mDismissUtils.createTaskDismissSettlingSpringAnimation(draggedTaskView, velocity,
isDismissing, dismissLength, dismissThreshold, finalPosition, shouldRemoveTaskView,
isDismissing, dismissThreshold, finalPosition, shouldRemoveTaskView,
isSplitSelection, onEndRunnable);
}
+1 -1
View File
@@ -114,7 +114,7 @@
<item name="expressive_dismiss_task_trans_y_damping_ratio" type="dimen" format="float">0.6</item>
<item name="expressive_dismiss_task_trans_y_stiffness" type="dimen" format="float">900</item>
<item name="expressive_dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.8</item>
<item name="expressive_dismiss_task_trans_x_stiffness" type="dimen" format="float">900</item>
<item name="expressive_dismiss_task_trans_x_stiffness" type="dimen" format="float">3000</item>
<item name="expressive_dismiss_effects_damping_ratio" type="dimen" format="float">1</item>
<item name="expressive_dismiss_effects_stiffness" type="dimen" format="float">1600</item>