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);
         }
     }
 }