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

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

ImeVisibilityStateComputer: show IME snapshot when the screen is off

Handle IME visibility when interactive changed before finishing the
input to ensure we preserve the last state as possible.

Bug: 258048231
Bug: 260952459
Test: atest ImeVisibilityStateComputerTest#testOnInteractiveChanged
Test: manual as issue steps:
    1. Show keyboard
    2. Select sticker icon on the keyboard
    3. screen off - screen on - swipe
    4. check ime : Nolank screen shows after screen off/on
Change-Id: I71da97a96f221d2dc9836ab3ecb55a359a6d2f38
parent 99038488
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -25,8 +25,10 @@ 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_REMOVE_IME_SNAPSHOT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -170,6 +172,12 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
                mService.showCurrentInputLocked(windowToken, statsToken,
                        InputMethodManager.SHOW_IMPLICIT, null, reason);
                break;
            case STATE_SHOW_IME_SNAPSHOT:
                showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked(), null);
                break;
            case STATE_REMOVE_IME_SNAPSHOT:
                removeImeScreenshot(mService.getDisplayIdToShowImeLocked());
                break;
            default:
                throw new IllegalArgumentException("Invalid IME visibility state: " + state);
        }
+28 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFI
import static android.view.WindowManager.LayoutParams.SoftInputModeFlags;

import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString;
import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget;

import android.accessibilityservice.AccessibilityService;
@@ -100,6 +102,12 @@ public final class ImeVisibilityStateComputer {
     */
    private boolean mInputShown;

    /**
     * Set if we called
     * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}.
     */
    private boolean mRequestedImeScreenshot;

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

@@ -123,6 +131,10 @@ public final class ImeVisibilityStateComputer {
    public static final int STATE_HIDE_IME_NOT_ALWAYS = 6;

    public static final int STATE_SHOW_IME_IMPLICIT = 7;

    /** State to handle removing an IME preview surface when necessary. */
    public static final int STATE_REMOVE_IME_SNAPSHOT = 8;

    @IntDef({
            STATE_INVALID,
            STATE_HIDE_IME,
@@ -133,6 +145,7 @@ public final class ImeVisibilityStateComputer {
            STATE_HIDE_IME_EXPLICIT,
            STATE_HIDE_IME_NOT_ALWAYS,
            STATE_SHOW_IME_IMPLICIT,
            STATE_REMOVE_IME_SNAPSHOT,
    })
    @interface VisibilityState {}

@@ -467,6 +480,21 @@ public final class ImeVisibilityStateComputer {
        return null;
    }

    @VisibleForTesting
    ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) {
        final ImeTargetWindowState state = getWindowStateOrNull(windowToken);
        if (state != null && state.isRequestedImeVisible() && mInputShown && !interactive) {
            mRequestedImeScreenshot = true;
            return new ImeVisibilityResult(STATE_SHOW_IME_SNAPSHOT, SHOW_IME_SCREENSHOT_FROM_IMMS);
        }
        if (interactive && mRequestedImeScreenshot) {
            mRequestedImeScreenshot = false;
            return new ImeVisibilityResult(STATE_REMOVE_IME_SNAPSHOT,
                    REMOVE_IME_SCREENSHOT_FROM_IMMS);
        }
        return null;
    }

    IBinder getWindowTokenFrom(IBinder requestImeToken) {
        for (IBinder windowToken : mRequestWindowStateMap.keySet()) {
            final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
+8 −0
Original line number Diff line number Diff line
@@ -5049,6 +5049,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                return;
            }
            if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(getCurMethodUidLocked())) {
                // Handle IME visibility when interactive changed before finishing the input to
                // ensure we preserve the last state as possible.
                final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
                        mCurFocusedWindow, interactive);
                if (imeVisRes != null) {
                    mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null,
                            imeVisRes.getState(), imeVisRes.getReason());
                }
                // Eligible IME processes use new "setInteractive" protocol.
                mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode);
            } else {
+29 −0
Original line number Diff line number Diff line
@@ -24,7 +24,11 @@ import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;

import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT;
import static com.android.server.inputmethod.InputMethodManagerService.FALLBACK_DISPLAY_ID;
import static com.android.server.inputmethod.InputMethodManagerService.ImeDisplayValidator;

@@ -196,6 +200,31 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
                lastState.isRequestedImeVisible());
    }

    @Test
    public void testOnInteractiveChanged() {
        mComputer.getOrCreateWindowState(mWindowToken);
        // Precondition: ensure IME has shown before hiding request.
        mComputer.requestImeVisibility(mWindowToken, true);
        mComputer.setInputShown(true);

        // No need any visibility change When initially shows IME on the device was interactive.
        ImeVisibilityStateComputer.ImeVisibilityResult result = mComputer.onInteractiveChanged(
                mWindowToken, true /* interactive */);
        assertThat(result).isNull();

        // Show the IME screenshot to capture the last IME visible state when the device inactive.
        result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */);
        assertThat(result).isNotNull();
        assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT);
        assertThat(result.getReason()).isEqualTo(SHOW_IME_SCREENSHOT_FROM_IMMS);

        // Remove the IME screenshot when the device became interactive again.
        result = mComputer.onInteractiveChanged(mWindowToken, true /* interactive */);
        assertThat(result).isNotNull();
        assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT);
        assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS);
    }

    private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) {
        final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
                0, true, true, true);