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
This commit is contained in:
Ming-Shin Lu
2022-04-14 23:56:36 +08:00
parent ddd9dc84b8
commit b2c568f368
2 changed files with 35 additions and 4 deletions

View File

@@ -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,10 +4314,24 @@ 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;
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

View File

@@ -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();