Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 804d70a0 authored by Cosmin Băieș's avatar Cosmin Băieș Committed by Android (Google) Code Review
Browse files

Merge "Check IME parent visibility for IME window focus" into main

parents 29baa8ae c1fe8472
Loading
Loading
Loading
Loading
+21 −14
Original line number Diff line number Diff line
@@ -831,21 +831,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            return false;
        }

        // When switching the app task, we keep the IME window visibility for better
        // transitioning experiences.
        // However, in case IME created a child window or the IME selection dialog without
        // dismissing during the task switching to keep the window focus because IME window has
        // higher window hierarchy, we don't give it focus if the next IME layering target
        // doesn't request IME visible.
        if (w.mIsImWindow && w.isChildWindow() && (mImeLayeringTarget == null
                || !mImeLayeringTarget.isRequestedVisible(ime()))) {
        // IME windows remain visibleRequested while switching apps to maintain a smooth animation.
        // This persists until the new app is focused, so they can be visibleRequested despite not
        // being visible to the user (i.e. occluded). These rank higher in the window hierarchy than
        // app windows, so they will always be considered first. To avoid having the focus stuck,
        // an IME window (child or not) cannot be focused if the IME parent is not visible. However,
        // child windows also require the IME to be visible in the current app.
        if (w.mIsImWindow) {
            final boolean imeParentVisible = mInputMethodSurfaceParentWindow != null
                    && mInputMethodSurfaceParentWindow.isVisibleRequested();
            if (!imeParentVisible) {
                ProtoLog.v(WM_DEBUG_FOCUS, "findFocusedWindow: IME window not focusable as"
                        + " IME parent is not visible");
                return false;
            }
        if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
                && !(mImeLayeringTarget.isRequestedVisible(ime())
                        && mImeLayeringTarget.isVisibleRequested())) {

            if (w.isChildWindow()
                    && !getInsetsStateController().getImeSourceProvider().isImeShowing()) {
                ProtoLog.v(WM_DEBUG_FOCUS, "findFocusedWindow: IME child window not focusable as"
                        + " IME is not visible");
                return false;
            }
        }

        final ActivityRecord activity = w.mActivityRecord;

+57 −35
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -56,6 +55,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
@@ -2693,55 +2693,77 @@ public class DisplayContentTests extends WindowTestsBase {

    @SetupWindows(addWindows = W_INPUT_METHOD)
    @Test
    public void testImeChildWindowFocusWhenImeLayeringTargetChanges() {
        final WindowState imeChildWindow = newWindowBuilder("imeChildWindow",
    public void testImeChildWindowFocusWhenImeParentWindowChanges() {
        final var imeChildWindow = newWindowBuilder("imeChildWindow",
                TYPE_APPLICATION_ATTACHED_DIALOG).setParent(mImeWindow).build();
        makeWindowVisibleAndDrawn(imeChildWindow, mImeWindow);
        assertTrue(imeChildWindow.canReceiveKeys());
        mDisplayContent.setInputMethodWindowLocked(mImeWindow);

        // Verify imeChildWindow can be focused window if the next IME target requests IME visible.
        final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
        mDisplayContent.setImeLayeringTarget(imeAppTarget);
        spyOn(imeAppTarget);
        doReturn(true).when(imeAppTarget).isRequestedVisible(ime());
        assertEquals(imeChildWindow, mDisplayContent.findFocusedWindow());

        // Verify imeChildWindow doesn't be focused window if the next IME target does not
        // request IME visible.
        final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
        mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
        assertNotEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
        doTestImeWindowFocusWhenImeParentWindowChanged(imeChildWindow);
    }

    @SetupWindows(addWindows = W_INPUT_METHOD)
    @Test
    public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
        final WindowState imeMenuDialog = newWindowBuilder("imeMenuDialog",
    public void testImeDialogWindowFocusWhenImeParentWindowChanges() {
        final var imeDialogWindow = newWindowBuilder("imeMenuDialog",
                TYPE_INPUT_METHOD_DIALOG).build();
        makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
        assertTrue(imeMenuDialog.canReceiveKeys());
        doTestImeWindowFocusWhenImeParentWindowChanged(imeDialogWindow);
    }

    @SetupWindows(addWindows = W_INPUT_METHOD)
    @Test
    public void testImeWindowFocusWhenImeParentWindowChanges() {
        // Verify focusable, non-child IME windows.
        final var otherImeWindow = newWindowBuilder("otherImeWindow",
                TYPE_INPUT_METHOD).build();
        doTestImeWindowFocusWhenImeParentWindowChanged(otherImeWindow);
    }

    private void doTestImeWindowFocusWhenImeParentWindowChanged(@NonNull WindowState window) {
        makeWindowVisibleAndDrawn(window, mImeWindow);
        assertTrue("Window canReceiveKeys", window.canReceiveKeys());
        mDisplayContent.setInputMethodWindowLocked(mImeWindow);

        // Verify imeMenuDialog can be focused window if the next IME target requests IME visible.
        // Verify window can be focused if the IME parent is visible and the IME is visible.
        final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
        mDisplayContent.setImeLayeringTarget(imeAppTarget);
        imeAppTarget.setRequestedVisibleTypes(ime());
        assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());

        // Verify imeMenuDialog doesn't be focused window if the next IME target is closing.
        mDisplayContent.updateImeInputAndControlTarget(imeAppTarget);
        final var imeProvider = mDisplayContent.getInsetsStateController().getImeSourceProvider();
        imeProvider.setImeShowing(true);
        final var imeParentWindow = mDisplayContent.getImeParentWindow();
        assertNotNull("IME parent window is not null", imeParentWindow);
        assertTrue("IME parent window is visible", imeParentWindow.isVisibleRequested());
        assertTrue("IME is visible", imeProvider.isImeShowing());
        assertEquals("Window is the focused one", window, mDisplayContent.findFocusedWindow());

        // Verify window can't be focused if the IME parent is not visible.
        final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
        makeWindowVisibleAndDrawn(nextImeAppTarget);
        // Even if the app still requests IME, the ime dialog should not gain focus if the target
        // app is invisible.
        nextImeAppTarget.setRequestedVisibleTypes(ime());
        nextImeAppTarget.mActivityRecord.setVisibility(false);
        // Change layering target but keep input target (and thus imeParent) the same.
        mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
        assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
        // IME parent window is not visible, occluded by new layering target.
        imeParentWindow.setVisibleRequested(false);
        assertEquals("IME parent window did not change", imeParentWindow,
                mDisplayContent.getImeParentWindow());
        assertFalse("IME parent window is not visible", imeParentWindow.isVisibleRequested());
        assertTrue("IME is visible", imeProvider.isImeShowing());
        assertNotEquals("Window is not the focused one when imeParent is not visible", window,
                mDisplayContent.findFocusedWindow());

        // Verify window can be focused if the IME is not visible.
        mDisplayContent.updateImeInputAndControlTarget(nextImeAppTarget);
        imeProvider.setImeShowing(false);
        final var nextImeParentWindow = mDisplayContent.getImeParentWindow();
        assertNotNull("Next IME parent window is not null", nextImeParentWindow);
        assertNotEquals("IME parent window changed", imeParentWindow, nextImeParentWindow);
        assertTrue("Next IME parent window is visible", nextImeParentWindow.isVisibleRequested());
        assertFalse("IME is not visible", imeProvider.isImeShowing());
        if (window.isChildWindow()) {
            assertNotEquals("Child window is not the focused on when the IME is not visible",
                    window, mDisplayContent.findFocusedWindow());
        } else {
            assertEquals("Window is the focused one when the IME is not visible",
                    window, mDisplayContent.findFocusedWindow());
        }
    }

    @Test