From 646422456d5ea6656efe4e1ef71ab364fa287dfc Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 28 Mar 2025 16:44:46 +0000 Subject: [PATCH] Make sure animation properties are applied correctly for non-gestural transitions - In TaskViewSimulator.addAppToCarouselAnim, make sure carouselScale stays at 1f when it's atomicEvent (3 button, KEYCODE_APP_SWITCH, keybaord etc,), as we transition direclty from fullscreen to Overview without involving the carousel - Made AnimatorSet pass to onPrepareGestureEndAnimation @NonNull, to avoid diverged code path handling null AnimatorSet. - For 3-button/Keyboard interactions, pass the gesture AnimatorSet with a boolean that indicates atomic event; some float peroperties will immediately settle while it's atomic: GRID_PROGRESS, TASK_THUMBNAIL_SPLASH_ALPHA - For home gesture, pass a new AnimatorSet that will be played immediately with 0 duration - Converted onPrepareGestureEndAnimation to Kotlin, and removed the code path when animatorSet is null - updateGridProperties is now called based on displayOverviewTasksAsGrid, which is effectively the same check as endTarget == GestureState.GestureEndTarget.RECENTS; - RECENTS_GRID_PROGRESS is no longer caleld per remoteTargetHandle, which duplicates the animaton for nothing, as it's a per recents rather than per task property Fix: 405384582 Fix: 407059929 Flag: EXEMPT bug fix Test: Swipe up/KEYCODE_APP_SWITCH/Recents button with default and 3p Launcher Change-Id: I56ba9137219f6d7cb982d8e5a3534f09ba3d189d --- .../android/quickstep/AbsSwipeUpHandler.java | 11 +- .../quickstep/SwipeUpAnimationLogic.java | 3 +- .../fallback/FallbackRecentsView.java | 15 +-- .../quickstep/util/TaskViewSimulator.java | 5 +- .../android/quickstep/views/RecentsView.java | 87 +-------------- .../quickstep/views/RecentsViewUtils.kt | 101 ++++++++++++++++++ .../com/android/quickstep/views/TaskView.kt | 4 +- 7 files changed, 125 insertions(+), 101 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index c841c4b63b..316ec8700a 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1767,8 +1767,10 @@ public abstract class AbsSwipeUpHandler< mLauncherTransitionController = null; if (mRecentsView != null) { - mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget(), - mRemoteTargetHandles); + AnimatorSet animatorSet = new AnimatorSet(); + mRecentsView.onPrepareGestureEndAnimation(animatorSet, mGestureState.getEndTarget(), + mRemoteTargetHandles, /* isHandlingAtomicEvent= */ true); + animatorSet.setDuration(0).start(); } } else { AnimatorSet animatorSet = new AnimatorSet(); @@ -1810,9 +1812,10 @@ public abstract class AbsSwipeUpHandler< animatorSet.play(windowAnim); if (mRecentsView != null) { mRecentsView.onPrepareGestureEndAnimation( - mGestureState.isHandlingAtomicEvent() ? null : animatorSet, + animatorSet, mGestureState.getEndTarget(), - mRemoteTargetHandles); + mRemoteTargetHandles, + mGestureState.isHandlingAtomicEvent()); } animatorSet.setDuration(duration).setInterpolator(interpolator); animatorSet.start(); diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index 850bc20c29..47268295ba 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -120,7 +120,8 @@ public abstract class SwipeUpAnimationLogic implements PendingAnimation pendingAnimation = new PendingAnimation(mTransitionDragLength * 2); TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator(); taskViewSimulator.setDp(dp); - taskViewSimulator.addAppToCarouselAnim(pendingAnimation, LINEAR); + taskViewSimulator.addAppToCarouselAnim(pendingAnimation, LINEAR, + mGestureState.isHandlingAtomicEvent()); AnimatorPlaybackController playbackController = pendingAnimation.createPlaybackController(); diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index d2b62b1f13..fbcf2522b1 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -23,7 +23,6 @@ import static com.android.quickstep.fallback.RecentsState.DEFAULT; import static com.android.quickstep.fallback.RecentsState.MODAL_TASK; import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT; -import android.animation.Animator; import android.animation.AnimatorSet; import android.content.Context; import android.util.AttributeSet; @@ -134,9 +133,10 @@ public class FallbackRecentsView setCurrentTask(-1)); AnimatorPlaybackController controller = pendingAnimation.createPlaybackController(); controller.dispatchOnStart(); - Animator homeDismissAnimator = controller.getAnimationPlayer(); - if (animatorSet != null) { - animatorSet.play(homeDismissAnimator); - } else { - homeDismissAnimator.setDuration(0).start(); - } + animatorSet.play(controller.getAnimationPlayer()); } } } diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index 661fe898ee..17c262c7ba 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -317,9 +317,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { /** * Adds animation for all the components corresponding to transition from an app to carousel. */ - public void addAppToCarouselAnim(PendingAnimation pa, Interpolator interpolator) { + public void addAppToCarouselAnim(PendingAnimation pa, Interpolator interpolator, + boolean isHandlingAtomicEvent) { pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator); - if (enableGridOnlyOverview() && mDp.isTablet && mDp.isGestureMode) { + if (enableGridOnlyOverview() && mDp.isTablet && !isHandlingAtomicEvent) { mIsAnimatingToCarousel = true; carouselScale.value = mCarouselTaskSize.width() / (float) mFullTaskSize.width(); } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 1767356892..fd866a3091 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2969,87 +2969,10 @@ public abstract class RecentsView< * Called when a gesture from an app has finished, and an end target has been determined. */ public void onPrepareGestureEndAnimation( - @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget, - RemoteTargetHandle[] remoteTargetHandles) { - Log.d(TAG, "onPrepareGestureEndAnimation - endTarget: " + endTarget); - mCurrentGestureEndTarget = endTarget; - boolean isOverviewEndTarget = endTarget == GestureState.GestureEndTarget.RECENTS; - if (isOverviewEndTarget) { - updateGridProperties(); - } - - BaseState endState = mSizeStrategy.stateFromGestureEndTarget(endTarget); - // Starting the desk exploded animation when the gesture from an app is released. - if (enableDesktopExplodedView()) { - if (animatorSet == null) { - mUtils.setDeskExplodeProgress(endState.showExplodedDesktopView() ? 1f : 0f); - } else { - animatorSet.play( - ObjectAnimator.ofFloat(this, DESK_EXPLODE_PROGRESS, - endState.showExplodedDesktopView() ? 1f : 0f)); - } - - for (TaskView taskView : getTaskViews()) { - if (taskView instanceof DesktopTaskView desktopTaskView) { - desktopTaskView.setRemoteTargetHandles(remoteTargetHandles); - } - } - } - - if (endState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) { - TaskView runningTaskView = getRunningTaskView(); - float runningTaskGridTranslationX = 0; - float runningTaskGridTranslationY = 0; - if (runningTaskView != null) { - // Apply the grid translation to running task unless it's being snapped to - // and removes the current translation applied to the running task. - runningTaskGridTranslationX = runningTaskView.getGridTranslationX() - - runningTaskView.getNonGridTranslationX(); - runningTaskGridTranslationY = runningTaskView.getGridTranslationY(); - } - for (RemoteTargetHandle remoteTargetHandle : remoteTargetHandles) { - TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator(); - if (animatorSet == null) { - setGridProgress(1); - if (enableGridOnlyOverview()) { - tvs.taskGridTranslationX.value = runningTaskGridTranslationX; - tvs.taskGridTranslationY.value = runningTaskGridTranslationY; - } else { - tvs.taskPrimaryTranslation.value = runningTaskGridTranslationX; - tvs.taskSecondaryTranslation.value = runningTaskGridTranslationY; - } - } else { - animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1)); - if (enableGridOnlyOverview()) { - animatorSet.play(tvs.carouselScale.animateToValue(1)); - animatorSet.play(tvs.taskGridTranslationX.animateToValue( - runningTaskGridTranslationX)); - animatorSet.play(tvs.taskGridTranslationY.animateToValue( - runningTaskGridTranslationY)); - } else { - animatorSet.play(tvs.taskPrimaryTranslation.animateToValue( - runningTaskGridTranslationX)); - animatorSet.play(tvs.taskSecondaryTranslation.animateToValue( - runningTaskGridTranslationY)); - } - } - } - } - int splashAlpha = endState.showTaskThumbnailSplash() ? 1 : 0; - if (animatorSet == null) { - setTaskThumbnailSplashAlpha(splashAlpha); - } else { - animatorSet.play( - ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, splashAlpha)); - } - if (enableLargeDesktopWindowingTile()) { - if (animatorSet != null) { - animatorSet.play( - ObjectAnimator.ofFloat(this, DESKTOP_CAROUSEL_DETACH_PROGRESS, 0f)); - } else { - DESKTOP_CAROUSEL_DETACH_PROGRESS.set(this, 0f); - } - } + AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget, + RemoteTargetHandle[] remoteTargetHandles, boolean isHandlingAtomicEvent) { + mUtils.onPrepareGestureEndAnimation(animatorSet, endTarget, remoteTargetHandles, + isHandlingAtomicEvent); } /** @@ -3298,7 +3221,7 @@ public abstract class RecentsView< * * Skips rebalance. */ - private void updateGridProperties() { + protected void updateGridProperties() { updateGridProperties(null); } diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt index 67f5e9589b..c80b1209f6 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt +++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt @@ -16,10 +16,13 @@ package com.android.quickstep.views +import android.animation.AnimatorSet +import android.animation.ObjectAnimator import android.graphics.PointF import android.graphics.Rect import android.util.FloatProperty import android.util.Log +import android.util.Property import android.view.KeyEvent import android.view.View import android.view.View.LAYOUT_DIRECTION_LTR @@ -27,6 +30,7 @@ import android.view.View.LAYOUT_DIRECTION_RTL import androidx.core.view.children import com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU import com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType +import com.android.launcher3.Flags.enableDesktopExplodedView import com.android.launcher3.Flags.enableGridOnlyOverview import com.android.launcher3.Flags.enableLargeDesktopWindowingTile import com.android.launcher3.Flags.enableOverviewIconMenu @@ -34,14 +38,20 @@ import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks import com.android.launcher3.Utilities.getPivotsForScalingRectToRect import com.android.launcher3.statehandlers.DesktopVisibilityController import com.android.launcher3.statehandlers.DesktopVisibilityController.Companion.INACTIVE_DESK_ID +import com.android.launcher3.statemanager.BaseState import com.android.launcher3.util.IntArray import com.android.launcher3.util.window.WindowManagerProxy.DesktopVisibilityListener +import com.android.quickstep.GestureState +import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle import com.android.quickstep.util.DesksUtils.Companion.areMultiDesksFlagsEnabled import com.android.quickstep.util.DesktopTask import com.android.quickstep.util.GroupTask import com.android.quickstep.util.isExternalDisplay +import com.android.quickstep.views.RecentsView.DESKTOP_CAROUSEL_DETACH_PROGRESS +import com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA import com.android.quickstep.views.RecentsView.TAG +import com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA import com.android.systemui.shared.recents.model.Task import com.android.systemui.shared.recents.model.ThumbnailData import com.android.wm.shell.shared.GroupedTaskInfo @@ -563,6 +573,97 @@ class RecentsViewUtils(private val recentsView: RecentsView<*, *>) : DesktopVisi return matchingTaskView == null } + fun onPrepareGestureEndAnimation( + animatorSet: AnimatorSet, + endTarget: GestureState.GestureEndTarget, + remoteTargetHandles: Array, + isHandlingAtomicEvent: Boolean, + ) { + // Create ObjectAnimator that immediately settles on [endStateValue] when + // [isHandlingAtomicEvent] is true. + fun immediateObjectAnimator( + target: T, + property: Property, + endStateValue: Float, + ) = + if (isHandlingAtomicEvent) + ObjectAnimator.ofFloat(target, property, endStateValue, endStateValue) + else ObjectAnimator.ofFloat(target, property, endStateValue) + + with(recentsView) { + Log.d(TAG, "onPrepareGestureEndAnimation - endTarget: $endTarget") + mCurrentGestureEndTarget = endTarget + val endState: BaseState<*> = mSizeStrategy.stateFromGestureEndTarget(endTarget) + + // Starting the desk exploded animation when the gesture from an app is released. + if (enableDesktopExplodedView()) { + animatorSet.play( + ObjectAnimator.ofFloat( + this, + DESK_EXPLODE_PROGRESS, + if (endState.showExplodedDesktopView()) 1f else 0f, + ) + ) + taskViews.filterIsInstance().forEach { + it.remoteTargetHandles = remoteTargetHandles + } + } + + if (endState.displayOverviewTasksAsGrid(getDeviceProfile())) { + updateGridProperties() + animatorSet.play(immediateObjectAnimator(this, RECENTS_GRID_PROGRESS, 1f)) + + val runningTaskView = runningTaskView + var runningTaskGridTranslationX = 0f + var runningTaskGridTranslationY = 0f + if (runningTaskView != null) { + // Apply the grid translation to running task unless it's being snapped to + // and removes the current translation applied to the running task. + runningTaskGridTranslationX = + (runningTaskView.gridTranslationX - runningTaskView.nonGridTranslationX) + runningTaskGridTranslationY = runningTaskView.gridTranslationY + } + remoteTargetHandles.forEach { remoteTargetHandle -> + val taskViewSimulator = remoteTargetHandle.taskViewSimulator + if (enableGridOnlyOverview()) { + animatorSet.play(taskViewSimulator.carouselScale.animateToValue(1f)) + animatorSet.play( + taskViewSimulator.taskGridTranslationX.animateToValue( + runningTaskGridTranslationX + ) + ) + animatorSet.play( + taskViewSimulator.taskGridTranslationY.animateToValue( + runningTaskGridTranslationY + ) + ) + } else { + animatorSet.play( + taskViewSimulator.taskPrimaryTranslation.animateToValue( + runningTaskGridTranslationX + ) + ) + animatorSet.play( + taskViewSimulator.taskSecondaryTranslation.animateToValue( + runningTaskGridTranslationY + ) + ) + } + } + } + animatorSet.play( + immediateObjectAnimator( + this, + TASK_THUMBNAIL_SPLASH_ALPHA, + if (endState.showTaskThumbnailSplash()) 1f else 0f, + ) + ) + if (enableLargeDesktopWindowingTile()) { + animatorSet.play(ObjectAnimator.ofFloat(this, DESKTOP_CAROUSEL_DETACH_PROGRESS, 0f)) + } + } + } + companion object { class RecentsViewFloatProperty( private val utilsProperty: KMutableProperty1 diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt index fefd59df9c..c31b55c638 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.kt +++ b/quickstep/src/com/android/quickstep/views/TaskView.kt @@ -423,7 +423,7 @@ constructor( } // The following grid translations scales with mGridProgress. - protected var gridTranslationX = 0f + var gridTranslationX = 0f set(value) { field = value applyTranslationX() @@ -444,7 +444,7 @@ constructor( // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick // switch. - protected var nonGridTranslationX = 0f + var nonGridTranslationX = 0f set(value) { field = value applyTranslationX()