Loading services/core/java/com/android/server/wm/DisplayContent.java +8 −1 Original line number Diff line number Diff line Loading @@ -4107,8 +4107,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Callbacks when the given type of {@link WindowContainer} animation finished running in the * hierarchy. */ void onWindowAnimationFinished(int type) { void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) { if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) { // Unfreeze the insets state of the frozen target when the animation finished if exists. final Task task = wc.asTask(); if (task != null) { task.forAllWindows(w -> { w.clearFrozenInsetsState(); }, true /* traverseTopToBottom */); } removeImeSurfaceImmediately(); } } Loading services/core/java/com/android/server/wm/InsetsStateController.java +6 −6 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ class InsetsStateController { private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); private final Consumer<WindowState> mDispatchInsetsChanged = w -> { if (w.isVisible()) { if (w.isReadyToDispatchInsetsState()) { w.notifyInsetsChanged(); } }; Loading Loading @@ -117,7 +117,8 @@ class InsetsStateController { final @InternalInsetsType int type = provider != null ? provider.getSource().getType() : ITYPE_INVALID; return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(), isAboveIme(target)); isAboveIme(target), target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState); } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { Loading @@ -132,7 +133,7 @@ class InsetsStateController { final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token)); return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState); } private boolean isAboveIme(WindowContainer target) { Loading Loading @@ -180,9 +181,8 @@ class InsetsStateController { * @see #getInsetsForWindowMetrics */ private InsetsState getInsetsForTarget(@InternalInsetsType int type, @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) { InsetsState state = mState; @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme, @NonNull InsetsState state) { if (type != ITYPE_INVALID) { state = new InsetsState(state); state.removeSource(type); Loading services/core/java/com/android/server/wm/WindowContainer.java +9 −1 Original line number Diff line number Diff line Loading @@ -2684,6 +2684,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @Nullable ArrayList<WindowContainer> sources) { final Task task = asTask(); if (task != null && !enter && !task.isHomeOrRecentsRootTask()) { if (AppTransition.isClosingTransitOld(transit)) { // Freezes the insets state when the window is in app exiting transition, to // ensure the exiting window won't receive unexpected insets changes from the // next window. task.forAllWindows(w -> { w.freezeInsetsState(); }, true /* traverseTopToBottom */); } mDisplayContent.showImeScreenshot(); } final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, Loading Loading @@ -2831,7 +2839,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } mSurfaceAnimationSources.clear(); if (mDisplayContent != null) { mDisplayContent.onWindowAnimationFinished(type); mDisplayContent.onWindowAnimationFinished(this, type); } } Loading services/core/java/com/android/server/wm/WindowState.java +33 −0 Original line number Diff line number Diff line Loading @@ -713,6 +713,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private @Nullable InsetsSourceProvider mControllableInsetProvider; private final InsetsState mRequestedInsetsState = new InsetsState(); /** * Freeze the insets state in some cases that not necessarily keeps up-to-date to the client. * (e.g app exiting transition) */ private InsetsState mFrozenInsetsState; @Nullable InsetsSourceProvider mPendingPositionChanged; private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; Loading Loading @@ -758,6 +764,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } /** * Set a freeze state for the window to ignore dispatching its insets state to the client. * * Used to keep the insets state for some use cases. (e.g. app exiting transition) */ void freezeInsetsState() { if (mFrozenInsetsState == null) { mFrozenInsetsState = new InsetsState(getInsetsState(), true /* copySources */); } } void clearFrozenInsetsState() { mFrozenInsetsState = null; } InsetsState getFrozenInsetsState() { return mFrozenInsetsState; } /** * Check if the insets state of the window is ready to dispatch to the client when invoking * {@link InsetsStateController#notifyInsetsChanged}. */ boolean isReadyToDispatchInsetsState() { return isVisible() && mFrozenInsetsState == null; } void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation, @Rotation int rotation, boolean requested) { // Invisible windows and the wallpaper do not participate in the seamless rotation animation Loading services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +27 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; Loading Loading @@ -978,6 +980,31 @@ public class WindowContainerTests extends WindowTestsBase { assertEquals(200, listener.mConfiguration.densityDpi); } @Test public void testFreezeInsetsStateWhenAppTransition() { final Task stack = createTaskStackOnDisplay(mDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); spyOn(win); doReturn(true).when(task).okToAnimate(); ArrayList<WindowContainer> sources = new ArrayList<>(); sources.add(activity); // Simulate the task applying the exit transition, verify the main window of the task // will be set the frozen insets state. task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */, false /* isVoiceInteraction */, sources); verify(win).freezeInsetsState(); // Simulate the task transition finished, verify the frozen insets state of the window // will be reset. task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, task.mSurfaceAnimator.getAnimation()); verify(win).clearFrozenInsetsState(); } /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; Loading Loading
services/core/java/com/android/server/wm/DisplayContent.java +8 −1 Original line number Diff line number Diff line Loading @@ -4107,8 +4107,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Callbacks when the given type of {@link WindowContainer} animation finished running in the * hierarchy. */ void onWindowAnimationFinished(int type) { void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) { if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) { // Unfreeze the insets state of the frozen target when the animation finished if exists. final Task task = wc.asTask(); if (task != null) { task.forAllWindows(w -> { w.clearFrozenInsetsState(); }, true /* traverseTopToBottom */); } removeImeSurfaceImmediately(); } } Loading
services/core/java/com/android/server/wm/InsetsStateController.java +6 −6 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ class InsetsStateController { private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); private final Consumer<WindowState> mDispatchInsetsChanged = w -> { if (w.isVisible()) { if (w.isReadyToDispatchInsetsState()) { w.notifyInsetsChanged(); } }; Loading Loading @@ -117,7 +117,8 @@ class InsetsStateController { final @InternalInsetsType int type = provider != null ? provider.getSource().getType() : ITYPE_INVALID; return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(), isAboveIme(target)); isAboveIme(target), target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState); } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { Loading @@ -132,7 +133,7 @@ class InsetsStateController { final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token)); return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState); } private boolean isAboveIme(WindowContainer target) { Loading Loading @@ -180,9 +181,8 @@ class InsetsStateController { * @see #getInsetsForWindowMetrics */ private InsetsState getInsetsForTarget(@InternalInsetsType int type, @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) { InsetsState state = mState; @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme, @NonNull InsetsState state) { if (type != ITYPE_INVALID) { state = new InsetsState(state); state.removeSource(type); Loading
services/core/java/com/android/server/wm/WindowContainer.java +9 −1 Original line number Diff line number Diff line Loading @@ -2684,6 +2684,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @Nullable ArrayList<WindowContainer> sources) { final Task task = asTask(); if (task != null && !enter && !task.isHomeOrRecentsRootTask()) { if (AppTransition.isClosingTransitOld(transit)) { // Freezes the insets state when the window is in app exiting transition, to // ensure the exiting window won't receive unexpected insets changes from the // next window. task.forAllWindows(w -> { w.freezeInsetsState(); }, true /* traverseTopToBottom */); } mDisplayContent.showImeScreenshot(); } final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, Loading Loading @@ -2831,7 +2839,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } mSurfaceAnimationSources.clear(); if (mDisplayContent != null) { mDisplayContent.onWindowAnimationFinished(type); mDisplayContent.onWindowAnimationFinished(this, type); } } Loading
services/core/java/com/android/server/wm/WindowState.java +33 −0 Original line number Diff line number Diff line Loading @@ -713,6 +713,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private @Nullable InsetsSourceProvider mControllableInsetProvider; private final InsetsState mRequestedInsetsState = new InsetsState(); /** * Freeze the insets state in some cases that not necessarily keeps up-to-date to the client. * (e.g app exiting transition) */ private InsetsState mFrozenInsetsState; @Nullable InsetsSourceProvider mPendingPositionChanged; private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; Loading Loading @@ -758,6 +764,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } /** * Set a freeze state for the window to ignore dispatching its insets state to the client. * * Used to keep the insets state for some use cases. (e.g. app exiting transition) */ void freezeInsetsState() { if (mFrozenInsetsState == null) { mFrozenInsetsState = new InsetsState(getInsetsState(), true /* copySources */); } } void clearFrozenInsetsState() { mFrozenInsetsState = null; } InsetsState getFrozenInsetsState() { return mFrozenInsetsState; } /** * Check if the insets state of the window is ready to dispatch to the client when invoking * {@link InsetsStateController#notifyInsetsChanged}. */ boolean isReadyToDispatchInsetsState() { return isVisible() && mFrozenInsetsState == null; } void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation, @Rotation int rotation, boolean requested) { // Invisible windows and the wallpaper do not participate in the seamless rotation animation Loading
services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +27 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; Loading Loading @@ -978,6 +980,31 @@ public class WindowContainerTests extends WindowTestsBase { assertEquals(200, listener.mConfiguration.densityDpi); } @Test public void testFreezeInsetsStateWhenAppTransition() { final Task stack = createTaskStackOnDisplay(mDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); spyOn(win); doReturn(true).when(task).okToAnimate(); ArrayList<WindowContainer> sources = new ArrayList<>(); sources.add(activity); // Simulate the task applying the exit transition, verify the main window of the task // will be set the frozen insets state. task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */, false /* isVoiceInteraction */, sources); verify(win).freezeInsetsState(); // Simulate the task transition finished, verify the frozen insets state of the window // will be reset. task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, task.mSurfaceAnimator.getAnimation()); verify(win).clearFrozenInsetsState(); } /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; Loading