Loading libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +46 −13 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.wm.shell.unfold; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_TO_BACK; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS; import static com.android.wm.shell.transition.Transitions.TRANSIT_BUBBLE_CONVERT_FLOATING_TO_BAR; Loading Loading @@ -234,16 +236,34 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback) { if (info.getType() != TRANSIT_CHANGE && info.getType() != TRANSIT_BUBBLE_CONVERT_FLOATING_TO_BAR) { return; } if ((info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) { return; } // TODO (b/286928742) unfold transition handler should be part of mixed handler to // handle merges better. final boolean merged = switch (info.getType()) { case TRANSIT_CHANGE, TRANSIT_BUBBLE_CONVERT_FLOATING_TO_BAR -> tryMergeBubbleTaskTransition(info, startT, finishT); case TRANSIT_CLOSE -> tryMergeTaskFragmentClose(info, startT); default -> false; }; if (!merged) return; // Apply changes happening during the unfold animation immediately startT.apply(); finishCallback.onTransitionFinished(null); if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) { // Force-finish current unfold animation as we are processing folding now which doesn't // have any animations on the Shell side finishTransitionIfNeeded(); } } private boolean tryMergeBubbleTaskTransition(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT) { for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change change = info.getChanges().get(i); final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); Loading @@ -258,24 +278,37 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene .get() .mergeTaskWithUnfold(taskInfo, info, change, startT, finishT); if (!merged) { return; return false; } } else { return; return false; } } } // Apply changes happening during the unfold animation immediately startT.apply(); finishCallback.onTransitionFinished(null); return true; } if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) { // Force-finish current unfold animation as we are processing folding now which doesn't // have any animations on the Shell side finishTransitionIfNeeded(); /** * Tries to merge TRANSIT_CLOSE for task fragments * @return true if we should continue with merging, false otherwise */ private boolean tryMergeTaskFragmentClose(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT) { boolean shouldMerge = false; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change change = info.getChanges().get(i); final boolean isHideChange = change.getMode() == TRANSIT_TO_BACK || change.getMode() == TRANSIT_CLOSE; if (change.getTaskFragmentToken() != null && isHideChange) { shouldMerge = true; startT.hide(change.getLeash()); } } return shouldMerge; } /** Whether `request` contains an unfold action. */ public boolean shouldPlayUnfoldAnimation(@NonNull TransitionRequestInfo request) { // Unfold animation won't play when animations are disabled Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java +101 −88 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wm.shell.unfold; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_NONE; Loading @@ -25,12 +26,15 @@ import static com.android.wm.shell.unfold.UnfoldTransitionHandler.FINISH_ANIMATI import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.RETURNS_SELF; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.graphics.Rect; import android.os.Binder; Loading Loading @@ -161,19 +165,11 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to // notify about the end of the animation mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); // Send fold transition request TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class); mUnfoldTransitionHandler.mergeAnimation(new Binder(), createFoldTransitionInfo(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), mTransition, mergeFinishCallback); mergeAnimation(createFoldTransitionInfo(), mergeFinishCallback); mTestLooper.dispatchAll(); // Verify that fold transition is merged into unfold and that unfold is finished Loading @@ -182,19 +178,61 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { inOrder.verify(finishCallback).onTransitionFinished(any()); } @Test public void handleCloseTransitionMergeRequestWithTaskFragmentClose_acceptsMerge() { TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo(); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to // notify about the end of the animation startAnimation(finishCallback); // Create CLOSE transition request with task fragment TRANSIT_CLOSE change final TransitionInfo.Change closeTaskFragmentChange = new TransitionInfo.Change(/* container= */ null, /* leash= */ null); closeTaskFragmentChange.setTaskFragmentToken(new Binder()); closeTaskFragmentChange.setMode(TRANSIT_CLOSE); final TransitionInfo closeTransitionInfo = createCloseTransitionInfo(closeTaskFragmentChange); TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class); mergeAnimation(closeTransitionInfo, mergeFinishCallback); mTestLooper.dispatchAll(); verify(mergeFinishCallback).onTransitionFinished(any()); } @Test public void handleCloseTransitionMergeRequestWithoutTaskFragmentClose_doesNotAcceptMerge() { TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo(); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to // notify about the end of the animation startAnimation(finishCallback); // Create CLOSE transition without task fragment change (task fragment token is null) final TransitionInfo.Change closeTaskFragmentChange = new TransitionInfo.Change(/* container= */ null, /* leash= */ null); closeTaskFragmentChange.setTaskFragmentToken(null); closeTaskFragmentChange.setMode(TRANSIT_CLOSE); final TransitionInfo closeTransitionInfo = createCloseTransitionInfo(closeTaskFragmentChange); TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class); mergeAnimation(closeTransitionInfo, mergeFinishCallback); mTestLooper.dispatchAll(); verify(mergeFinishCallback, never()).onTransitionFinished(any()); } @Test public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() { 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 ); startAnimation(finishCallback); verify(finishCallback, never()).onTransitionFinished(any()); } Loading @@ -205,11 +243,9 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); boolean animationStarted = mUnfoldTransitionHandler.startAnimation( boolean animationStarted = startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); Loading @@ -221,13 +257,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { 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 ); startAnimation(finishCallback); mTestLooper.moveTimeForward(FINISH_ANIMATION_TIMEOUT_MILLIS + 1); mTestLooper.dispatchAll(); Loading @@ -240,13 +270,8 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo()); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); boolean animationStarted = mUnfoldTransitionHandler.startAnimation( mTransition, createUnfoldTransitionInfo(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); boolean animationStarted = startAnimation(mTransition, createUnfoldTransitionInfo(), finishCallback); assertThat(animationStarted).isFalse(); } Loading @@ -256,13 +281,8 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo()); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); boolean animationStarted = mUnfoldTransitionHandler.startAnimation( mTransition, createNonUnfoldTransitionInfo(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); boolean animationStarted = startAnimation(mTransition, createNonUnfoldTransitionInfo(), finishCallback); assertThat(animationStarted).isFalse(); } Loading @@ -273,13 +293,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); mShellUnfoldProgressProvider.onStateChangeStarted(); mShellUnfoldProgressProvider.onStateChangeFinished(); Loading @@ -295,13 +309,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mShellUnfoldProgressProvider.onStateChangeStarted(); mShellUnfoldProgressProvider.onStateChangeProgress(0.5f); mShellUnfoldProgressProvider.onStateChangeFinished(); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); verify(finishCallback).onTransitionFinished(any()); } Loading @@ -316,13 +324,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mShellUnfoldProgressProvider.onStateChangeStarted(); mShellUnfoldProgressProvider.onStateChangeFinished(); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); clearInvocations(finishCallback); // Fold Loading Loading @@ -354,13 +356,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { // Unfold mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ false); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); // Start animation but don't finish it mShellUnfoldProgressProvider.onStateChangeStarted(); Loading @@ -379,31 +375,16 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); TransitionFinishCallback mergeCallback = mock(TransitionFinishCallback.class); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback); startAnimation(finishCallback); // Offer a keyguard unlock transition - this should NOT merge mUnfoldTransitionHandler.mergeAnimation( new Binder(), mergeAnimation( new TransitionInfoBuilder(TRANSIT_CHANGE, TRANSIT_FLAG_KEYGUARD_GOING_AWAY).build(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), mTransition, mergeCallback); verify(finishCallback, never()).onTransitionFinished(any()); // Offer a CHANGE-only transition - this SHOULD merge (b/278064943) mUnfoldTransitionHandler.mergeAnimation( new Binder(), new TransitionInfoBuilder(TRANSIT_CHANGE).build(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), mTransition, mergeCallback); mergeAnimation(new TransitionInfoBuilder(TRANSIT_CHANGE).build(), mergeCallback); verify(mergeCallback).onTransitionFinished(any()); // We should never have finished the original transition. Loading Loading @@ -434,6 +415,38 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { return transitionInfo; } private TransitionInfo createCloseTransitionInfo(@Nullable TransitionInfo.Change changeToAdd) { final TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0); transitionInfo.addChange(changeToAdd); return transitionInfo; } private boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo transitionInfo, @NonNull TransitionFinishCallback finishCallback) { return mUnfoldTransitionHandler.startAnimation( transition, transitionInfo, mTransactionPool.acquire(), mTransactionPool.acquire(), finishCallback); } private boolean startAnimation(@NonNull TransitionFinishCallback finishCallback) { return startAnimation(mTransition, mock(TransitionInfo.class), finishCallback); } private void mergeAnimation(@NonNull TransitionInfo transitionInfo, @NonNull TransitionFinishCallback mergeCallback) { mUnfoldTransitionHandler.mergeAnimation( new Binder(), transitionInfo, mTransactionPool.acquire(), mTransactionPool.acquire(), mTransition, mergeCallback); } private TransitionRequestInfo createNoneTransitionInfo() { return new TransitionRequestInfo(TRANSIT_NONE, /* triggerTask= */ null, /* remoteTransition= */ null, Loading Loading @@ -474,7 +487,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { private static class TestTransactionPool extends TransactionPool { @Override public SurfaceControl.Transaction acquire() { return mock(SurfaceControl.Transaction.class); return mock(SurfaceControl.Transaction.class, RETURNS_SELF); } @Override Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +46 −13 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.wm.shell.unfold; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_TO_BACK; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS; import static com.android.wm.shell.transition.Transitions.TRANSIT_BUBBLE_CONVERT_FLOATING_TO_BAR; Loading Loading @@ -234,16 +236,34 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback) { if (info.getType() != TRANSIT_CHANGE && info.getType() != TRANSIT_BUBBLE_CONVERT_FLOATING_TO_BAR) { return; } if ((info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) { return; } // TODO (b/286928742) unfold transition handler should be part of mixed handler to // handle merges better. final boolean merged = switch (info.getType()) { case TRANSIT_CHANGE, TRANSIT_BUBBLE_CONVERT_FLOATING_TO_BAR -> tryMergeBubbleTaskTransition(info, startT, finishT); case TRANSIT_CLOSE -> tryMergeTaskFragmentClose(info, startT); default -> false; }; if (!merged) return; // Apply changes happening during the unfold animation immediately startT.apply(); finishCallback.onTransitionFinished(null); if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) { // Force-finish current unfold animation as we are processing folding now which doesn't // have any animations on the Shell side finishTransitionIfNeeded(); } } private boolean tryMergeBubbleTaskTransition(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT) { for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change change = info.getChanges().get(i); final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); Loading @@ -258,24 +278,37 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene .get() .mergeTaskWithUnfold(taskInfo, info, change, startT, finishT); if (!merged) { return; return false; } } else { return; return false; } } } // Apply changes happening during the unfold animation immediately startT.apply(); finishCallback.onTransitionFinished(null); return true; } if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) { // Force-finish current unfold animation as we are processing folding now which doesn't // have any animations on the Shell side finishTransitionIfNeeded(); /** * Tries to merge TRANSIT_CLOSE for task fragments * @return true if we should continue with merging, false otherwise */ private boolean tryMergeTaskFragmentClose(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT) { boolean shouldMerge = false; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change change = info.getChanges().get(i); final boolean isHideChange = change.getMode() == TRANSIT_TO_BACK || change.getMode() == TRANSIT_CLOSE; if (change.getTaskFragmentToken() != null && isHideChange) { shouldMerge = true; startT.hide(change.getLeash()); } } return shouldMerge; } /** Whether `request` contains an unfold action. */ public boolean shouldPlayUnfoldAnimation(@NonNull TransitionRequestInfo request) { // Unfold animation won't play when animations are disabled Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java +101 −88 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wm.shell.unfold; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_NONE; Loading @@ -25,12 +26,15 @@ import static com.android.wm.shell.unfold.UnfoldTransitionHandler.FINISH_ANIMATI import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.RETURNS_SELF; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.graphics.Rect; import android.os.Binder; Loading Loading @@ -161,19 +165,11 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to // notify about the end of the animation mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); // Send fold transition request TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class); mUnfoldTransitionHandler.mergeAnimation(new Binder(), createFoldTransitionInfo(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), mTransition, mergeFinishCallback); mergeAnimation(createFoldTransitionInfo(), mergeFinishCallback); mTestLooper.dispatchAll(); // Verify that fold transition is merged into unfold and that unfold is finished Loading @@ -182,19 +178,61 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { inOrder.verify(finishCallback).onTransitionFinished(any()); } @Test public void handleCloseTransitionMergeRequestWithTaskFragmentClose_acceptsMerge() { TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo(); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to // notify about the end of the animation startAnimation(finishCallback); // Create CLOSE transition request with task fragment TRANSIT_CLOSE change final TransitionInfo.Change closeTaskFragmentChange = new TransitionInfo.Change(/* container= */ null, /* leash= */ null); closeTaskFragmentChange.setTaskFragmentToken(new Binder()); closeTaskFragmentChange.setMode(TRANSIT_CLOSE); final TransitionInfo closeTransitionInfo = createCloseTransitionInfo(closeTaskFragmentChange); TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class); mergeAnimation(closeTransitionInfo, mergeFinishCallback); mTestLooper.dispatchAll(); verify(mergeFinishCallback).onTransitionFinished(any()); } @Test public void handleCloseTransitionMergeRequestWithoutTaskFragmentClose_doesNotAcceptMerge() { TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo(); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to // notify about the end of the animation startAnimation(finishCallback); // Create CLOSE transition without task fragment change (task fragment token is null) final TransitionInfo.Change closeTaskFragmentChange = new TransitionInfo.Change(/* container= */ null, /* leash= */ null); closeTaskFragmentChange.setTaskFragmentToken(null); closeTaskFragmentChange.setMode(TRANSIT_CLOSE); final TransitionInfo closeTransitionInfo = createCloseTransitionInfo(closeTaskFragmentChange); TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class); mergeAnimation(closeTransitionInfo, mergeFinishCallback); mTestLooper.dispatchAll(); verify(mergeFinishCallback, never()).onTransitionFinished(any()); } @Test public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() { 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 ); startAnimation(finishCallback); verify(finishCallback, never()).onTransitionFinished(any()); } Loading @@ -205,11 +243,9 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); boolean animationStarted = mUnfoldTransitionHandler.startAnimation( boolean animationStarted = startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); Loading @@ -221,13 +257,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { 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 ); startAnimation(finishCallback); mTestLooper.moveTimeForward(FINISH_ANIMATION_TIMEOUT_MILLIS + 1); mTestLooper.dispatchAll(); Loading @@ -240,13 +270,8 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo()); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); boolean animationStarted = mUnfoldTransitionHandler.startAnimation( mTransition, createUnfoldTransitionInfo(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); boolean animationStarted = startAnimation(mTransition, createUnfoldTransitionInfo(), finishCallback); assertThat(animationStarted).isFalse(); } Loading @@ -256,13 +281,8 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo()); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); boolean animationStarted = mUnfoldTransitionHandler.startAnimation( mTransition, createNonUnfoldTransitionInfo(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); boolean animationStarted = startAnimation(mTransition, createNonUnfoldTransitionInfo(), finishCallback); assertThat(animationStarted).isFalse(); } Loading @@ -273,13 +293,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); mShellUnfoldProgressProvider.onStateChangeStarted(); mShellUnfoldProgressProvider.onStateChangeFinished(); Loading @@ -295,13 +309,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mShellUnfoldProgressProvider.onStateChangeStarted(); mShellUnfoldProgressProvider.onStateChangeProgress(0.5f); mShellUnfoldProgressProvider.onStateChangeFinished(); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); verify(finishCallback).onTransitionFinished(any()); } Loading @@ -316,13 +324,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { mShellUnfoldProgressProvider.onStateChangeStarted(); mShellUnfoldProgressProvider.onStateChangeFinished(); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); clearInvocations(finishCallback); // Fold Loading Loading @@ -354,13 +356,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { // Unfold mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ false); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback ); startAnimation(finishCallback); // Start animation but don't finish it mShellUnfoldProgressProvider.onStateChangeStarted(); Loading @@ -379,31 +375,16 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); TransitionFinishCallback mergeCallback = mock(TransitionFinishCallback.class); mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback); startAnimation(finishCallback); // Offer a keyguard unlock transition - this should NOT merge mUnfoldTransitionHandler.mergeAnimation( new Binder(), mergeAnimation( new TransitionInfoBuilder(TRANSIT_CHANGE, TRANSIT_FLAG_KEYGUARD_GOING_AWAY).build(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), mTransition, mergeCallback); verify(finishCallback, never()).onTransitionFinished(any()); // Offer a CHANGE-only transition - this SHOULD merge (b/278064943) mUnfoldTransitionHandler.mergeAnimation( new Binder(), new TransitionInfoBuilder(TRANSIT_CHANGE).build(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), mTransition, mergeCallback); mergeAnimation(new TransitionInfoBuilder(TRANSIT_CHANGE).build(), mergeCallback); verify(mergeCallback).onTransitionFinished(any()); // We should never have finished the original transition. Loading Loading @@ -434,6 +415,38 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { return transitionInfo; } private TransitionInfo createCloseTransitionInfo(@Nullable TransitionInfo.Change changeToAdd) { final TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0); transitionInfo.addChange(changeToAdd); return transitionInfo; } private boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo transitionInfo, @NonNull TransitionFinishCallback finishCallback) { return mUnfoldTransitionHandler.startAnimation( transition, transitionInfo, mTransactionPool.acquire(), mTransactionPool.acquire(), finishCallback); } private boolean startAnimation(@NonNull TransitionFinishCallback finishCallback) { return startAnimation(mTransition, mock(TransitionInfo.class), finishCallback); } private void mergeAnimation(@NonNull TransitionInfo transitionInfo, @NonNull TransitionFinishCallback mergeCallback) { mUnfoldTransitionHandler.mergeAnimation( new Binder(), transitionInfo, mTransactionPool.acquire(), mTransactionPool.acquire(), mTransition, mergeCallback); } private TransitionRequestInfo createNoneTransitionInfo() { return new TransitionRequestInfo(TRANSIT_NONE, /* triggerTask= */ null, /* remoteTransition= */ null, Loading Loading @@ -474,7 +487,7 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase { private static class TestTransactionPool extends TransactionPool { @Override public SurfaceControl.Transaction acquire() { return mock(SurfaceControl.Transaction.class); return mock(SurfaceControl.Transaction.class, RETURNS_SELF); } @Override Loading