From ec1eac9a06deebb4d97c6c8d63a1fd98e81adbe7 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Wed, 17 Apr 2024 19:49:55 +0800 Subject: Detach stale IME leash for orientation change with gesture navigation Once computeImeTarget is called that updates mImeLayeringTarget, the IME surface container will reparent to the new target when starting input connection (reportStartInput). When there is an orientation change, the insets source control may take time to be ready (drawn). If IME surface container is reparented before the new insets control target updates the insets leash, the IME in previous rotation will show on the app in new rotation. e.g. Top: PortraitApp (visible) > ImeContainer > Ime leash for LandscapeApp LandscapeApp (invisible) And LandscapeApp may still update visibility/position to the leash. So simply detach the leash to avoid showing inconsistent content. The new leash for PortraitApp will be created once IME is drawn. Bug: 335204769 Test: Launch chrome, click search bar to show IME. Rotate device to landscape. Swipe from landscape to portrait home. A landscape IME should not flicker on portrait home. Change-Id: Iac6edc569330b0d89c0ee03fbd1143573babe228 --- .../java/com/android/server/wm/Transition.java | 38 +++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 1b380aadee35..a9301ade3ed3 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -112,7 +112,6 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.Executor; import java.util.function.Predicate; /** @@ -1430,20 +1429,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } if (mTransientLaunches != null) { - InsetsControlTarget prevImeTarget = dc.getImeTarget( - DisplayContent.IME_TARGET_CONTROL); - InsetsControlTarget newImeTarget = null; TaskDisplayArea transientTDA = null; - // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget), - // so re-compute in case the IME target is changed after transition. for (int t = 0; t < mTransientLaunches.size(); ++t) { if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) { - newImeTarget = dc.computeImeTarget(true /* updateImeTarget */); + if (hasVisibleTransientLaunch) { + updateImeForVisibleTransientLaunch(dc); + } transientTDA = mTransientLaunches.keyAt(i).getTaskDisplayArea(); break; } } - if (mRecentsDisplayId != INVALID_DISPLAY && prevImeTarget == newImeTarget) { + if (!hasVisibleTransientLaunch && mRecentsDisplayId == dc.mDisplayId) { // Restore IME icon only when moving the original app task to front from // recents, in case IME icon may missing if the moving task has already been // the current focused task. @@ -1541,6 +1537,32 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return null; } + /** + * Transient-launch activities cannot be IME target (see {@link WindowState#canBeImeTarget}), + * so re-compute in case the IME target is changed after transition. + */ + private void updateImeForVisibleTransientLaunch(@NonNull DisplayContent dc) { + final WindowState imeTarget = dc.computeImeTarget(true /* updateImeTarget */); + final WindowState imeWindow = dc.mInputMethodWindow; + if (imeWindow == null || imeTarget == null + || !mController.hasCollectingRotationChange(dc, dc.getRotation())) { + return; + } + // Drop the insets leash if it is still controlled by previous (invisible) app. This avoids + // showing IME with old rotation on an app with new rotation if IME parent is updated + // but insets leash hasn't been refreshed, i.e. DisplayContent#updateImeParent is called + // but InsetsStateController#notifyControlTargetChanged still waits for IME to redraw. + final InsetsSourceProvider sourceProvider = imeWindow.getControllableInsetProvider(); + if (sourceProvider == null || sourceProvider.mControl == null + || imeTarget == sourceProvider.getControlTarget()) { + return; + } + final SurfaceControl imeInsetsLeash = sourceProvider.mControl.getLeash(); + if (imeInsetsLeash != null) { + dc.getSyncTransaction().reparent(imeInsetsLeash, null); + } + } + void abort() { // This calls back into itself via controller.abort, so just early return here. if (mState == STATE_ABORT) return; -- cgit v1.2.3-59-g8ed1b