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

Commit b2c568f3 authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Fix IME layering issue by non-focusable IME target overlay

CL[1] aims to polish the IME flicker issue happens from switching the
app task with IME shown to the new split-screen task.

The CL added a check to not change the IME parent on top of the layering
tartet if the IME layering target window hasn't requested IME.

However, there is an edge case that if the app creates a
non-focusable application overlay window that intentionaly makes IME
layering above this overlay with "NOT_FOCUSABLE | ALT_FOCUSABLE_IM" flags
which means that even the overlay isn't request the IME explicitly,
the IME still allows to reparent on top of the IME layering target.

With the CL, it breaks if an activity behind the overlay requests IME and
will not show up since the overlay has taken the layering control but without
the overlay window requests the IME, IME will not reparent on top of the
acitvity.

To fix it, we should also check if the IME layering target may actually
become the input target, if so, then hold the reparent change until
the target has started the input, if not like the overlay use case then
we can allow the IME reparent on top of it directly.

[1]: I332c0e4fff62df5d7b793eda2767bb58fe85a938

Fix: 228766370
Test: atest DisplayContentTests#testComputeImeParent_inputTargetNotUpdate
Test: atest WindowStateTests#\
   testAdjustImeInsetsVisibilityWhenSwitchingApps_toAppInMultiWindowMode
Test: atest DisplayContentTests#testComputeImeParent_inputTargetNotUpdate
Test: manual by using EditorTextVariations tool
 1) make and install EditTextVariations
 2) Enable "Settings > Display over other apps" for
    EditTextVariations
 3) Launch EditTextVariations from all apps
 4) Menu -> Show IME focuable overlay
 5) Go to home screen by gesture or pressing home key
 6) Launch any app (e.g. Chrome) and tap the editor
 7) Expect IME can show up
Test: make sure bug 210391817 won't happen

Change-Id: I97025e9466d79d3fc28b717e597a558bc90b08ff
parent ddd9dc84
Loading
Loading
Loading
Loading
+19 −4
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -4313,11 +4314,25 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     */
    @VisibleForTesting
    SurfaceControl computeImeParent() {
        if (mImeLayeringTarget != null && mImeInputTarget != null
        if (mImeLayeringTarget != null) {
            // Ensure changing the IME parent when the layering target that may use IME has
            // became to the input target for preventing IME flickers.
            // Note that:
            // 1) For the imeLayeringTarget that may not use IME but requires IME on top
            // of it (e.g. an overlay window with NOT_FOCUSABLE|ALT_FOCUSABLE_IM flags), we allow
            // it to re-parent the IME on top the display to keep the legacy behavior.
            // 2) Even though the starting window won't use IME, the associated activity
            // behind the starting window may request the input. If so, then we should still hold
            // the IME parent change until the activity started the input.
            boolean imeLayeringTargetMayUseIme =
                    LayoutParams.mayUseInputMethod(mImeLayeringTarget.mAttrs.flags)
                    || mImeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING;
            if (imeLayeringTargetMayUseIme && 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.
+16 −0
Original line number Diff line number Diff line
@@ -41,8 +41,10 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -1165,6 +1167,20 @@ public class DisplayContentTests extends WindowTestsBase {
        assertNull(mDisplayContent.computeImeParent());
    }

    @UseTestDisplay(addWindows = W_ACTIVITY)
    @Test
    public void testComputeImeParent_updateParentWhenTargetNotUseIme() throws Exception {
        WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay");
        overlay.setBounds(100, 100, 200, 200);
        overlay.mAttrs.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
        WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app");
        mDisplayContent.setImeLayeringTarget(overlay);
        mDisplayContent.setImeInputTarget(app);
        assertFalse(mDisplayContent.shouldImeAttachedToApp());
        assertEquals(mDisplayContent.getImeContainer().getParentSurfaceControl(),
                mDisplayContent.computeImeParent());
    }

    @Test
    public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception {
        final DisplayContent dc = createNewDisplay();