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

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

Add ImeTargetChangeListener for IME targets visibility

In order to stablize the IME surface z-ordering use cases from IMMS
side, as IMMS knows the true IME input target availability but lacks
of knowing visibility once the input target removed by itself turns
out unable to hide IME if the next IME layering target on top. Or,
unable to track non-focusable IME layering target visibility
caused IME surface being jumped up-and-down during the IME transition.

In this CL, we added ImeTargetChangeListener for IMMS side
(specifically is ImeVisibilityStateComputer) to track the IME
input target and non-focusable IME layering overlay window visiblity:

    - onImeTargetOverlayVisibilityChanged
    - onImeInputTargetVisibilityChanged

As this CL is the first-step of providing visibility tracking infra to
ImeVisibilityStateComputer, We will submit follow-up CL with using this
callback for fixing related IME layering issues.

Bug: 258048231
Test: atest WindowStateTests
Change-Id: I90c59c298e2e4568e308fb2c0dc4ca309d5546a3
parent 7473649b
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
import com.android.server.wm.ImeTargetChangeListener;
import com.android.server.wm.WindowManagerInternal;

import java.io.PrintWriter;
@@ -172,6 +173,19 @@ public final class ImeVisibilityStateComputer {
        mWindowManagerInternal = wmService;
        mImeDisplayValidator = imeDisplayValidator;
        mPolicy = imePolicy;
        mWindowManagerInternal.setInputMethodTargetChangeListener(new ImeTargetChangeListener() {
            @Override
            public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken,
                    boolean visible, boolean removed) {
                // TODO(b/258048231): implement logic to fix IME layering overlay visibility issue.
            }

            @Override
            public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
                    boolean visibleRequested, boolean removed) {
                // TODO(b/258048231): implement logic to fix IME input target visibility issue.
            }
        });
    }

    /**
+39 −0
Original line number Diff line number Diff line
@@ -656,6 +656,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     */
    private InputTarget mLastImeInputTarget;


    /**
     * Tracks the windowToken of the input method input target and the corresponding
     * {@link WindowContainerListener} for monitoring changes (e.g. the requested visibility
     * change).
     */
    private @Nullable Pair<IBinder, WindowContainerListener> mImeTargetTokenListenerPair;

    /**
     * This controls the visibility and animation of the input method window.
     */
@@ -4248,7 +4256,38 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp

    @VisibleForTesting
    void setImeInputTarget(InputTarget target) {
        if (mImeTargetTokenListenerPair != null) {
            // Unregister the listener before changing to the new IME input target.
            final WindowToken oldToken = mTokenMap.get(mImeTargetTokenListenerPair.first);
            if (oldToken != null) {
                oldToken.unregisterWindowContainerListener(mImeTargetTokenListenerPair.second);
            }
            mImeTargetTokenListenerPair = null;
        }
        mImeInputTarget = target;
        // Notify listeners about IME input target window visibility by the target change.
        if (target != null) {
            // TODO(b/276743705): Let InputTarget register the visibility change of the hierarchy.
            final WindowState targetWin = target.getWindowState();
            if (targetWin != null) {
                mImeTargetTokenListenerPair = new Pair<>(targetWin.mToken.token,
                        new WindowContainerListener() {
                            @Override
                            public void onVisibleRequestedChanged(boolean isVisibleRequested) {
                                // Notify listeners for IME input target window visibility change
                                // requested by the parent container.
                                mWmService.dispatchImeInputTargetVisibilityChanged(
                                        targetWin.mClient.asBinder(), isVisibleRequested,
                                        targetWin.mActivityRecord != null
                                                && targetWin.mActivityRecord.finishing);
                            }
                        });
                targetWin.mToken.registerWindowContainerListener(
                        mImeTargetTokenListenerPair.second);
                mWmService.dispatchImeInputTargetVisibilityChanged(targetWin.mClient.asBinder(),
                        targetWin.isVisible() /* visible */, false /* removed */);
            }
        }
        if (refreshImeSecureFlag(getPendingTransaction())) {
            mWmService.requestTraversal();
        }
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import android.annotation.NonNull;
import android.os.IBinder;

/**
 * Callback the IME targeting window visibility change state for
 * {@link com.android.server.inputmethod.InputMethodManagerService} to manage the IME surface
 * visibility and z-ordering.
 */
public interface ImeTargetChangeListener {
    /**
     * Called when a non-IME-focusable overlay window being the IME layering target (e.g. a
     * window with {@link android.view.WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} and
     * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flags)
     * has changed its window visibility.
     *
     * @param overlayWindowToken the window token of the overlay window.
     * @param visible            the visibility of the overlay window, {@code true} means visible
     *                           and {@code false} otherwise.
     * @param removed            Whether the IME target overlay window has being removed.
     */
    default void onImeTargetOverlayVisibilityChanged(@NonNull IBinder overlayWindowToken,
            boolean visible, boolean removed) {
    }

    /**
     * Called when the visibility of IME input target window has changed.
     *
     * @param imeInputTarget   the window token of the IME input target window.
     * @param visible          the new window visibility made by {@param imeInputTarget}. visible is
     *                         {@code true} when switching to the new visible IME input target
     *                         window and started input, or the same input target relayout to
     *                         visible from invisible. In contrast, visible is {@code false} when
     *                         closing the input target, or the same input target relayout to
     *                         invisible from visible.
     * @param removed          Whether the IME input target window has being removed.
     */
    default void onImeInputTargetVisibilityChanged(@NonNull IBinder imeInputTarget, boolean visible,
            boolean removed) {
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -847,6 +847,16 @@ public abstract class WindowManagerInternal {
        }
    }

    /**
     * Sets by the {@link com.android.server.inputmethod.InputMethodManagerService} to monitor
     * the visibility change of the IME targeted windows.
     *
     * @see ImeTargetChangeListener#onImeTargetOverlayVisibilityChanged
     * @see ImeTargetChangeListener#onImeInputTargetVisibilityChanged
     */
    public abstract void setInputMethodTargetChangeListener(
            @NonNull ImeTargetChangeListener listener);

    /**
     * Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
     */
+44 −1
Original line number Diff line number Diff line
@@ -724,6 +724,9 @@ public class WindowManagerService extends IWindowManager.Stub

    boolean mHardKeyboardAvailable;
    WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;

    @Nullable ImeTargetChangeListener mImeTargetChangeListener;

    SettingsObserver mSettingsObserver;
    final EmbeddedWindowController mEmbeddedWindowController;
    final AnrController mAnrController;
@@ -1808,6 +1811,10 @@ public class WindowManagerService extends IWindowManager.Stub

            if (imMayMove) {
                displayContent.computeImeTarget(true /* updateImeTarget */);
                if (win.isImeOverlayLayeringTarget()) {
                    dispatchImeTargetOverlayVisibilityChanged(client.asBinder(),
                            win.isVisibleRequestedOrAdding(), false /* removed */);
                }
            }

            // Don't do layout here, the window must call
@@ -2329,6 +2336,8 @@ public class WindowManagerService extends IWindowManager.Stub
                winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
            }

            final boolean wasVisible = win.isVisible();

            win.mRelayoutCalled = true;
            win.mInRelayout = true;

@@ -2337,7 +2346,6 @@ public class WindowManagerService extends IWindowManager.Stub
                    "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
                            viewVisibility, new RuntimeException().fillInStackTrace());


            win.setDisplayLayoutNeeded();
            win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;

@@ -2502,6 +2510,18 @@ public class WindowManagerService extends IWindowManager.Stub
            }
            win.mInRelayout = false;

            final boolean winVisibleChanged = win.isVisible() != wasVisible;
            if (win.isImeOverlayLayeringTarget() && winVisibleChanged) {
                dispatchImeTargetOverlayVisibilityChanged(client.asBinder(),
                        win.isVisible(), false /* removed */);
            }
            // Notify listeners about IME input target window visibility change.
            final boolean isImeInputTarget = win.getDisplayContent().getImeInputTarget() == win;
            if (isImeInputTarget && winVisibleChanged) {
                dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(),
                        win.isVisible() /* visible */, false /* removed */);
            }

            if (outSyncIdBundle != null) {
                final int maybeSyncSeqId;
                if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility == View.VISIBLE
@@ -3326,6 +3346,22 @@ public class WindowManagerService extends IWindowManager.Stub
        });
    }

    void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token, boolean visible,
            boolean removed) {
        if (mImeTargetChangeListener != null) {
            mH.post(() -> mImeTargetChangeListener.onImeTargetOverlayVisibilityChanged(token,
                    visible, removed));
        }
    }

    void dispatchImeInputTargetVisibilityChanged(@NonNull IBinder token, boolean visible,
            boolean removed) {
        if (mImeTargetChangeListener != null) {
            mH.post(() -> mImeTargetChangeListener.onImeInputTargetVisibilityChanged(token,
                    visible, removed));
        }
    }

    @Override
    public void setSwitchingUser(boolean switching) {
        if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -8238,6 +8274,13 @@ public class WindowManagerService extends IWindowManager.Stub
            }
            return null;
        }

        @Override
        public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) {
            synchronized (mGlobalLock) {
                mImeTargetChangeListener = listener;
            }
        }
    }

    private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
Loading