Loading tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java +98 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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(); } Loading tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java +40 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() { } Loading Loading @@ -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); } } } } Loading
tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java +98 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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(); } Loading
tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java +40 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() { } Loading Loading @@ -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); } } } }