summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nick Chameyev <nickchameyev@google.com> 2024-10-14 17:22:55 +0100
committer Nick Chameyev <nickchameyev@google.com> 2024-10-16 11:11:26 +0100
commit201e538f9641db8419eaf8b009cdfddf184b569e (patch)
treeb1c7adb0b2a2e5a6f61399ce76aa937750c76d64
parent5282b2b035606be96a0e262fbd3c02ab94d029df (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
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java30
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);