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

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

Add unit tests for ImeVisibilityStateComputer

Bug: 246309664
Test: atest ImeVisibilityStateComputerTest
Change-Id: I1a0757ad743a62328e7f7dacf6446274b47fb27f
parent 56eba23f
Loading
Loading
Loading
Loading
+32 −4
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethod;
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.WindowManagerInternal;
@@ -138,11 +139,38 @@ public final class ImeVisibilityStateComputer {
     */
    private final ImeVisibilityPolicy mPolicy;

    public ImeVisibilityStateComputer(InputMethodManagerService service) {
    public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service) {
        this(service,
                LocalServices.getService(WindowManagerInternal.class),
                LocalServices.getService(WindowManagerInternal.class)::getDisplayImePolicy,
                new ImeVisibilityPolicy());
    }

    @VisibleForTesting
    public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service,
            @NonNull Injector injector) {
        this(service, injector.getWmService(), injector.getImeValidator(),
                new ImeVisibilityPolicy());
    }

    interface Injector {
        default WindowManagerInternal getWmService() {
            return null;
        }

        default InputMethodManagerService.ImeDisplayValidator getImeValidator() {
            return null;
        }
    }

    private ImeVisibilityStateComputer(InputMethodManagerService service,
            WindowManagerInternal wmService,
            InputMethodManagerService.ImeDisplayValidator imeDisplayValidator,
            ImeVisibilityPolicy imePolicy) {
        mService = service;
        mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
        mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy;
        mPolicy = new ImeVisibilityPolicy();
        mWindowManagerInternal = wmService;
        mImeDisplayValidator = imeDisplayValidator;
        mPolicy = imePolicy;
    }

    /**
+11 −1
Original line number Diff line number Diff line
@@ -683,7 +683,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
     * The display ID of the input method indicates the fallback display which returned by
     * {@link #computeImeDisplayIdForTarget}.
     */
    private static final int FALLBACK_DISPLAY_ID = DEFAULT_DISPLAY;
    static final int FALLBACK_DISPLAY_ID = DEFAULT_DISPLAY;

    /**
     * If non-null, this is the input method service we are currently connected
@@ -4647,6 +4647,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }

    @VisibleForTesting
    ImeVisibilityStateComputer getVisibilityStateComputer() {
        return mVisibilityStateComputer;
    }

    @VisibleForTesting
    ImeVisibilityApplier getVisibilityApplier() {
        return mVisibilityApplier;
    }

    @GuardedBy("ImfLock.class")
    void setEnabledSessionLocked(SessionState session) {
        if (mEnabledSession != session) {
+185 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.inputmethod;

import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
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.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
import static com.android.server.inputmethod.InputMethodManagerService.FALLBACK_DISPLAY_ID;
import static com.android.server.inputmethod.InputMethodManagerService.ImeDisplayValidator;

import static com.google.common.truth.Truth.assertThat;

import android.os.IBinder;
import android.os.RemoteException;
import android.view.inputmethod.InputMethodManager;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.android.server.wm.WindowManagerInternal;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when
 * requesting the IME visibility.
 *
 * Build/Install/Run:
 * atest FrameworksInputMethodSystemServerTests:ImeVisibilityStateComputerTest
 */
@RunWith(AndroidJUnit4.class)
public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTestBase {
    private ImeVisibilityStateComputer mComputer;
    private int mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL;

    @Before
    public void setUp() throws RemoteException {
        super.setUp();
        ImeVisibilityStateComputer.Injector injector = new ImeVisibilityStateComputer.Injector() {
            @Override
            public WindowManagerInternal getWmService() {
                return mMockWindowManagerInternal;
            }

            @Override
            public ImeDisplayValidator getImeValidator() {
                return displayId -> mImeDisplayPolicy;
            }
        };
        mComputer = new ImeVisibilityStateComputer(mInputMethodManagerService, injector);
    }

    @Test
    public void testRequestImeVisibility_showImplicit() {
        initImeTargetWindowState(mWindowToken);
        boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
        mComputer.requestImeVisibility(mWindowToken, res);

        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
        assertThat(state).isNotNull();
        assertThat(state.hasEdiorFocused()).isTrue();
        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
        assertThat(state.isRequestedImeVisible()).isTrue();

        assertThat(mComputer.mRequestedShowExplicitly).isFalse();
    }

    @Test
    public void testRequestImeVisibility_showExplicit() {
        initImeTargetWindowState(mWindowToken);
        boolean res = mComputer.onImeShowFlags(null, 0 /* show explicit */);
        mComputer.requestImeVisibility(mWindowToken, res);

        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
        assertThat(state).isNotNull();
        assertThat(state.hasEdiorFocused()).isTrue();
        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
        assertThat(state.isRequestedImeVisible()).isTrue();

        assertThat(mComputer.mRequestedShowExplicitly).isTrue();
    }

    @Test
    public void testRequestImeVisibility_showImplicit_a11yNoImePolicy() {
        // Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy
        mComputer.getImePolicy().setA11yRequestNoSoftKeyboard(SHOW_MODE_HIDDEN);

        initImeTargetWindowState(mWindowToken);
        boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
        mComputer.requestImeVisibility(mWindowToken, res);

        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
        assertThat(state).isNotNull();
        assertThat(state.hasEdiorFocused()).isTrue();
        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
        assertThat(state.isRequestedImeVisible()).isFalse();

        assertThat(mComputer.mRequestedShowExplicitly).isFalse();
    }

    @Test
    public void testRequestImeVisibility_showImplicit_imeHiddenPolicy() {
        // Precondition: set IME hidden display policy before calling showSoftInput
        mComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);

        initImeTargetWindowState(mWindowToken);
        boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
        mComputer.requestImeVisibility(mWindowToken, res);

        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
        assertThat(state).isNotNull();
        assertThat(state.hasEdiorFocused()).isTrue();
        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
        assertThat(state.isRequestedImeVisible()).isFalse();

        assertThat(mComputer.mRequestedShowExplicitly).isFalse();
    }

    @Test
    public void testRequestImeVisibility_hideNotAlways() {
        // Precondition: ensure IME has shown before hiding request.
        mComputer.setInputShown(true);

        initImeTargetWindowState(mWindowToken);
        assertThat(mComputer.canHideIme(null, InputMethodManager.HIDE_NOT_ALWAYS)).isTrue();
        mComputer.requestImeVisibility(mWindowToken, false);

        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
        assertThat(state).isNotNull();
        assertThat(state.hasEdiorFocused()).isTrue();
        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
        assertThat(state.isRequestedImeVisible()).isFalse();
    }

    @Test
    public void testComputeImeDisplayId() {
        final ImeTargetWindowState state = mComputer.getOrCreateWindowState(mWindowToken);

        mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL;
        mComputer.computeImeDisplayId(state, DEFAULT_DISPLAY);
        assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
        assertThat(state.getImeDisplayId()).isEqualTo(DEFAULT_DISPLAY);

        mComputer.computeImeDisplayId(state, 10 /* displayId */);
        assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
        assertThat(state.getImeDisplayId()).isEqualTo(10);

        mImeDisplayPolicy = DISPLAY_IME_POLICY_HIDE;
        mComputer.computeImeDisplayId(state, 10 /* displayId */);
        assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isTrue();
        assertThat(state.getImeDisplayId()).isEqualTo(INVALID_DISPLAY);

        mImeDisplayPolicy = DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
        mComputer.computeImeDisplayId(state, 10 /* displayId */);
        assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
        assertThat(state.getImeDisplayId()).isEqualTo(FALLBACK_DISPLAY_ID);
    }

    private void initImeTargetWindowState(IBinder windowToken) {
        final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
                0, true, true, true);
        mComputer.setWindowState(windowToken, state);
    }
}
+2 −3
Original line number Diff line number Diff line
@@ -200,9 +200,8 @@ public class InputMethodManagerServiceTestBase {
                        "TestServiceThread",
                        Process.THREAD_PRIORITY_FOREGROUND, /* allowIo */
                        false);
        mInputMethodManagerService =
                new InputMethodManagerService(
                        mContext, mServiceThread, mMockInputMethodBindingController);
        mInputMethodManagerService = new InputMethodManagerService(mContext, mServiceThread,
                mMockInputMethodBindingController);

        // Start a InputMethodManagerService.Lifecycle to publish and manage the lifecycle of
        // InputMethodManagerService, which is closer to the real situation.