From ee98cd4bdf739c2f93f7cf0a20764acb2a837b0c Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 3 Oct 2024 23:16:29 +0000 Subject: [PATCH] Don't invalidate swipe handler until parallel anim finishes - When invalidateHandler() is called, it calls endRunningWindowAnim() which includes mParallelRunningAnim. This causes a jump if mParallelRunningAnim was not already finished, so we now wait to invalidate the handler after mParallelRunningAnim ends. Flag: EXEMPT bugfix Test: AbsSwipeUpHandlerTestCase (added two tests for this) Fixes: 370208192 Change-Id: I37ed281a993b1d2fa3634754378314511f3295f0 --- .../android/quickstep/AbsSwipeUpHandler.java | 8 ++- .../quickstep/AbsSwipeUpHandlerTestCase.java | 60 ++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 864328f883..211d20969b 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -237,6 +237,8 @@ public abstract class AbsSwipeUpHandler< getNextStateFlag("STATE_SCALED_CONTROLLER_HOME"); private static final int STATE_SCALED_CONTROLLER_RECENTS = getNextStateFlag("STATE_SCALED_CONTROLLER_RECENTS"); + private static final int STATE_PARALLEL_ANIM_FINISHED = + getNextStateFlag("STATE_PARALLEL_ANIM_FINISHED"); protected static final int STATE_HANDLER_INVALIDATED = getNextStateFlag("STATE_HANDLER_INVALIDATED"); @@ -453,7 +455,8 @@ public abstract class AbsSwipeUpHandler< mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED | STATE_SCALED_CONTROLLER_HOME, this::finishCurrentTransitionToHome); - mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED, + mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED + | STATE_PARALLEL_ANIM_FINISHED, this::reset); mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED @@ -1540,9 +1543,12 @@ public abstract class AbsSwipeUpHandler< @Override public void onAnimationEnd(Animator animation) { mParallelRunningAnim = null; + mStateCallback.setStateOnUiThread(STATE_PARALLEL_ANIM_FINISHED); } }); mParallelRunningAnim.start(); + } else { + mStateCallback.setStateOnUiThread(STATE_PARALLEL_ANIM_FINISHED); } } diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java index 87a7cdae14..52c1f8945a 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java @@ -16,15 +16,23 @@ package com.android.quickstep; +import static com.android.quickstep.AbsSwipeUpHandler.STATE_HANDLER_INVALIDATED; + +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.animation.ValueAnimator; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; @@ -57,6 +65,7 @@ import com.android.systemui.shared.system.InputConsumerController; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -221,7 +230,7 @@ public abstract class AbsSwipeUpHandlerTestCase< runOnMainSync(() -> { absSwipeUpHandler.startNewTask(unused -> {}); - verify(mRecentsAnimationController).finish(anyBoolean(), any()); + verifyRecentsAnimationFinishedAndCallCallback(); }); } @@ -231,10 +240,57 @@ public abstract class AbsSwipeUpHandlerTestCase< runOnMainSync(() -> { verify(mRecentsAnimationController).detachNavigationBarFromApp(true); - verify(mRecentsAnimationController).finish(anyBoolean(), any(), anyBoolean()); + verifyRecentsAnimationFinishedAndCallCallback(); }); } + @Test + public void testHomeGesture_invalidatesHandlerAfterParallelAnim() { + ValueAnimator parallelAnim = new ValueAnimator(); + parallelAnim.setRepeatCount(ValueAnimator.INFINITE); + when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any())) + .thenReturn(parallelAnim); + SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME); + runOnMainSync(() -> { + parallelAnim.start(); + verifyRecentsAnimationFinishedAndCallCallback(); + assertFalse(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)); + parallelAnim.end(); + assertTrue(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)); + }); + } + + @Test + public void testHomeGesture_invalidatesHandlerIfNoParallelAnim() { + when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any())) + .thenReturn(null); + SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME); + runOnMainSync(() -> { + verifyRecentsAnimationFinishedAndCallCallback(); + assertTrue(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)); + }); + } + + /** + * Verifies that RecentsAnimationController#finish() is called, and captures and runs any + * callback that was passed to it. This ensures that STATE_CURRENT_TASK_FINISHED is correctly + * set for example. + */ + private void verifyRecentsAnimationFinishedAndCallCallback() { + ArgumentCaptor finishCallback = ArgumentCaptor.forClass(Runnable.class); + // Check if the 2 parameter method is called. + verify(mRecentsAnimationController, atLeast(0)).finish( + anyBoolean(), finishCallback.capture()); + if (finishCallback.getAllValues().isEmpty()) { + // Check if the 3 parameter method is called. + verify(mRecentsAnimationController).finish( + anyBoolean(), finishCallback.capture(), anyBoolean()); + } + if (finishCallback.getValue() != null) { + finishCallback.getValue().run(); + } + } + private SWIPE_HANDLER createSwipeUpHandlerForGesture(GestureState.GestureEndTarget endTarget) { boolean isQuickSwitch = endTarget == GestureState.GestureEndTarget.NEW_TASK;