Rewrite OnScreenPositionTest#testImeIsNotBehindNavBar()
This CL rewrites
OnScreenPositionTest#testImeIsNotBehindNavBar(),
which we added in Android P development cycle [1].
The main motivation of that test was to prevent issues where IMEs were
placed as if there was no nav bar [2], but how the test was
implemented was based on some assumptions about IME-observable insets.
Now that we have a way to test user-perceivable IME visibility [3],
the test is expected to be more robust and compatible if we rewrite it
with InputMethodVisibilityVerifier.
[1]: I0005aee0259f25c96fb94054535dd945a56e8cfb
6ff19b02ae5aa75f595a0ebd073b641d6edebf89
[2]: Bug 11237795, Bug 26984057, Bug 31313118, Bug 33095565,
Bug 33308065
[3]: If999aa51fd2e5fd20e9e2d72a7e1dbeb75e3456d
d7cb355421143641af4cdda38574337cfae3d24c
Bug: 38298890
Fix: 209695046
Test: atest CtsInputMethodTestCases:OnScreenPositionTest
Change-Id: I83e592855c616784be3b62dd9821cf8dd4566916
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeLayoutInfo.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeLayoutInfo.java
index 40bdc3f..7f38f44 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeLayoutInfo.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeLayoutInfo.java
@@ -21,7 +21,6 @@
import android.os.Bundle;
import android.view.Display;
import android.view.View;
-import android.view.WindowInsets;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -37,8 +36,6 @@
private static final String OLD_LAYOUT_KEY = "oldLayout";
private static final String VIEW_ORIGIN_ON_SCREEN_KEY = "viewOriginOnScreen";
private static final String DISPLAY_SIZE_KEY = "displaySize";
- private static final String SYSTEM_WINDOW_INSET_KEY = "systemWindowInset";
- private static final String STABLE_INSET_KEY = "stableInset";
@NonNull
private final Rect mNewLayout;
@@ -48,10 +45,6 @@
private Point mViewOriginOnScreen;
@Nullable
private Point mDisplaySize;
- @Nullable
- private Rect mSystemWindowInset;
- @Nullable
- private Rect mStableInset;
/**
* Returns the bounding box of the {@link View} passed to
@@ -71,71 +64,12 @@
mViewOriginOnScreen.y + mNewLayout.height());
}
- /**
- * Returns the screen area in screen coordinates that does not overlap with the system
- * window inset, which represents the area of a full-screen window that is partially or
- * fully obscured by the status bar, navigation bar, IME or other system windows.
- *
- * <p>May return {@code null} when this information is not yet ready.</p>
- *
- * @return Region in screen coordinates. {@code null} when it is not available
- *
- * @see WindowInsets#hasSystemWindowInsets()
- * @see WindowInsets#getSystemWindowInsetBottom()
- * @see WindowInsets#getSystemWindowInsetLeft()
- * @see WindowInsets#getSystemWindowInsetRight()
- * @see WindowInsets#getSystemWindowInsetTop()
- */
- @Nullable
- public Rect getScreenRectWithoutSystemWindowInset() {
- if (mDisplaySize == null) {
- return null;
- }
- if (mSystemWindowInset == null) {
- return new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
- }
- return new Rect(mSystemWindowInset.left, mSystemWindowInset.top,
- mDisplaySize.x - mSystemWindowInset.right,
- mDisplaySize.y - mSystemWindowInset.bottom);
- }
-
- /**
- * Returns the screen area in screen coordinates that does not overlap with the stable
- * inset, which represents the area of a full-screen window that <b>may</b> be partially or
- * fully obscured by the system UI elements.
- *
- * <p>May return {@code null} when this information is not yet ready.</p>
- *
- * @return Region in screen coordinates. {@code null} when it is not available
- *
- * @see WindowInsets#hasStableInsets()
- * @see WindowInsets#getStableInsetBottom()
- * @see WindowInsets#getStableInsetLeft()
- * @see WindowInsets#getStableInsetRight()
- * @see WindowInsets#getStableInsetTop()
- */
- @Nullable
- public Rect getScreenRectWithoutStableInset() {
- if (mDisplaySize == null) {
- return null;
- }
- if (mStableInset == null) {
- return new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
- }
- return new Rect(mStableInset.left, mStableInset.top,
- mDisplaySize.x - mStableInset.right,
- mDisplaySize.y - mStableInset.bottom);
- }
-
ImeLayoutInfo(@NonNull Rect newLayout, @NonNull Rect oldLayout,
- @NonNull Point viewOriginOnScreen, @Nullable Point displaySize,
- @Nullable Rect systemWindowInset, @Nullable Rect stableInset) {
+ @NonNull Point viewOriginOnScreen, @Nullable Point displaySize) {
mNewLayout = new Rect(newLayout);
mOldLayout = new Rect(oldLayout);
mViewOriginOnScreen = new Point(viewOriginOnScreen);
mDisplaySize = new Point(displaySize);
- mSystemWindowInset = systemWindowInset;
- mStableInset = stableInset;
}
void writeToBundle(@NonNull Bundle bundle) {
@@ -143,8 +77,6 @@
bundle.putParcelable(OLD_LAYOUT_KEY, mOldLayout);
bundle.putParcelable(VIEW_ORIGIN_ON_SCREEN_KEY, mViewOriginOnScreen);
bundle.putParcelable(DISPLAY_SIZE_KEY, mDisplaySize);
- bundle.putParcelable(SYSTEM_WINDOW_INSET_KEY, mSystemWindowInset);
- bundle.putParcelable(STABLE_INSET_KEY, mStableInset);
}
static ImeLayoutInfo readFromBundle(@NonNull Bundle bundle) {
@@ -152,11 +84,8 @@
final Rect oldLayout = bundle.getParcelable(OLD_LAYOUT_KEY);
final Point viewOrigin = bundle.getParcelable(VIEW_ORIGIN_ON_SCREEN_KEY);
final Point displaySize = bundle.getParcelable(DISPLAY_SIZE_KEY);
- final Rect systemWindowInset = bundle.getParcelable(SYSTEM_WINDOW_INSET_KEY);
- final Rect stableInset = bundle.getParcelable(STABLE_INSET_KEY);
- return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize, systemWindowInset,
- stableInset);
+ return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize);
}
static ImeLayoutInfo fromLayoutListenerCallback(View v, int left, int top, int right,
@@ -174,25 +103,6 @@
} else {
displaySize = null;
}
- final WindowInsets windowInsets = v.getRootWindowInsets();
- final Rect systemWindowInset;
- if (windowInsets != null && windowInsets.hasSystemWindowInsets()) {
- systemWindowInset = new Rect(
- windowInsets.getSystemWindowInsetLeft(), windowInsets.getSystemWindowInsetTop(),
- windowInsets.getSystemWindowInsetRight(),
- windowInsets.getSystemWindowInsetBottom());
- } else {
- systemWindowInset = null;
- }
- final Rect stableInset;
- if (windowInsets != null && windowInsets.hasStableInsets()) {
- stableInset = new Rect(
- windowInsets.getStableInsetLeft(), windowInsets.getStableInsetTop(),
- windowInsets.getStableInsetRight(), windowInsets.getStableInsetBottom());
- } else {
- stableInset = null;
- }
- return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize, systemWindowInset,
- stableInset);
+ return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize);
}
}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
index d2a8743..be709b4 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
@@ -52,6 +52,7 @@
private static final String FULLSCREEN_MODE_POLICY = "FullscreenModePolicy";
private static final String INPUT_VIEW_SYSTEM_UI_VISIBILITY = "InputViewSystemUiVisibility";
private static final String WATERMARK_ENABLED = "WatermarkEnabled";
+ private static final String WATERMARK_GRAVITY = "WatermarkGravity";
private static final String HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED =
"HardKeyboardConfigurationBehaviorAllowed";
private static final String INLINE_SUGGESTIONS_ENABLED = "InlineSuggestionsEnabled";
@@ -156,6 +157,10 @@
return mBundle.getBoolean(WATERMARK_ENABLED, defaultValue);
}
+ public int getWatermarkGravity(int defaultValue) {
+ return mBundle.getInt(WATERMARK_GRAVITY, defaultValue);
+ }
+
public boolean getHardKeyboardConfigurationBehaviorAllowed(boolean defaultValue) {
return mBundle.getBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, defaultValue);
}
@@ -285,6 +290,18 @@
}
/**
+ * Sets the {@link android.view.Gravity} flags for the watermark image.
+ *
+ * <p>{@link android.view.Gravity#CENTER} will be used if not set.</p>
+ *
+ * @param gravity {@code true} {@link android.view.Gravity} flags to be set.
+ */
+ public Builder setWatermarkGravity(int gravity) {
+ mBundle.putInt(WATERMARK_GRAVITY, gravity);
+ return this;
+ }
+
+ /**
* Controls whether {@link MockIme} is allowed to change the behavior based on
* {@link android.content.res.Configuration#keyboard} and
* {@link android.content.res.Configuration#hardKeyboardHidden}.
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 99d2ea7..c173766 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -761,7 +761,7 @@
imageView.setImageBitmap(bitmap);
secondaryLayout.addView(imageView,
new FrameLayout.LayoutParams(bitmap.getWidth(), bitmap.getHeight(),
- Gravity.CENTER));
+ mSettings.getWatermarkGravity(Gravity.CENTER)));
}
mLayout.addView(secondaryLayout);
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java
index 92594d3..614851d 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java
@@ -16,18 +16,14 @@
package android.view.inputmethod.cts;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
+import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeVisible;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.waitForInputViewLayoutStable;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.Rect;
-import android.os.Process;
-import android.text.TextUtils;
-import android.view.inputmethod.EditorInfo;
+import android.os.SystemClock;
+import android.view.Gravity;
+import android.view.WindowManager;
import android.view.inputmethod.cts.util.EndToEndImeTestBase;
import android.view.inputmethod.cts.util.TestActivity;
import android.view.inputmethod.cts.util.UnlockScreenRule;
@@ -38,9 +34,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.CtsTouchUtils;
import com.android.cts.mockime.ImeEventStream;
-import com.android.cts.mockime.ImeLayoutInfo;
import com.android.cts.mockime.ImeSettings;
import com.android.cts.mockime.MockImeSession;
@@ -49,41 +43,27 @@
import org.junit.runner.RunWith;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class OnScreenPositionTest extends EndToEndImeTestBase {
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
- private static final long LAYOUT_STABLE_THRESHOLD = TimeUnit.SECONDS.toMillis(3);
- private static final String TEST_MARKER = "android.view.inputmethod.cts.OnScreenPositionTest";
+ private static final String TEST_MARKER_PREFIX =
+ "android.view.inputmethod.cts.OnScreenPositionTest";
+
+ private static String getTestMarker() {
+ return TEST_MARKER_PREFIX + "/" + SystemClock.elapsedRealtimeNanos();
+ }
@Rule
public final UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
- public EditText launchTestActivity() {
- final AtomicReference<EditText> editTextRef = new AtomicReference<>();
- TestActivity.startSync(activity -> {
- final LinearLayout layout = new LinearLayout(activity);
- layout.setOrientation(LinearLayout.VERTICAL);
-
- final EditText editText = new EditText(activity);
- editText.setPrivateImeOptions(TEST_MARKER);
- editText.setHint("editText");
- editText.requestFocus();
- editTextRef.set(editText);
-
- layout.addView(editText);
- return layout;
- });
- return editTextRef.get();
- }
-
- private static final int EXPECTED_KEYBOARD_HEIGHT = 100;
-
/**
* Regression test for Bug 33308065.
+ *
+ * <p>Verify that {@link com.android.cts.mockime.Watermark} is visible even if it's placed
+ * at the bottom of the IME content area.</p>
*/
@Test
public void testImeIsNotBehindNavBar() throws Exception {
@@ -91,51 +71,29 @@
InstrumentationRegistry.getInstrumentation().getContext(),
InstrumentationRegistry.getInstrumentation().getUiAutomation(),
new ImeSettings.Builder()
- .setInputViewHeight(EXPECTED_KEYBOARD_HEIGHT))) {
+ .setWatermarkGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM))) {
final ImeEventStream stream = imeSession.openEventStream();
- final EditText editText = launchTestActivity();
+ final String marker = getTestMarker();
+ TestActivity.startSync(activity -> {
+ activity.getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
- // Wait until the MockIme gets bound to the TestActivity.
- expectBindInput(stream, Process.myPid(), TIMEOUT);
+ final LinearLayout layout = new LinearLayout(activity);
+ layout.setOrientation(LinearLayout.VERTICAL);
- // Emulate tap event
- CtsTouchUtils.emulateTapOnViewCenter(
- InstrumentationRegistry.getInstrumentation(), null, editText);
+ final EditText editText = new EditText(activity);
+ editText.setPrivateImeOptions(marker);
+ editText.setHint("editText");
+ editText.requestFocus();
- // Wait until "onStartInput" gets called for the EditText.
- expectEvent(stream, event -> {
- if (!TextUtils.equals("onStartInputView", event.getEventName())) {
- return false;
- }
- final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
- return TextUtils.equals(TEST_MARKER, editorInfo.privateImeOptions);
- }, TIMEOUT);
+ layout.addView(editText);
+ return layout;
+ });
- // Wait until MockIme's layout becomes stable.
- final ImeLayoutInfo lastLayout =
- waitForInputViewLayoutStable(stream, LAYOUT_STABLE_THRESHOLD);
- assertNotNull(lastLayout);
+ expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
- // We consider that the screenRectWithoutNavBar is a union of those two rects.
- // See the following methods for details.
- // - DecorView#getColorViewTopInset(int, int)
- // - DecorView#getColorViewBottomInset(int, int)
- // - DecorView#getColorViewRightInset(int, int)
- // - DecorView#getColorViewLeftInset(int, int)
- final Rect screenRectWithoutNavBar = lastLayout.getScreenRectWithoutStableInset();
- screenRectWithoutNavBar.union(lastLayout.getScreenRectWithoutSystemWindowInset());
-
- final Rect keyboardViewBounds = lastLayout.getInputViewBoundsInScreen();
- // By default, the above region must contain the keyboard view region.
- assertTrue("screenRectWithoutNavBar(" + screenRectWithoutNavBar + ") must"
- + " contain keyboardViewBounds(" + keyboardViewBounds + ")",
- screenRectWithoutNavBar.contains(keyboardViewBounds));
-
- // Make sure that the keyboard height is expected. Here we assume that the expected
- // height is small enough for all the Android-based devices to show.
- assertEquals(EXPECTED_KEYBOARD_HEIGHT,
- lastLayout.getInputViewBoundsInScreen().height());
+ expectImeVisible(TIMEOUT);
}
}
}