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

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

Migrate show/hide IME check softInputMode to ImeVisibilityStateComputer

Bug: 246309664
Test: atest CtsInputMethodTestCases
Change-Id: Ib6ad7b6ddc0a677bbe09f86185e6eb8770177927
parent 279f7a58
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -21,7 +21,10 @@ import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
import static com.android.server.EventLogTags.IMF_HIDE_IME;
import static com.android.server.EventLogTags.IMF_SHOW_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT;

import android.annotation.Nullable;
import android.os.Binder;
@@ -30,6 +33,7 @@ import android.os.ResultReceiver;
import android.util.EventLog;
import android.util.Slog;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -124,6 +128,12 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
    @Override
    public void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
            @ImeVisibilityStateComputer.VisibilityState int state) {
        applyImeVisibility(windowToken, statsToken, state, -1 /* ignore reason */);
    }

    @GuardedBy("ImfLock.class")
    void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
            @ImeVisibilityStateComputer.VisibilityState int state, int reason) {
        switch (state) {
            case STATE_SHOW_IME:
                ImeTracker.get().onProgress(statsToken,
@@ -148,6 +158,17 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
                            ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
                }
                break;
            case STATE_HIDE_IME_EXPLICIT:
                mService.hideCurrentInputLocked(windowToken, statsToken, 0, null, reason);
                break;
            case STATE_HIDE_IME_NOT_ALWAYS:
                mService.hideCurrentInputLocked(windowToken, statsToken,
                        InputMethodManager.HIDE_NOT_ALWAYS, null, reason);
                break;
            case STATE_SHOW_IME_IMPLICIT:
                mService.showCurrentInputLocked(windowToken, statsToken,
                        InputMethodManager.SHOW_IMPLICIT, null, reason);
                break;
            default:
                throw new IllegalArgumentException("Invalid IME visibility state: " + state);
        }
+194 −3
Original line number Diff line number Diff line
@@ -18,11 +18,13 @@ package com.android.server.inputmethod;

import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
import static android.server.inputmethod.InputMethodManagerServiceProto.ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD;
import static android.server.inputmethod.InputMethodManagerServiceProto.INPUT_SHOWN;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_EXPLICITLY_REQUESTED;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FORCED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.SoftInputModeFlags;

@@ -32,6 +34,7 @@ import static com.android.server.inputmethod.InputMethodManagerService.computeIm
import android.accessibilityservice.AccessibilityService;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.res.Configuration;
import android.os.IBinder;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -42,6 +45,7 @@ import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;

import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerInternal;

@@ -88,6 +92,11 @@ public final class ImeVisibilityStateComputer {
     */
    boolean mShowForced;

    /**
     * Set if we last told the input method to show itself.
     */
    private boolean mInputShown;

    /** Represent the invalid IME visibility state */
    public static final int STATE_INVALID = -1;

@@ -105,6 +114,12 @@ public final class ImeVisibilityStateComputer {

    /** State to handle showing an IME preview surface during the app was loosing the IME focus */
    public static final int STATE_SHOW_IME_SNAPSHOT = 4;

    public static final int STATE_HIDE_IME_EXPLICIT = 5;

    public static final int STATE_HIDE_IME_NOT_ALWAYS = 6;

    public static final int STATE_SHOW_IME_IMPLICIT = 7;
    @IntDef({
            STATE_INVALID,
            STATE_HIDE_IME,
@@ -112,6 +127,9 @@ public final class ImeVisibilityStateComputer {
            STATE_SHOW_IME_ABOVE_OVERLAY,
            STATE_SHOW_IME_BEHIND_OVERLAY,
            STATE_SHOW_IME_SNAPSHOT,
            STATE_HIDE_IME_EXPLICIT,
            STATE_HIDE_IME_NOT_ALWAYS,
            STATE_SHOW_IME_IMPLICIT,
    })
    @interface VisibilityState {}

@@ -187,6 +205,7 @@ public final class ImeVisibilityStateComputer {
    void clearImeShowFlags() {
        mRequestedShowExplicitly = false;
        mShowForced = false;
        mInputShown = false;
    }

    int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) {
@@ -215,7 +234,7 @@ public final class ImeVisibilityStateComputer {
    ImeTargetWindowState getOrCreateWindowState(IBinder windowToken) {
        ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
        if (state == null) {
            state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNSPECIFIED, false, false);
            state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNSPECIFIED, 0, false, false, false);
        }
        return state;
    }
@@ -239,6 +258,152 @@ public final class ImeVisibilityStateComputer {
        mRequestWindowStateMap.put(windowToken, newState);
    }

    static class ImeVisibilityResult {
        private final @VisibilityState int mState;
        private final @SoftInputShowHideReason int mReason;

        ImeVisibilityResult(@VisibilityState int state, @SoftInputShowHideReason int reason) {
            mState = state;
            mReason = reason;
        }

        @VisibilityState int getState() {
            return mState;
        }

        @SoftInputShowHideReason int getReason() {
            return mReason;
        }
    }

    ImeVisibilityResult computeState(ImeTargetWindowState state, boolean allowVisible) {
        // TODO: Output the request IME visibility state according to the requested window state
        final int softInputVisibility = state.mSoftInputModeState & SOFT_INPUT_MASK_STATE;
        // Should we auto-show the IME even if the caller has not
        // specified what should be done with it?
        // We only do this automatically if the window can resize
        // to accommodate the IME (so what the user sees will give
        // them good context without input information being obscured
        // by the IME) or if running on a large screen where there
        // is more room for the target window + IME.
        final boolean doAutoShow =
                (state.mSoftInputModeState & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
                        == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                        || mService.mRes.getConfiguration().isLayoutSizeAtLeast(
                        Configuration.SCREENLAYOUT_SIZE_LARGE);
        final boolean isForwardNavigation = (state.mSoftInputModeState
                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;

        // We shows the IME when the system allows the IME focused target window to restore the
        // IME visibility (e.g. switching to the app task when last time the IME is visible).
        // Note that we don't restore IME visibility for some cases (e.g. when the soft input
        // state is ALWAYS_HIDDEN or STATE_HIDDEN with forward navigation).
        // Because the app might leverage these flags to hide soft-keyboard with showing their own
        // UI for input.
        if (state.hasEdiorFocused() && shouldRestoreImeVisibility(state)) {
            if (DEBUG) Slog.v(TAG, "Will show input to restore visibility");
            return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
                    SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
        }

        switch (softInputVisibility) {
            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
                if (state.hasImeFocusChanged() && (!state.hasEdiorFocused() || !doAutoShow)) {
                    if (WindowManager.LayoutParams.mayUseInputMethod(state.getWindowFlags())) {
                        // There is no focus view, and this window will
                        // be behind any soft input window, so hide the
                        // soft input window if it is shown.
                        if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
                        return new ImeVisibilityResult(STATE_HIDE_IME_NOT_ALWAYS,
                                SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
                    }
                } else if (state.hasEdiorFocused() && doAutoShow && isForwardNavigation) {
                    // There is a focus view, and we are navigating forward
                    // into the window, so show the input window for the user.
                    // We only do this automatically if the window can resize
                    // to accommodate the IME (so what the user sees will give
                    // them good context without input information being obscured
                    // by the IME) or if running on a large screen where there
                    // is more room for the target window + IME.
                    if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
                    return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
                            SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
                }
                break;
            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
                // Do nothing.
                break;
            case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
                if (isForwardNavigation) {
                    if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
                    return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
                            SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
                }
                break;
            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
                if (state.hasImeFocusChanged()) {
                    if (DEBUG) Slog.v(TAG, "Window asks to hide input");
                    return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
                            SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
                }
                break;
            case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
                if (isForwardNavigation) {
                    if (allowVisible) {
                        if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
                        return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
                                SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV);
                    } else {
                        Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
                                + " there is no focused view that also returns true from"
                                + " View#onCheckIsTextEditor()");
                    }
                }
                break;
            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
                if (DEBUG) Slog.v(TAG, "Window asks to always show input");
                if (allowVisible) {
                    if (state.hasImeFocusChanged()) {
                        return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
                                SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
                    }
                } else {
                    Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
                            + " there is no focused view that also returns true from"
                            + " View#onCheckIsTextEditor()");
                }
                break;
        }

        if (!state.hasImeFocusChanged()) {
            // On previous platforms, when Dialogs re-gained focus, the Activity behind
            // would briefly gain focus first, and dismiss the IME.
            // On R that behavior has been fixed, but unfortunately apps have come
            // to rely on this behavior to hide the IME when the editor no longer has focus
            // To maintain compatibility, we are now hiding the IME when we don't have
            // an editor upon refocusing a window.
            if (state.isStartInputByGainFocus()) {
                if (DEBUG) Slog.v(TAG, "Same window without editor will hide input");
                return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
                        SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
            }
        }
        if (!state.hasEdiorFocused() && mInputShown && state.isStartInputByGainFocus()
                && mService.mInputMethodDeviceConfigs.shouldHideImeWhenNoEditorFocus()) {
            // Hide the soft-keyboard when the system do nothing for softInputModeState
            // of the window being gained focus without an editor. This behavior benefits
            // to resolve some unexpected IME visible cases while that window with following
            // configurations being switched from an IME shown window:
            // 1) SOFT_INPUT_STATE_UNCHANGED state without an editor
            // 2) SOFT_INPUT_STATE_VISIBLE state without an editor
            // 3) SOFT_INPUT_STATE_ALWAYS_VISIBLE state without an editor
            if (DEBUG) Slog.v(TAG, "Window without editor will hide input");
            return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
                    SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR);
        }
        return null;
    }

    IBinder getWindowTokenFrom(IBinder requestImeToken) {
        for (IBinder windowToken : mRequestWindowStateMap.keySet()) {
            final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
@@ -273,11 +438,20 @@ public final class ImeVisibilityStateComputer {
        return mWindowManagerInternal.shouldRestoreImeVisibility(getWindowTokenFrom(state));
    }

    boolean isInputShown() {
        return mInputShown;
    }

    void setInputShown(boolean inputShown) {
        mInputShown = inputShown;
    }

    void dumpDebug(ProtoOutputStream proto, long fieldId) {
        proto.write(SHOW_EXPLICITLY_REQUESTED, mRequestedShowExplicitly);
        proto.write(SHOW_FORCED, mShowForced);
        proto.write(ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD,
                mPolicy.isA11yRequestNoSoftKeyboard());
        proto.write(INPUT_SHOWN, mInputShown);
    }

    void dump(PrintWriter pw) {
@@ -285,6 +459,7 @@ public final class ImeVisibilityStateComputer {
        p.println(" mRequestedShowExplicitly=" + mRequestedShowExplicitly
                + " mShowForced=" + mShowForced);
        p.println("  mImeHiddenByDisplayPolicy=" + mPolicy.isImeHiddenByDisplayPolicy());
        p.println("  mInputShown=" + mInputShown);
    }

    /**
@@ -335,11 +510,14 @@ public final class ImeVisibilityStateComputer {
     * A class that represents the current state of the IME target window.
     */
    static class ImeTargetWindowState {
        ImeTargetWindowState(@SoftInputModeFlags int softInputModeState, boolean imeFocusChanged,
                boolean hasFocusedEditor) {
        ImeTargetWindowState(@SoftInputModeFlags int softInputModeState, int windowFlags,
                boolean imeFocusChanged, boolean hasFocusedEditor,
                boolean isStartInputByGainFocus) {
            mSoftInputModeState = softInputModeState;
            mWindowFlags = windowFlags;
            mImeFocusChanged = imeFocusChanged;
            mHasFocusedEditor = hasFocusedEditor;
            mIsStartInputByGainFocus = isStartInputByGainFocus;
        }

        /**
@@ -347,6 +525,8 @@ public final class ImeVisibilityStateComputer {
         */
        private final @SoftInputModeFlags int mSoftInputModeState;

        private final int mWindowFlags;

        /**
         * {@code true} means the IME focus changed from the previous window, {@code false}
         * otherwise.
@@ -358,6 +538,8 @@ public final class ImeVisibilityStateComputer {
         */
        private final boolean mHasFocusedEditor;

        private final boolean mIsStartInputByGainFocus;

        /**
         * Set if the client has asked for the input method to be shown.
         */
@@ -382,10 +564,18 @@ public final class ImeVisibilityStateComputer {
            return mHasFocusedEditor;
        }

        boolean isStartInputByGainFocus() {
            return mIsStartInputByGainFocus;
        }

        int getSoftInputModeState() {
            return mSoftInputModeState;
        }

        int getWindowFlags() {
            return mWindowFlags;
        }

        private void setImeDisplayId(int imeDisplayId) {
            mImeDisplayId = imeDisplayId;
        }
@@ -418,6 +608,7 @@ public final class ImeVisibilityStateComputer {
                    + " requestedImeVisible " + mRequestedImeVisible
                    + " imeDisplayId " + mImeDisplayId
                    + " softInputModeState " + softInputModeToString(mSoftInputModeState)
                    + " isStartInputByGainFocus " + mIsStartInputByGainFocus
                    + "}";
        }
    }
+42 −178

File changed.

Preview size limit exceeded, changes collapsed.