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

Commit 9f9e91c2 authored by Felix Stern's avatar Felix Stern
Browse files

Fix showing the IME from EmbeddedWindow

- With the refactor in [1], the IME does not show, when requested from EmbeddedWindow.
- This CL introduces a new parent interface (InsetsTarget) for InsetsControlTarget and InputType. However, an EmbeddedWindow may not be a controlTarget, as it should not be able to control other insets types, except of the IME.
- Previously the initiallyVisible of an IME control was always false [2]. This will now take into account, whether there was an ongoing transition (e.g., in case of app -> Launcher, in this case it is not initiallyVisible), or otherwise whether it is is client- and serverVisible (otherwise it cannot be currently be shown).
- The leash was hidden in InsetsSourceProvider.ControlAdapter#startAnimation, which is due to this change also not needed anymore (otherwise the leash could have a different visibility than given in initiallyVisible).
- DisplayImeController was not using initiallyVisible, but uses it now to update its own imeVisible state and start an animation, if necessary.

[1]: I8e3a74ee579f085cb582040fdba725e7a63d6b85
[2]: I459debedb243b4345b9981b88adf6e4a0ea9b44a

Test: atest CtsSurfaceControlTests:android.view.surfacecontrol.cts.SurfaceControlViewHostTests#testImeVisible
Flag: android.view.inputmethod.refactor_insets_controller
Fix: 355047142
Change-Id: I5aeb3ccfbfe7d4cd0b7b5f0e0400769e65bb70a4
parent 4f667c4e
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -587,7 +587,14 @@ public class WindowlessWindowManager implements IWindowSession {

    @Override
    public void updateRequestedVisibleTypes(IWindow window,
            @InsetsType int requestedVisibleTypes, @Nullable ImeTracker.Token imeStatsToken)  {
            @InsetsType int requestedVisibleTypes, @Nullable ImeTracker.Token imeStatsToken)
            throws RemoteException {
        if (android.view.inputmethod.Flags.refactorInsetsController()) {
            // Embedded windows do not control insets (except for IME). The host window is
            // responsible for controlling the insets.
            mRealWm.updateRequestedVisibleTypes(window,
                    requestedVisibleTypes & WindowInsets.Type.ime(), imeStatsToken);
        }
    }

    @Override
+16 −8
Original line number Diff line number Diff line
@@ -309,21 +309,29 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
                            lastSurfacePosition);
                } else {
                    if (!haveSameLeash(mImeSourceControl, imeSourceControl)) {
                        applyVisibilityToLeash(imeSourceControl);

                        if (android.view.inputmethod.Flags.refactorInsetsController()) {
                            pendingImeStartAnimation = true;
                            // The starting point for the IME should be it's previous state
                            // (whether it is initiallyVisible or not)
                            updateImeVisibility(imeSourceControl.isInitiallyVisible());
                        }
                        applyVisibilityToLeash(imeSourceControl);
                    }
                    if (!mImeShowing) {
                        removeImeSurface(mDisplayId);
                    }
                }
            } else if (!android.view.inputmethod.Flags.refactorInsetsController()
            } else {
                if (!android.view.inputmethod.Flags.refactorInsetsController()
                        && mAnimation != null) {
                // we don"t want to cancel the hide animation, when the control is lost, but
                    // we don't want to cancel the hide animation, when the control is lost, but
                    // continue the bar to slide to the end (even without visible IME)
                    mAnimation.cancel();
                } else if (android.view.inputmethod.Flags.refactorInsetsController() && mImeShowing
                        && mAnimation == null) {
                    // There is no leash, so the IME cannot be in a showing state
                    updateImeVisibility(false);
                }
            }
            if (positionChanged) {
                if (android.view.inputmethod.Flags.refactorInsetsController()) {
@@ -345,7 +353,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged

            if (android.view.inputmethod.Flags.refactorInsetsController()) {
                if (pendingImeStartAnimation) {
                    startAnimation(true, true /* forceRestart */);
                    startAnimation(mImeRequestedVisible, true /* forceRestart */);
                }
            }
        }
+15 −6
Original line number Diff line number Diff line
@@ -710,6 +710,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    @Retention(RetentionPolicy.SOURCE)
    @interface InputMethodTarget {}

    /** The surface parent window of the IME container. */
    private WindowContainer mInputMethodSurfaceParentWindow;
    /** The surface parent of the IME container. */
    @VisibleForTesting
    SurfaceControl mInputMethodSurfaceParent;
@@ -1535,6 +1537,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        return mDisplayRotation.getLastOrientation();
    }

    WindowContainer getImeParentWindow() {
        return mInputMethodSurfaceParentWindow;
    }

    void registerRemoteAnimations(RemoteAnimationDefinition definition) {
        mAppTransitionController.registerRemoteAnimations(definition);
    }
@@ -4739,13 +4745,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
                Slog.i(TAG_WM, "ImeContainer is organized. Skip updateImeParent.");
            }
            // Leave the ImeContainer where the DisplayAreaPolicy placed it.
            // FEATURE_IME is organized by vendor so they are responible for placing the surface.
            // FEATURE_IME is organized by vendor so they are responsible for placing the surface.
            mInputMethodSurfaceParentWindow = null;
            mInputMethodSurfaceParent = null;
            return;
        }

        final SurfaceControl newParent = computeImeParent();
        final var newParentWindow = computeImeParent();
        final SurfaceControl newParent =
                newParentWindow != null ? newParentWindow.getSurfaceControl() : null;
        if (newParent != null && newParent != mInputMethodSurfaceParent) {
            mInputMethodSurfaceParentWindow = newParentWindow;
            mInputMethodSurfaceParent = newParent;
            getSyncTransaction().reparent(mImeWindowsContainer.mSurfaceControl, newParent);
            if (DEBUG_IME_VISIBILITY) {
@@ -4806,7 +4816,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     * Computes the window the IME should be attached to.
     */
    @VisibleForTesting
    SurfaceControl computeImeParent() {
    WindowContainer computeImeParent() {
        if (!ImeTargetVisibilityPolicy.canComputeImeParent(mImeLayeringTarget, mImeInputTarget)) {
            return null;
        }
@@ -4814,11 +4824,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        // screen. If it's not covering the entire screen the IME might extend beyond the apps
        // bounds.
        if (shouldImeAttachedToApp()) {
            return mImeLayeringTarget.mActivityRecord.getSurfaceControl();
            return mImeLayeringTarget.mActivityRecord;
        }
        // Otherwise, we just attach it to where the display area policy put it.
        return mImeWindowsContainer.getParent() != null
                ? mImeWindowsContainer.getParent().getSurfaceControl() : null;
        return mImeWindowsContainer.getParent();
    }

    void setLayoutNeeded() {
+31 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.WindowInsets;
import android.window.InputTransferToken;

import com.android.internal.protolog.ProtoLog;
@@ -222,6 +223,10 @@ class EmbeddedWindowController {

        private boolean mIsFocusable;

        // The EmbeddedWindow can only request the IME. All other insets types are requested by
        // the host window.
        private @WindowInsets.Type.InsetsType int mRequestedVisibleTypes = 0;

        /**
         * @param session  calling session to check ownership of the window
         * @param clientToken client token used to clean up the map if the embedding process dies
@@ -310,6 +315,27 @@ class EmbeddedWindowController {
            return mClient;
        }

        @Override
        public boolean isRequestedVisible(@WindowInsets.Type.InsetsType int types) {
            return (mRequestedVisibleTypes & types) != 0;
        }

        @Override
        public @WindowInsets.Type.InsetsType int getRequestedVisibleTypes() {
            return mRequestedVisibleTypes;
        }

        /**
         * Only the IME can be requested from the EmbeddedWindow.
         * @param requestedVisibleTypes other types than {@link WindowInsets.Type.IME} are
         *                              not sent to system server via WindowlessWindowManager.
         */
        void setRequestedVisibleTypes(@WindowInsets.Type.InsetsType int requestedVisibleTypes) {
            if (mRequestedVisibleTypes != requestedVisibleTypes) {
                mRequestedVisibleTypes = requestedVisibleTypes;
            }
        }

        @Override
        public int getPid() {
            return mOwnerPid;
@@ -375,6 +401,11 @@ class EmbeddedWindowController {

        @Override
        public boolean shouldControlIme() {
            if (android.view.inputmethod.Flags.refactorInsetsController()) {
                // EmbeddedWindow should never be able to control the IME directly, but only the
                // RemoteInsetsControlTarget.
                return false;
            }
            return mHostWindowState != null;
        }

+16 −21
Original line number Diff line number Diff line
@@ -268,7 +268,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {

    // TODO(b/353463205) change statsToken to be NonNull, after the flag is permanently enabled
    @Override
    protected boolean updateClientVisibility(InsetsControlTarget caller,
    protected boolean updateClientVisibility(InsetsTarget caller,
            @Nullable ImeTracker.Token statsToken) {
        InsetsControlTarget controlTarget = getControlTarget();
        if (caller != controlTarget) {
@@ -283,12 +283,13 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
                        ImeTracker.forLogging().onProgress(statsToken,
                                ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
                        controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
                    } else {
                    } else if (caller instanceof InsetsControlTarget) {
                        // In case of a virtual display that cannot show the IME, the
                        // controlTarget will be null here, as no controlTarget was set yet. In
                        // that case, proceed similar to the multi window mode (fallback =
                        // RemoteInsetsControlTarget of the default display)
                        controlTarget = mDisplayContent.getImeHostOrFallback(caller.getWindow());
                        controlTarget = mDisplayContent.getImeHostOrFallback(
                                ((InsetsControlTarget) caller).getWindow());

                        if (controlTarget != caller) {
                            ImeTracker.forLogging().onProgress(statsToken,
@@ -300,8 +301,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
                        }
                    }

                    WindowState windowState = caller.getWindow();
                    invokeOnImeRequestedChangedListener(windowState, statsToken);
                    invokeOnImeRequestedChangedListener(caller, statsToken);
                } else {
                    // TODO(b/353463205) add ImeTracker?
                }
@@ -309,20 +309,16 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
            return false;
        }
        boolean changed = super.updateClientVisibility(caller, statsToken);
        if (!Flags.refactorInsetsController()) {
        if (!Flags.refactorInsetsController() && caller instanceof InsetsControlTarget) {
            if (changed && caller.isRequestedVisible(mSource.getType())) {
                reportImeDrawnForOrganizerIfNeeded(caller);
                reportImeDrawnForOrganizerIfNeeded((InsetsControlTarget) caller);
            }
        }
        changed |= mDisplayContent.onImeInsetsClientVisibilityUpdate();
        if (Flags.refactorInsetsController()) {
            if (changed) {
                // RemoteInsetsControlTarget does not have a window. In this case, we use the
                // windowState from the imeInputTarget
                WindowState windowState = caller.getWindow() != null ? caller.getWindow()
                        : ((mDisplayContent.getImeInputTarget() != null)
                                ? mDisplayContent.getImeInputTarget().getWindowState() : null);
                invokeOnImeRequestedChangedListener(windowState, statsToken);
                invokeOnImeRequestedChangedListener(mDisplayContent.getImeInputTarget(),
                        statsToken);
            } else {
                // TODO(b/329229469) change phase and check cancelled / failed
                ImeTracker.forLogging().onCancelled(statsToken,
@@ -334,32 +330,31 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {

    void onInputTargetChanged(InputTarget target) {
        if (Flags.refactorInsetsController() && target != null) {
            WindowState targetWin = target.getWindowState();
            InsetsControlTarget imeControlTarget = getControlTarget();
            if (target != imeControlTarget && targetWin != null) {
            if (target != imeControlTarget) {
                // If the targetWin is not the imeControlTarget (=RemoteInsetsControlTarget) let it
                // know about the new requestedVisibleTypes for the IME.
                if (imeControlTarget != null) {
                    imeControlTarget.setImeInputTargetRequestedVisibility(
                            (targetWin.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
                            (target.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
                }
            }
        }
    }

    // TODO(b/353463205) check callers to see if we can make statsToken @NonNull
    private void invokeOnImeRequestedChangedListener(WindowState windowState,
    private void invokeOnImeRequestedChangedListener(InsetsTarget insetsTarget,
            @Nullable ImeTracker.Token statsToken) {
        final var imeListener = mDisplayContent.mWmService.mOnImeRequestedChangedListener;
        if (imeListener != null) {
            if (windowState != null) {
            if (insetsTarget != null) {
                ImeTracker.forLogging().onProgress(statsToken,
                        ImeTracker.PHASE_WM_POSTING_CHANGED_IME_VISIBILITY);
                mDisplayContent.mWmService.mH.post(() -> {
                    ImeTracker.forLogging().onProgress(statsToken,
                            ImeTracker.PHASE_WM_INVOKING_IME_REQUESTED_LISTENER);
                    imeListener.onImeRequestedChanged(windowState.mClient.asBinder(),
                            windowState.isRequestedVisible(WindowInsets.Type.ime()), statsToken);
                    imeListener.onImeRequestedChanged(insetsTarget.getWindowToken(),
                            insetsTarget.isRequestedVisible(WindowInsets.Type.ime()), statsToken);
                });
            } else {
                ImeTracker.forLogging().onFailed(statsToken,
@@ -676,7 +671,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
        return target == mDisplayContent.getImeFallback();
    }

    private boolean isImeInputTarget(@NonNull InsetsControlTarget target) {
    private boolean isImeInputTarget(@NonNull InsetsTarget target) {
        return target == mDisplayContent.getImeInputTarget();
    }

Loading