diff options
| author | 2024-10-14 17:22:55 +0100 | |
|---|---|---|
| committer | 2024-10-16 11:11:26 +0100 | |
| commit | 201e538f9641db8419eaf8b009cdfddf184b569e (patch) | |
| tree | b1c7adb0b2a2e5a6f61399ce76aa937750c76d64 | |
| parent | 5282b2b035606be96a0e262fbd3c02ab94d029df (diff) | |
Finish unfold transition after timeout
Adds a safeguard to finish the unfold Shell transition animation in
case if UnfoldTransitionProgressProvider won't send the 'animation
finish' event or it has already sent it by the time we received
startAnimation.
This should happen rarely, either due to a timing difference
(startAnimation is called way too late, more than ~1 second
after turning on the screen) or if there is a bug in the implementation
causing the animation not to finish. These problems should be addressed
as part of b/318803244.
Bug: 372319646
Test: atest UnfoldTransitionHandlerTest
Test: remove transtion finishing, fold/unfold
=> verify that transition is finished after timeout
Test: fold multiple times quickly using a script
=> verify all transitions are finished
Flag: EXEMPT bugfix
Change-Id: I75176986ac815c45216731ffa42225d2748f3456
3 files changed, 54 insertions, 1 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 52262e68c401..7a9d9734baa0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -596,9 +596,10 @@ public abstract class WMShellModule { TransactionPool transactionPool, Transitions transitions, @ShellMainThread ShellExecutor executor, + @ShellMainThread Handler handler, ShellInit shellInit) { return new UnfoldTransitionHandler(shellInit, progressProvider.get(), animator, - unfoldAnimator, transactionPool, executor, transitions); + unfoldAnimator, transactionPool, executor, handler, transitions); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index 5a2abe1e7e25..3e0e15afc53a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -24,7 +24,9 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITI import android.animation.ValueAnimator; import android.app.ActivityManager; +import android.os.Handler; import android.os.IBinder; +import android.util.Slog; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -33,6 +35,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.shared.TransactionPool; @@ -59,6 +62,10 @@ import java.util.concurrent.Executor; */ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener { + private static final String TAG = "UnfoldTransitionHandler"; + @VisibleForTesting + static final int FINISH_ANIMATION_TIMEOUT_MILLIS = 5_000; + @Retention(RetentionPolicy.SOURCE) @IntDef({ DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE, @@ -75,6 +82,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene private final Transitions mTransitions; private final Executor mExecutor; private final TransactionPool mTransactionPool; + private final Handler mHandler; @Nullable private TransitionFinishCallback mFinishCallback; @@ -87,17 +95,25 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene private float mLastAnimationProgress = 0.0f; private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>(); + private final Runnable mAnimationPlayingTimeoutRunnable = () -> { + Slog.wtf(TAG, "Timeout occurred when playing the unfold animation, " + + "force finishing the transition"); + finishTransitionIfNeeded(); + }; + public UnfoldTransitionHandler(ShellInit shellInit, ShellUnfoldProgressProvider unfoldProgressProvider, FullscreenUnfoldTaskAnimator fullscreenUnfoldAnimator, SplitTaskUnfoldAnimator splitUnfoldTaskAnimator, TransactionPool transactionPool, Executor executor, + Handler handler, Transitions transitions) { mUnfoldProgressProvider = unfoldProgressProvider; mTransitions = transitions; mTransactionPool = transactionPool; mExecutor = executor; + mHandler = handler; mAnimators.add(splitUnfoldTaskAnimator); mAnimators.add(fullscreenUnfoldAnimator); @@ -159,6 +175,11 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene // finish shell transition immediately if (mAnimationFinished) { finishTransitionIfNeeded(); + } else { + // TODO: b/318803244 - remove timeout handling when we could guarantee that + // the animation will be always finished after receiving startAnimation + mHandler.removeCallbacks(mAnimationPlayingTimeoutRunnable); + mHandler.postDelayed(mAnimationPlayingTimeoutRunnable, FINISH_ANIMATION_TIMEOUT_MILLIS); } return true; @@ -333,6 +354,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene animator.stop(); } + mHandler.removeCallbacks(mAnimationPlayingTimeoutRunnable); mFinishCallback.onTransitionFinished(null); mFinishCallback = null; mTransition = null; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java index 22da66dd5e67..c36b88e34835 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java @@ -20,6 +20,8 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_NONE; +import static com.android.wm.shell.unfold.UnfoldTransitionHandler.FINISH_ANIMATION_TIMEOUT_MILLIS; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -32,7 +34,9 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.graphics.Rect; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.test.TestLooper; import android.view.Display; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -67,6 +71,8 @@ public class UnfoldTransitionHandlerTest { private FullscreenUnfoldTaskAnimator mFullscreenUnfoldTaskAnimator; private SplitTaskUnfoldAnimator mSplitTaskUnfoldAnimator; private Transitions mTransitions; + private TestLooper mTestLooper; + private Handler mHandler; private final IBinder mTransition = new Binder(); @@ -75,6 +81,9 @@ public class UnfoldTransitionHandlerTest { final ShellExecutor executor = new TestSyncExecutor(); final ShellInit shellInit = new ShellInit(executor); + mTestLooper = new TestLooper(); + mHandler = new Handler(mTestLooper.getLooper()); + mFullscreenUnfoldTaskAnimator = mock(FullscreenUnfoldTaskAnimator.class); mSplitTaskUnfoldAnimator = mock(SplitTaskUnfoldAnimator.class); mTransitions = mock(Transitions.class); @@ -86,6 +95,7 @@ public class UnfoldTransitionHandlerTest { mSplitTaskUnfoldAnimator, mTransactionPool, executor, + mHandler, mTransitions ); @@ -159,6 +169,7 @@ public class UnfoldTransitionHandlerTest { TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class); mUnfoldTransitionHandler.mergeAnimation(new Binder(), createFoldTransitionInfo(), mock(SurfaceControl.Transaction.class), mTransition, mergeFinishCallback); + mTestLooper.dispatchAll(); // Verify that fold transition is merged into unfold and that unfold is finished final InOrder inOrder = inOrder(mergeFinishCallback, finishCallback); @@ -201,6 +212,25 @@ public class UnfoldTransitionHandlerTest { } @Test + public void startAnimation_animationHasNotFinishedAfterTimeout_finishesTheTransition() { + TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo(); + mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); + TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); + mUnfoldTransitionHandler.startAnimation( + mTransition, + mock(TransitionInfo.class), + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), + finishCallback + ); + + mTestLooper.moveTimeForward(FINISH_ANIMATION_TIMEOUT_MILLIS + 1); + mTestLooper.dispatchAll(); + + verify(finishCallback).onTransitionFinished(any()); + } + + @Test public void startAnimation_differentTransitionFromRequestWithResize_doesNotStartAnimation() { mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo()); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); |