diff options
| author | 2022-03-16 00:26:02 +0000 | |
|---|---|---|
| committer | 2022-03-16 00:26:02 +0000 | |
| commit | 5059dbde7b07cb405c6dedf8e734d09ada6cd90f (patch) | |
| tree | cd5b8f98bdf099ad93564ae9e76d9e02cdf0dd81 | |
| parent | 840f39893e825f92df4fd1aee3acdcc459a52d7e (diff) | |
| parent | dc3dac34bdf38cda039e1b28f0707f31e599a569 (diff) | |
Merge "[18/n] Letterbox Education: start dialog enter animation after all shell transitions have finished" into tm-dev
9 files changed, 176 insertions, 14 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index b2bbafeb7bf5..99b32a677abe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -42,6 +42,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState; import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager; +import com.android.wm.shell.transition.Transitions; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -50,6 +51,8 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; +import dagger.Lazy; + /** * Controller to show/update compat UI components on Tasks based on whether the foreground * activities are in compatibility mode. @@ -102,6 +105,7 @@ public class CompatUIController implements OnDisplaysChangedListener, private final DisplayImeController mImeController; private final SyncTransactionQueue mSyncQueue; private final ShellExecutor mMainExecutor; + private final Lazy<Transitions> mTransitionsLazy; private final CompatUIImpl mImpl = new CompatUIImpl(); private CompatUICallback mCallback; @@ -118,13 +122,15 @@ public class CompatUIController implements OnDisplaysChangedListener, DisplayInsetsController displayInsetsController, DisplayImeController imeController, SyncTransactionQueue syncQueue, - ShellExecutor mainExecutor) { + ShellExecutor mainExecutor, + Lazy<Transitions> transitionsLazy) { mContext = context; mDisplayController = displayController; mDisplayInsetsController = displayInsetsController; mImeController = imeController; mSyncQueue = syncQueue; mMainExecutor = mainExecutor; + mTransitionsLazy = transitionsLazy; mDisplayController.addDisplayWindowListener(this); mImeController.addPositionProcessor(this); mCompatUIHintsState = new CompatUIHintsState(); @@ -302,6 +308,7 @@ public class CompatUIController implements OnDisplaysChangedListener, ShellTaskOrganizer.TaskListener taskListener) { return new LetterboxEduWindowManager(context, taskInfo, mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), + mTransitionsLazy.get(), this::onLetterboxEduDismissed); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java index 03986ee3b6d2..3061eab17d24 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java @@ -18,6 +18,7 @@ package com.android.wm.shell.compatui.letterboxedu; import static com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation; import static com.android.internal.R.styleable.WindowAnimation_windowExitAnimation; +import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -42,7 +43,9 @@ import com.android.internal.policy.TransitionAnimation; class LetterboxEduAnimationController { private static final String TAG = "LetterboxEduAnimation"; - private static final int ENTER_ANIM_START_DELAY_MILLIS = 500; + // If shell transitions are enabled, startEnterAnimation will be called after all transitions + // have finished, and therefore the start delay should be shorter. + private static final int ENTER_ANIM_START_DELAY_MILLIS = ENABLE_SHELL_TRANSITIONS ? 300 : 500; private final TransitionAnimation mTransitionAnimation; private final String mPackageName; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java index 8aa4d0ee99ab..2e0b09e9d230 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java @@ -38,6 +38,7 @@ class LetterboxEduDialogLayout extends ConstraintLayout { // The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque). // 204 is simply 255 * 0.8. static final int BACKGROUND_DIM_ALPHA = 204; + private View mDialogContainer; private TextView mDialogTitle; private Drawable mBackgroundDim; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java index dda72ffb432f..cc3a3b2206f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java @@ -36,6 +36,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.CompatUIWindowManagerAbstract; +import com.android.wm.shell.transition.Transitions; /** * Window manager for the Letterbox Education. @@ -63,6 +64,8 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { private final LetterboxEduAnimationController mAnimationController; + private final Transitions mTransitions; + // Remember the last reported state in case visibility changes due to keyguard or IME updates. private boolean mEligibleForLetterboxEducation; @@ -80,17 +83,19 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { public LetterboxEduWindowManager(Context context, TaskInfo taskInfo, SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener, - DisplayLayout displayLayout, Runnable onDismissCallback) { - this(context, taskInfo, syncQueue, taskListener, displayLayout, onDismissCallback, - new LetterboxEduAnimationController(context)); + DisplayLayout displayLayout, Transitions transitions, + Runnable onDismissCallback) { + this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions, + onDismissCallback, new LetterboxEduAnimationController(context)); } @VisibleForTesting LetterboxEduWindowManager(Context context, TaskInfo taskInfo, SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener, - DisplayLayout displayLayout, Runnable onDismissCallback, + DisplayLayout displayLayout, Transitions transitions, Runnable onDismissCallback, LetterboxEduAnimationController animationController) { super(context, taskInfo, syncQueue, taskListener, displayLayout); + mTransitions = transitions; mOnDismissCallback = onDismissCallback; mAnimationController = animationController; mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation; @@ -132,8 +137,8 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { mLayout = inflateLayout(); updateDialogMargins(); - mAnimationController.startEnterAnimation(mLayout, /* endCallback= */ - this::onDialogEnterAnimationEnded); + // startEnterAnimation will be called immediately if shell-transitions are disabled. + mTransitions.runOnIdle(this::startEnterAnimation); return mLayout; } @@ -158,8 +163,18 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { R.layout.letterbox_education_dialog_layout, null); } + private void startEnterAnimation() { + if (mLayout == null) { + // Dialog has already been released. + return; + } + mAnimationController.startEnterAnimation(mLayout, /* endCallback= */ + this::onDialogEnterAnimationEnded); + } + private void onDialogEnterAnimationEnded() { if (mLayout == null) { + // Dialog has already been released. return; } mLayout.setDismissOnClickListener(this::onDismiss); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index bf0337dbd11a..1ee9407d385d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -96,6 +96,7 @@ import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import java.util.Optional; import dagger.BindsOptionalOf; +import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -210,9 +211,9 @@ public abstract class WMShellBaseModule { static CompatUIController provideCompatUIController(Context context, DisplayController displayController, DisplayInsetsController displayInsetsController, DisplayImeController imeController, SyncTransactionQueue syncQueue, - @ShellMainThread ShellExecutor mainExecutor) { + @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy) { return new CompatUIController(context, displayController, displayInsetsController, - imeController, syncQueue, mainExecutor); + imeController, syncQueue, mainExecutor, transitionsLazy); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index fb3cd87448d1..435d67087f34 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -109,6 +109,9 @@ public class Transitions implements RemoteCallable<Transitions> { /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); + /** List of {@link Runnable} instances to run when the last active transition has finished. */ + private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>(); + private float mTransitionAnimationScaleSetting = 1.0f; private static final class ActiveTransition { @@ -224,6 +227,21 @@ public class Transitions implements RemoteCallable<Transitions> { mRemoteTransitionHandler.removeFiltered(remoteTransition); } + /** + * Runs the given {@code runnable} when the last active transition has finished, or immediately + * if there are currently no active transitions. + * + * <p>This method should be called on the Shell main-thread, where the given {@code runnable} + * will be executed when the last active transition is finished. + */ + public void runOnIdle(Runnable runnable) { + if (mActiveTransitions.isEmpty()) { + runnable.run(); + } else { + mRunWhenIdleQueue.add(runnable); + } + } + /** @return true if the transition was triggered by opening something vs closing something */ public static boolean isOpeningType(@WindowManager.TransitionType int type) { return type == TRANSIT_OPEN @@ -520,6 +538,11 @@ public class Transitions implements RemoteCallable<Transitions> { if (mActiveTransitions.size() <= activeIdx) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations " + "finished"); + // Run all runnables from the run-when-idle queue. + for (int i = 0; i < mRunWhenIdleQueue.size(); i++) { + mRunWhenIdleQueue.get(i).run(); + } + mRunWhenIdleQueue.clear(); return; } // Start animating the next active transition diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index a31b28737552..4607d8acc63e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -53,6 +53,7 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager; +import com.android.wm.shell.transition.Transitions; import org.junit.Before; import org.junit.Test; @@ -62,6 +63,8 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import dagger.Lazy; + /** * Tests for {@link CompatUIController}. * @@ -82,6 +85,7 @@ public class CompatUIControllerTest extends ShellTestCase { private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener; private @Mock SyncTransactionQueue mMockSyncQueue; private @Mock ShellExecutor mMockExecutor; + private @Mock Lazy<Transitions> mMockTransitionsLazy; private @Mock CompatUIWindowManager mMockCompatLayout; private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout; @@ -102,7 +106,8 @@ public class CompatUIControllerTest extends ShellTestCase { doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean()); doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean()); mController = new CompatUIController(mContext, mMockDisplayController, - mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) { + mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor, + mMockTransitionsLazy) { @Override CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java index 337b7385faec..7d51b521a9fb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java @@ -54,6 +54,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.transition.Transitions; import org.junit.After; import org.junit.Before; @@ -86,11 +87,14 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { private ArgumentCaptor<WindowManager.LayoutParams> mWindowAttrsCaptor; @Captor private ArgumentCaptor<Runnable> mEndCallbackCaptor; + @Captor + private ArgumentCaptor<Runnable> mRunOnIdleCaptor; @Mock private LetterboxEduAnimationController mAnimationController; @Mock private SyncTransactionQueue mSyncTransactionQueue; @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock private SurfaceControlViewHost mViewHost; + @Mock private Transitions mTransitions; @Mock private Runnable mOnDismissCallback; private SharedPreferences mSharedPreferences; @@ -204,6 +208,23 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { } @Test + public void testCreateLayout_windowManagerReleasedBeforeTransitionsIsIdle_doesNotStartAnim() { + LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true); + + assertTrue(windowManager.createLayout(/* canShow= */ true)); + + assertTrue(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false)); + + verify(mTransitions).runOnIdle(mRunOnIdleCaptor.capture()); + + windowManager.release(); + + mRunOnIdleCaptor.getValue().run(); + + verify(mAnimationController, never()).startEnterAnimation(any(), any()); + } + + @Test public void testUpdateCompatInfo_updatesLayoutCorrectly() { LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true); @@ -303,6 +324,13 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { } private void verifyAndFinishEnterAnimation(LetterboxEduDialogLayout layout) { + verify(mTransitions).runOnIdle(mRunOnIdleCaptor.capture()); + + // startEnterAnimation isn't called until run-on-idle runnable is called. + verify(mAnimationController, never()).startEnterAnimation(any(), any()); + + mRunOnIdleCaptor.getValue().run(); + verify(mAnimationController).startEnterAnimation(eq(layout), mEndCallbackCaptor.capture()); mEndCallbackCaptor.getValue().run(); } @@ -320,7 +348,8 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { boolean isTaskbarEduShowing) { LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext, createTaskInfo(eligible), mSyncTransactionQueue, mTaskListener, - createDisplayLayout(), mOnDismissCallback, mAnimationController); + createDisplayLayout(), mTransitions, mOnDismissCallback, + mAnimationController); spyOn(windowManager); doReturn(mViewHost).when(windowManager).createSurfaceViewHost(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index dbf93b4d7e0a..a0b12976b467 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -46,6 +46,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -93,7 +94,7 @@ import java.util.ArrayList; * Tests for the shell transitions. * * Build/Install/Run: - * atest WMShellUnitTests:ShellTransitionTests + * atest WMShellUnitTests:ShellTransitionTests */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -600,6 +601,83 @@ public class ShellTransitionTests { assertTrue(DefaultTransitionHandler.isRotationSeamless(seamlessDisplay, displays)); } + @Test + public void testRunWhenIdle() { + Transitions transitions = createTestTransitions(); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + Runnable runnable1 = mock(Runnable.class); + Runnable runnable2 = mock(Runnable.class); + Runnable runnable3 = mock(Runnable.class); + Runnable runnable4 = mock(Runnable.class); + + transitions.runOnIdle(runnable1); + + // runnable1 is executed immediately because there are no active transitions. + verify(runnable1, times(1)).run(); + + clearInvocations(runnable1); + + IBinder transitToken1 = new Binder(); + transitions.requestStartTransition(transitToken1, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + transitions.onTransitionReady(transitToken1, info1, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class)); + assertEquals(1, mDefaultHandler.activeCount()); + + transitions.runOnIdle(runnable2); + transitions.runOnIdle(runnable3); + + // runnable2 and runnable3 aren't executed immediately because there is an active + // transaction. + + IBinder transitToken2 = new Binder(); + transitions.requestStartTransition(transitToken2, + new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); + TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + transitions.onTransitionReady(transitToken2, info2, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class)); + assertEquals(1, mDefaultHandler.activeCount()); + + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + // first transition finished + verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any()); + verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any()); + // But now the "queued" transition is running + assertEquals(1, mDefaultHandler.activeCount()); + + // runnable2 and runnable3 are still not executed because the second transition is still + // active. + verify(runnable2, times(0)).run(); + verify(runnable3, times(0)).run(); + + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any()); + + // runnable2 and runnable3 are executed after the second transition finishes because there + // are no other active transitions, runnable1 isn't executed again. + verify(runnable1, times(0)).run(); + verify(runnable2, times(1)).run(); + verify(runnable3, times(1)).run(); + + clearInvocations(runnable2); + clearInvocations(runnable3); + + transitions.runOnIdle(runnable4); + + // runnable4 is executed immediately because there are no active transitions, all other + // runnables aren't executed again. + verify(runnable1, times(0)).run(); + verify(runnable2, times(0)).run(); + verify(runnable3, times(0)).run(); + verify(runnable4, times(1)).run(); + } + class TransitionInfoBuilder { final TransitionInfo mInfo; @@ -749,7 +827,7 @@ public class ShellTransitionTests { IWindowManager mockWM = mock(IWindowManager.class); final IDisplayWindowListener[] displayListener = new IDisplayWindowListener[1]; try { - doReturn(new int[] {DEFAULT_DISPLAY}).when(mockWM).registerDisplayWindowListener(any()); + doReturn(new int[]{DEFAULT_DISPLAY}).when(mockWM).registerDisplayWindowListener(any()); } catch (RemoteException e) { // No remote stuff happening, so this can't be hit } |