diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 77787e1e7530f..b9e81bada8b2e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4255,17 +4255,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @VisibleForTesting SurfaceControl computeImeParent() { + if (mImeLayeringTarget != null && mImeInputTarget != null + && mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord()) { + // Do not change parent if the window hasn't requested IME. + return null; + } // Attach it to app if the target is part of an app and such app is covering the entire // screen. If it's not covering the entire screen the IME might extend beyond the apps // bounds. if (shouldImeAttachedToApp()) { - if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord()) { - // Do not change parent if the window hasn't requested IME. - return null; - } return mImeLayeringTarget.mActivityRecord.getSurfaceControl(); } - // Otherwise, we just attach it to where the display area policy put it. return mImeWindowsContainer.getParent() != null ? mImeWindowsContainer.getParent().getSurfaceControl() : null; diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 4fdb1f7930f5d..08b60773a713a 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -424,14 +424,12 @@ class InsetsPolicy { return state; } } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { - // During switching tasks with gestural navigation, if the IME is attached to - // one app window on that time, even the next app window is behind the IME window, - // conceptually the window should not receive the IME insets if the next window is - // not eligible IME requester and ready to show IME on top of it. - final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp(); + // During switching tasks with gestural navigation, before the next IME input target + // starts the input, we should adjust and freeze the last IME visibility of the window + // in case delivering obsoleted IME insets state during transitioning. final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); - if (shouldImeAttachedToApp && originalImeSource != null) { + if (originalImeSource != null) { final boolean imeVisibility = w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME); final InsetsState state = copyState ? new InsetsState(originalState) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 2ea0bdc4354ea..92d0341c40044 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -3073,11 +3073,11 @@ public class ActivityRecordTests extends WindowTestsBase { // Simulate app re-start input or turning screen off/on then unlocked by un-secure // keyguard to back to the app, expect IME insets is not frozen + mDisplayContent.updateImeInputAndControlTarget(app); + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); imeSource.setFrame(new Rect(100, 400, 500, 500)); app.getInsetsState().addSource(imeSource); app.getInsetsState().setSourceVisible(ITYPE_IME, true); - mDisplayContent.updateImeInputAndControlTarget(app); - assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Verify when IME is visible and the app can receive the right IME insets from policy. makeWindowVisibleAndDrawn(app, mImeWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 925f4f5a4130e..2377d63d10cda 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1105,6 +1105,21 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent()); } + @UseTestDisplay(addWindows = W_ACTIVITY) + @Test + public void testComputeImeParent_inputTargetNotUpdate() throws Exception { + WindowState app1 = createWindow(null, TYPE_BASE_APPLICATION, "app1"); + WindowState app2 = createWindow(null, TYPE_BASE_APPLICATION, "app2"); + doReturn(true).when(mDisplayContent).shouldImeAttachedToApp(); + mDisplayContent.setImeLayeringTarget(app1); + mDisplayContent.setImeInputTarget(app1); + assertEquals(app1.mActivityRecord.getSurfaceControl(), mDisplayContent.computeImeParent()); + mDisplayContent.setImeLayeringTarget(app2); + // Expect null means no change IME parent when the IME layering target not yet + // request IME to be the input target. + assertNull(mDisplayContent.computeImeParent()); + } + @Test public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception { final DisplayContent dc = createNewDisplay(); 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 a554fab76c2b8..bcf4e88497477 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -917,6 +917,46 @@ public class WindowStateTests extends WindowTestsBase { assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); } + @Test + public void testAdjustImeInsetsVisibilityWhenSwitchingApps_toAppInMultiWindowMode() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app2 = createWindow(null, WINDOWING_MODE_MULTI_WINDOW, + ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app2"); + final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + spyOn(imeWindow); + doReturn(true).when(imeWindow).isVisible(); + mDisplayContent.mInputMethodWindow = imeWindow; + + final InsetsStateController controller = mDisplayContent.getInsetsStateController(); + controller.getImeSourceProvider().setWindowContainer(imeWindow, null, null); + + // Simulate app2 in multi-window mode is going to background to switch to the fullscreen + // app which requests IME with updating all windows Insets State when IME is above app. + app2.mActivityRecord.mImeInsetsFrozenUntilStartInput = true; + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + assertTrue(mDisplayContent.shouldImeAttachedToApp()); + controller.getImeSourceProvider().scheduleShowImePostLayout(app); + controller.getImeSourceProvider().getSource().setVisible(true); + controller.updateAboveInsetsState(false); + + // Expect app windows behind IME can receive IME insets visible, + // but not for app2 in background. + assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); + assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); + + // Simulate app plays closing transition to app2. + // And app2 is now IME layering target but not yet to be the IME input target. + mDisplayContent.setImeLayeringTarget(app2); + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Verify the IME insets is still visible on app, but not for app2 during task switching. + assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); + assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); + } + @UseTestDisplay(addWindows = {W_ACTIVITY}) @Test public void testUpdateImeControlTargetWhenLeavingMultiWindow() {