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

Commit d0091597 authored by Lan Wei's avatar Lan Wei
Browse files

Extend AutoShowTest to test more softInputMode flags

Extend AutoShowTest to test the behavior of combining various
softInputMode flags:
  * SOFT_INPUT_STATE_UNSPECIFIED
  * SOFT_INPUT_STATE_HIDDEN
  * SOFT_INPUT_STATE_ALWAYS_HIDDEN
  * SOFT_INPUT_STATE_VISIBLE
  * SOFT_INPUT_STATE_ALWAYS_VISIBLE

softInput adjustment flags:
  * SOFT_INPUT_ADJUST_UNSPECIFIED
  * SOFT_INPUT_ADJUST_RESIZE
  * SOFT_INPUT_ADJUST_PAN
  * SOFT_INPUT_ADJUST_NOTHING

set or not set:
  * SOFT_INPUT_IS_FORWARD_NAVIGATION

Flag SOFT_INPUT_STATE_UNCHANGED was not included for now as it requires
more than one activities.

Bug: 242838873
Bug: 77152727
Test: atest InputMethodStressTest
Change-Id: I21ef8073d0b6a582c108141000e23847be6daf33
parent 9d8f4ade
Loading
Loading
Loading
Loading
+98 −15
Original line number Diff line number Diff line
@@ -18,9 +18,8 @@ package com.android.inputmethod.stresstest;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;

import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;

@@ -30,19 +29,27 @@ import android.content.Intent;
import android.os.Bundle;
import android.platform.test.annotations.RootPermissionTest;
import android.platform.test.rule.UnlockScreenRule;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.ArrayList;
import java.util.List;

/**
 * Tests to verify the "auto show" behavior in {@code InputMethodManagerService} when the window
 * gaining the focus to start the input.
 */
@RootPermissionTest
@RunWith(AndroidJUnit4.class)
@RunWith(Parameterized.class)
public final class AutoShowTest {

    @Rule
@@ -52,19 +59,96 @@ public final class AutoShowTest {
    public ScreenCaptureRule mScreenCaptureRule =
            new ScreenCaptureRule("/sdcard/InputMethodStressTest");

    private static final int[] SOFT_INPUT_VISIBILITY_FLAGS =
            new int[] {
                WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,
                WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN,
                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN,
                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE,
                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE,
            };

    private static final int[] SOFT_INPUT_ADJUST_FLAGS =
            new int[] {
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED,
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN,
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
            };

    // TODO(b/240359838): add test case {@code Configuration.SCREENLAYOUT_SIZE_LARGE}.
    @Parameterized.Parameters(
            name =
                    "softInputVisibility={0}, softInputAdjustment={1},"
                            + " softInputModeIsForwardNavigation={2}")
    public static List<Object[]> softInputModeConfigs() {
        ArrayList<Object[]> params = new ArrayList<>();
        for (int softInputVisibility : SOFT_INPUT_VISIBILITY_FLAGS) {
            for (int softInputAdjust : SOFT_INPUT_ADJUST_FLAGS) {
                params.add(new Object[] {softInputVisibility, softInputAdjust, true});
                params.add(new Object[] {softInputVisibility, softInputAdjust, false});
            }
        }
        return params;
    }

    private static final String SOFT_INPUT_FLAGS = "soft_input_flags";

    private final int mSoftInputVisibility;
    private final int mSoftInputAdjustment;
    private final boolean mSoftInputIsForwardNavigation;

    public AutoShowTest(
            int softInputVisibility,
            int softInputAdjustment,
            boolean softInputIsForwardNavigation) {
        mSoftInputVisibility = softInputVisibility;
        mSoftInputAdjustment = softInputAdjustment;
        mSoftInputIsForwardNavigation = softInputIsForwardNavigation;
    }

    @Test
    public void autoShow() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent = new Intent()
        int flags = mSoftInputVisibility | mSoftInputAdjustment;
        if (mSoftInputIsForwardNavigation) {
            flags |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
        }

        Intent intent =
                new Intent()
                        .setAction(Intent.ACTION_MAIN)
                        .setClass(instrumentation.getContext(), TestActivity.class)
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
                        .putExtra(SOFT_INPUT_FLAGS, flags);
        TestActivity activity = (TestActivity) instrumentation.startActivitySync(intent);
        EditText editText = activity.getEditText();
        waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);

        if (mSoftInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
                || mSoftInputVisibility
                        == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) {
            // IME will be auto-shown if softInputMode is set with flag:
            // SOFT_INPUT_STATE_VISIBLE or SOFT_INPUT_STATE_ALWAYS_VISIBLE
            waitOnMainUntilImeIsShown(editText);
        } else if (mSoftInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
                || mSoftInputVisibility
                        == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
            // IME will be not be shown if softInputMode is set with flag:
            // SOFT_INPUT_STATE_HIDDEN or SOFT_INPUT_STATE_ALWAYS_HIDDEN
            verifyImeIsAlwaysHidden(editText);
        } else {
            // The current system behavior will choose to show IME automatically when navigating
            // forward to an app that has no visibility state specified  (i.e.
            // SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE flag.
            if (mSoftInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
                    && mSoftInputAdjustment == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                    && mSoftInputIsForwardNavigation) {
                waitOnMainUntilImeIsShown(editText);
            }
        }
    }

    public static class TestActivity extends Activity {
        private EditText mEditText;
@@ -72,16 +156,15 @@ public final class AutoShowTest {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // IME will be auto-shown if the following conditions are met:
            // 1. SoftInputMode state is SOFT_INPUT_STATE_UNSPECIFIED.
            // 2. SoftInputMode adjust is SOFT_INPUT_ADJUST_RESIZE.
            getWindow().setSoftInputMode(SOFT_INPUT_STATE_UNSPECIFIED | SOFT_INPUT_ADJUST_RESIZE);
            int flags = getIntent().getIntExtra(SOFT_INPUT_FLAGS, 0);
            getWindow().setSoftInputMode(flags);
            LinearLayout rootView = new LinearLayout(this);
            rootView.setOrientation(LinearLayout.VERTICAL);
            mEditText = new EditText(this);
            rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
            setContentView(rootView);
            // 3. The focused view is a text editor (View#onCheckIsTextEditor() returns true).
            // Ensure the focused view is a text editor (View#onCheckIsTextEditor() returns true) to
            // automatically display a soft input window.
            mEditText.requestFocus();
        }

+40 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import android.view.WindowInsets;

import androidx.test.platform.app.InstrumentationRegistry;

import com.android.compatibility.common.util.ThrowingRunnable;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -33,6 +35,7 @@ import java.util.concurrent.atomic.AtomicReference;
public final class ImeStressTestUtil {

    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
    private static final long VERIFY_DURATION = TimeUnit.SECONDS.toMillis(2);

    private ImeStressTestUtil() {
    }
@@ -77,4 +80,41 @@ public final class ImeStressTestUtil {
        eventually(() -> assertWithMessage("IME should be hidden").that(
                callOnMainSync(() -> isImeShown(view))).isFalse(), TIMEOUT);
    }

    /** Verify IME is always hidden within the given time duration. */
    public static void verifyImeIsAlwaysHidden(View view) {
        always(
                () ->
                        assertWithMessage("IME should be hidden")
                                .that(callOnMainSync(() -> isImeShown(view)))
                                .isFalse(),
                VERIFY_DURATION);
    }

    /**
     * Make sure that a {@link Runnable} always finishes without throwing a {@link Exception} in the
     * given duration
     *
     * @param r The {@link Runnable} to run.
     * @param timeoutMillis The number of milliseconds to wait for {@code r} to not throw
     */
    public static void always(ThrowingRunnable r, long timeoutMillis) {
        long start = System.currentTimeMillis();

        while (true) {
            try {
                r.run();
                if (System.currentTimeMillis() - start >= timeoutMillis) {
                    return;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ignored) {
                    // Do nothing
                }
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
    }
}