diff options
| author | 2021-01-29 16:45:23 +0000 | |
|---|---|---|
| committer | 2021-01-29 16:45:23 +0000 | |
| commit | 07d71cf8ee8788a313ec12431bce27b87b9e96ea (patch) | |
| tree | 0411d34592e7984abbd6bfb841401c4d61f544e3 | |
| parent | 2d1e90b508971e3cfa52727d595442bb7cef802a (diff) | |
| parent | 0f536fa99489c5cfd2d266e94798b19540e2cd12 (diff) | |
Merge "Better IME transition while switching app with recents (6/N)" into sc-dev
6 files changed, 106 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 73bcf47979fe..0aaa1a1752bf 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -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(); } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 398049fb97c3..267f67759a24 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -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(); } }; @@ -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) { @@ -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) { @@ -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); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 03fca1137e47..dd4ee877c05b 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -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, @@ -2831,7 +2839,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } mSurfaceAnimationSources.clear(); if (mDisplayContent != null) { - mDisplayContent.onWindowAnimationFinished(type); + mDisplayContent.onWindowAnimationFinished(this, type); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9f3188be7623..9a7823e35a01 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -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; @@ -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 diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index b0b8afd6c3a4..df5b48a038f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -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; @@ -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; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 263aa194a108..3231f8b6551a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -810,4 +810,27 @@ public class WindowStateTests extends WindowTestsBase { WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); } + + @Test + public void testSetFreezeInsetsState() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + spyOn(app); + doReturn(true).when(app).isVisible(); + + // Set freezing the insets state to make the window ignore to dispatch insets changed. + final InsetsState expectedState = new InsetsState(app.getInsetsState(), + true /* copySources */); + app.freezeInsetsState(); + assertEquals(expectedState, app.getFrozenInsetsState()); + assertFalse(app.isReadyToDispatchInsetsState()); + assertEquals(expectedState, app.getInsetsState()); + mDisplayContent.getInsetsStateController().notifyInsetsChanged(); + verify(app, never()).notifyInsetsChanged(); + + // Unfreeze the insets state to make the window can dispatch insets changed. + app.clearFrozenInsetsState(); + assertTrue(app.isReadyToDispatchInsetsState()); + mDisplayContent.getInsetsStateController().notifyInsetsChanged(); + verify(app).notifyInsetsChanged(); + } } |