Add a End-to-End test for Bug 166619182

To verify IME insets visiblity when switching to another activity with popup
dialog somehow IME insets control lost after focused back to original
activity.

Fix: 167936894
Test: atest KeyboardVisiblityControlTest#\
        testImeVisibilityWhenImeTransitionBetweenActivities_Full
Test: atest --instant KeyboardVisiblityControlTest#\
        testImeVisibilityWhenImeTransitionBetweenActivities_Instant

Change-Id: I4194c38cbf119ddb8af4670ed25ca911818bac5f
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
index ca24424..3d72a4d 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
@@ -16,6 +16,7 @@
 
 package android.view.inputmethod.cts;
 
+import static android.content.Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS;
 import static android.view.View.VISIBLE;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -24,6 +25,8 @@
 import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
 import static android.view.inputmethod.cts.util.TestUtils.runOnMainSync;
 
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEventWithKeyValue;
 import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
@@ -34,11 +37,17 @@
 
 import android.app.AlertDialog;
 import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Intent;
 import android.graphics.Color;
+import android.net.Uri;
 import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import android.support.test.uiautomator.UiObject2;
 import android.text.TextUtils;
 import android.util.Pair;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.WindowInsetsController;
@@ -55,9 +64,14 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.filters.MediumTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
 
 import com.android.cts.mockime.ImeEvent;
 import com.android.cts.mockime.ImeEventStream;
@@ -78,6 +92,18 @@
     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
     private static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(1);
 
+    private static final ComponentName TEST_ACTIVITY = new ComponentName(
+            "android.view.inputmethod.ctstestapp",
+            "android.view.inputmethod.ctstestapp.MainActivity");
+    private static final Uri TEST_ACTIVITY_URI =
+            Uri.parse("https://example.com/android/view/inputmethod/ctstestapp");
+    private static final String EXTRA_KEY_SHOW_DIALOG =
+            "android.view.inputmethod.ctstestapp.EXTRA_KEY_SHOW_DIALOG";
+
+    private static final String ACTION_TRIGGER = "broadcast_action_trigger";
+    private static final String EXTRA_DISMISS_DIALOG = "extra_dismiss_dialog";
+    private static final int NEW_KEYBOARD_HEIGHT = 400;
+
     @Rule
     public final UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
 
@@ -475,6 +501,118 @@
         }
     }
 
+    @AppModeFull
+    @Test
+    public void testImeVisibilityWhenImeTransitionBetweenActivities_Full() throws Exception {
+        runImeVisibilityWhenImeTransitionBetweenActivities(false /* instant */);
+    }
+
+    @AppModeInstant
+    @Test
+    public void testImeVisibilityWhenImeTransitionBetweenActivities_Instant() throws Exception {
+        runImeVisibilityWhenImeTransitionBetweenActivities(true /* instant */);
+    }
+
+    private void runImeVisibilityWhenImeTransitionBetweenActivities(boolean instant)
+            throws Exception {
+        try (MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getInstrumentation().getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder()
+                        .setInputViewHeight(NEW_KEYBOARD_HEIGHT)
+                        .setDrawsBehindNavBar(true))) {
+            final ImeEventStream stream = imeSession.openEventStream();
+            final String marker = getTestMarker();
+
+            AtomicReference<EditText> editTextRef = new AtomicReference<>();
+            // Launch test activity with focusing editor
+            final TestActivity testActivity =
+                    TestActivity.startSync(activity -> {
+                        final LinearLayout layout = new LinearLayout(activity);
+                        layout.setOrientation(LinearLayout.VERTICAL);
+                        layout.setGravity(Gravity.BOTTOM);
+                        final EditText editText = new EditText(activity);
+                        editTextRef.set(editText);
+                        editText.setHint("focused editText");
+                        editText.setPrivateImeOptions(marker);
+                        editText.requestFocus();
+                        layout.addView(editText);
+                        activity.getWindow().getDecorView().setFitsSystemWindows(true);
+                        activity.getWindow().getDecorView().getWindowInsetsController().show(ime());
+                        return layout;
+                    });
+            expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+            expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+            expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+            expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible",
+                    View.VISIBLE, TIMEOUT);
+            expectImeVisible(TIMEOUT);
+
+            // Launcher another test activity from another process with popup dialog.
+            launchRemoteDialogActivitySync(TEST_ACTIVITY, instant, TIMEOUT);
+            // Dismiss dialog and back to original test activity
+            triggerActionWithBroadcast(ACTION_TRIGGER, TEST_ACTIVITY.getPackageName(),
+                    EXTRA_DISMISS_DIALOG);
+
+            // Verify keyboard visibility should aligned with IME insets visibility.
+            TestUtils.waitOnMainUntil(
+                    () -> testActivity.getWindow().getDecorView().getVisibility() == VISIBLE
+                            && testActivity.getWindow().getDecorView().hasWindowFocus(), TIMEOUT);
+
+            AtomicReference<Boolean> imeInsetsVisible = new AtomicReference<>();
+            TestUtils.runOnMainSync(() ->
+                    imeInsetsVisible.set(editTextRef.get().getRootWindowInsets().isVisible(ime())));
+
+            if (imeInsetsVisible.get()) {
+                expectImeVisible(TIMEOUT);
+            } else {
+                expectImeInvisible(TIMEOUT);
+            }
+        }
+    }
+
+    private void launchRemoteDialogActivitySync(ComponentName componentName, boolean instant,
+            long timeout) {
+        final StringBuilder commandBuilder = new StringBuilder();
+        if (instant) {
+            final Uri uri = formatStringIntentParam(
+                    TEST_ACTIVITY_URI, EXTRA_KEY_SHOW_DIALOG, "true");
+            commandBuilder.append(String.format("am start -a %s -c %s %s",
+                    Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE, uri.toString()));
+        } else {
+            commandBuilder.append("am start -n ").append(componentName.flattenToShortString());
+        }
+
+        runWithShellPermissionIdentity(() -> {
+            runShellCommand(commandBuilder.toString());
+        });
+        UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        BySelector activitySelector = By.pkg(componentName.getPackageName()).depth(0);
+        uiDevice.wait(Until.hasObject(activitySelector), timeout);
+        assertNotNull(uiDevice.findObject(activitySelector));
+    }
+
+    @NonNull
+    private static Uri formatStringIntentParam(@NonNull Uri uri, @NonNull String key,
+            @Nullable String value) {
+        if (value == null) {
+            return uri;
+        }
+        return uri.buildUpon().appendQueryParameter(key, value).build();
+    }
+
+    private void triggerActionWithBroadcast(String action, String receiverPackage, String extra) {
+        final StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append("am broadcast -a ").append(action).append(" -p ").append(
+                receiverPackage);
+        commandBuilder.append(" -f 0x").append(
+                Integer.toHexString(FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS));
+        commandBuilder.append(" --ez " + extra + " true");
+        runWithShellPermissionIdentity(() -> {
+            runShellCommand(commandBuilder.toString());
+        });
+    }
+
     private static ImeSettings.Builder getFloatingImeSettings(@ColorInt int navigationBarColor) {
         final ImeSettings.Builder builder = new ImeSettings.Builder();
         builder.setWindowFlags(0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
index 04ac957..0b44a24 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
@@ -137,6 +137,19 @@
                 .getInstrumentation().startActivitySync(intent);
     }
 
+    public static TestActivity startNewTaskSync(
+            @NonNull Function<TestActivity, View> activityInitializer) {
+        sInitializer.set(activityInitializer);
+        final Intent intent = new Intent()
+                .setAction(Intent.ACTION_MAIN)
+                .setClass(InstrumentationRegistry.getInstrumentation().getContext(),
+                        TestActivity.class)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+        return (TestActivity) InstrumentationRegistry
+                .getInstrumentation().startActivitySync(intent);
+    }
+
     /**
      * Updates {@link WindowManager.LayoutParams#softInputMode}.
      *
diff --git a/tests/inputmethod/testapp/src/android/view/inputmethod/ctstestapp/MainActivity.java b/tests/inputmethod/testapp/src/android/view/inputmethod/ctstestapp/MainActivity.java
index 58d5c42..b0930ec 100644
--- a/tests/inputmethod/testapp/src/android/view/inputmethod/ctstestapp/MainActivity.java
+++ b/tests/inputmethod/testapp/src/android/view/inputmethod/ctstestapp/MainActivity.java
@@ -15,13 +15,25 @@
  */
 package android.view.inputmethod.ctstestapp;
 
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Gravity;
 import android.widget.EditText;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 
@@ -32,34 +44,93 @@
 
     private static final String EXTRA_KEY_PRIVATE_IME_OPTIONS =
             "android.view.inputmethod.ctstestapp.EXTRA_KEY_PRIVATE_IME_OPTIONS";
+    private static final String EXTRA_KEY_SHOW_DIALOG =
+            "android.view.inputmethod.ctstestapp.EXTRA_KEY_SHOW_DIALOG";
+
+    private static final String EXTRA_DISMISS_DIALOG = "extra_dismiss_dialog";
+
+    private static final String ACTION_TRIGGER = "broadcast_action_trigger";
+    private AlertDialog mDialog;
+    private final Handler mHandler = new Handler(Looper.myLooper());
+
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getBooleanExtra(EXTRA_DISMISS_DIALOG, false)) {
+                mDialog.dismiss();
+                mHandler.postDelayed(() -> finish(), 100);
+            }
+        }
+    };
 
     @Nullable
-    private String getPrivateImeOptions() {
+    private String getStringIntentExtra(String key) {
         if (getPackageManager().isInstantApp()) {
             final Uri uri = getIntent().getData();
             if (uri == null || !uri.isHierarchical()) {
                 return null;
             }
-            return uri.getQueryParameter(EXTRA_KEY_PRIVATE_IME_OPTIONS);
+            return uri.getQueryParameter(key);
         }
-        return getIntent().getStringExtra(EXTRA_KEY_PRIVATE_IME_OPTIONS);
+        return getIntent().getStringExtra(key);
+    }
+
+    private boolean getBooleanIntentExtra(String key) {
+        if (getPackageManager().isInstantApp()) {
+            final Uri uri = getIntent().getData();
+            if (uri == null || !uri.isHierarchical()) {
+                return false;
+            }
+            return uri.getBooleanQueryParameter(key, false);
+        }
+        return getIntent().getBooleanExtra(key, false);
     }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TRIGGER));
 
         final LinearLayout layout = new LinearLayout(this);
         layout.setOrientation(LinearLayout.VERTICAL);
-        final EditText editText = new EditText(this);
-        editText.setHint("editText");
-        final String privateImeOptions = getPrivateImeOptions();
-        if (privateImeOptions != null) {
-            editText.setPrivateImeOptions(privateImeOptions);
+        final boolean needShowDialog = getBooleanIntentExtra(EXTRA_KEY_SHOW_DIALOG);
+
+        if (needShowDialog) {
+            layout.setOrientation(LinearLayout.VERTICAL);
+            layout.setGravity(Gravity.BOTTOM);
+            getWindow().setSoftInputMode(SOFT_INPUT_ADJUST_RESIZE);
+
+            final TextView textView = new TextView(this);
+            textView.setText("This is DialogActivity");
+            layout.addView(textView);
+
+            mDialog= new AlertDialog.Builder(this)
+                    .setView(new LinearLayout(this))
+                    .create();
+            mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
+            mDialog.getWindow().setSoftInputMode(SOFT_INPUT_ADJUST_PAN);
+            mDialog.show();
+        } else {
+            final EditText editText = new EditText(this);
+            editText.setHint("editText");
+            final String privateImeOptions = getStringIntentExtra(EXTRA_KEY_PRIVATE_IME_OPTIONS);
+            if (privateImeOptions != null) {
+                editText.setPrivateImeOptions(privateImeOptions);
+            }
+            editText.requestFocus();
+            layout.addView(editText);
+            getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
         }
-        editText.requestFocus();
-        layout.addView(editText);
-        getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+
         setContentView(layout);
     }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mBroadcastReceiver != null) {
+            unregisterReceiver(mBroadcastReceiver);
+            mBroadcastReceiver = null;
+        }
+    }
 }