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 Original line Diff line number Diff line
@@ -831,21 +831,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            return false;
            return false;
        }
        }


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

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


        final ActivityRecord activity = w.mActivityRecord;
        final ActivityRecord activity = w.mActivityRecord;


+57 −35
Original line number Original line 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_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
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.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
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_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
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_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_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
@@ -2693,55 +2693,77 @@ public class DisplayContentTests extends WindowTestsBase {


    @SetupWindows(addWindows = W_INPUT_METHOD)
    @SetupWindows(addWindows = W_INPUT_METHOD)
    @Test
    @Test
    public void testImeChildWindowFocusWhenImeLayeringTargetChanges() {
    public void testImeChildWindowFocusWhenImeParentWindowChanges() {
        final WindowState imeChildWindow = newWindowBuilder("imeChildWindow",
        final var imeChildWindow = newWindowBuilder("imeChildWindow",
                TYPE_APPLICATION_ATTACHED_DIALOG).setParent(mImeWindow).build();
                TYPE_APPLICATION_ATTACHED_DIALOG).setParent(mImeWindow).build();
        makeWindowVisibleAndDrawn(imeChildWindow, mImeWindow);
        doTestImeWindowFocusWhenImeParentWindowChanged(imeChildWindow);
        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());
    }
    }


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

    @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);
        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",
        final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
        mDisplayContent.setImeLayeringTarget(imeAppTarget);
        mDisplayContent.setImeLayeringTarget(imeAppTarget);
        imeAppTarget.setRequestedVisibleTypes(ime());
        mDisplayContent.updateImeInputAndControlTarget(imeAppTarget);
        assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
        final var imeProvider = mDisplayContent.getInsetsStateController().getImeSourceProvider();

        imeProvider.setImeShowing(true);
        // Verify imeMenuDialog doesn't be focused window if the next IME target is closing.
        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",
        final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
        makeWindowVisibleAndDrawn(nextImeAppTarget);
        makeWindowVisibleAndDrawn(nextImeAppTarget);
        // Even if the app still requests IME, the ime dialog should not gain focus if the target
        // Change layering target but keep input target (and thus imeParent) the same.
        // app is invisible.
        nextImeAppTarget.setRequestedVisibleTypes(ime());
        nextImeAppTarget.mActivityRecord.setVisibility(false);
        mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
        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
    @Test