1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view.inputmethod.cts; 18 19 import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; 20 import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; 21 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 22 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 23 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 24 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 25 import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeInvisible; 26 import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeVisible; 27 import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync; 28 import static android.view.inputmethod.cts.util.TestUtils.isInputMethodPickerShown; 29 30 import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher; 31 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent; 32 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEventWithKeyValue; 33 import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent; 34 import static com.android.cts.mockime.ImeEventStreamTestUtils.showSoftInputMatcher; 35 36 import static org.junit.Assert.assertNotNull; 37 import static org.junit.Assert.assertTrue; 38 39 import android.app.Activity; 40 import android.content.Intent; 41 import android.content.res.Configuration; 42 import android.graphics.Color; 43 import android.graphics.PixelFormat; 44 import android.graphics.Point; 45 import android.platform.test.annotations.AppModeFull; 46 import android.platform.test.annotations.AppModeSdkSandbox; 47 import android.util.Pair; 48 import android.view.Gravity; 49 import android.view.View; 50 import android.view.WindowInsets; 51 import android.view.WindowManager; 52 import android.view.inputmethod.InputMethod; 53 import android.view.inputmethod.InputMethodManager; 54 import android.view.inputmethod.cts.util.EndToEndImeTestBase; 55 import android.view.inputmethod.cts.util.TestActivity; 56 import android.view.inputmethod.cts.util.TestUtils; 57 import android.view.inputmethod.cts.util.UnlockScreenRule; 58 import android.widget.EditText; 59 import android.widget.LinearLayout; 60 import android.widget.TextView; 61 62 import androidx.annotation.AnyThread; 63 import androidx.annotation.NonNull; 64 import androidx.test.filters.MediumTest; 65 import androidx.test.platform.app.InstrumentationRegistry; 66 import androidx.test.runner.AndroidJUnit4; 67 68 import com.android.compatibility.common.util.PollingCheck; 69 import com.android.cts.mockime.ImeEventStream; 70 import com.android.cts.mockime.ImeSettings; 71 import com.android.cts.mockime.MockImeSession; 72 73 import org.junit.Assume; 74 import org.junit.Rule; 75 import org.junit.Test; 76 import org.junit.runner.RunWith; 77 78 import java.util.concurrent.TimeUnit; 79 import java.util.concurrent.atomic.AtomicReference; 80 81 @MediumTest 82 @RunWith(AndroidJUnit4.class) 83 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") 84 public class ImeInsetsVisibilityTest extends EndToEndImeTestBase { 85 private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5); 86 private static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(2); 87 private static final int NEW_KEYBOARD_HEIGHT = 300; 88 89 @Rule 90 public final UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule(); 91 92 @Test testImeVisibilityWhenImeFocusableChildPopup()93 public void testImeVisibilityWhenImeFocusableChildPopup() throws Exception { 94 Assume.assumeFalse(isPreventImeStartup()); 95 final InputMethodManager imm = getImmOrFail(); 96 97 try (MockImeSession imeSession = MockImeSession.create( 98 InstrumentationRegistry.getInstrumentation().getContext(), 99 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 100 new ImeSettings.Builder())) { 101 final ImeEventStream stream = imeSession.openEventStream(); 102 103 final String marker = getTestMarker(); 104 final Pair<EditText, TestActivity> editTextTestActivityPair = 105 launchTestActivity(false, marker); 106 final EditText editText = editTextTestActivityPair.first; 107 final TestActivity activity = editTextTestActivityPair.second; 108 109 notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 110 expectImeInvisible(TIMEOUT); 111 112 assertTrue("showSoftInput must success if the View has IME focus", getOnMainSync( 113 () -> editText.requestFocus() && imm.showSoftInput(editText, 0))); 114 115 expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); 116 expectEvent(stream, showSoftInputMatcher(InputMethod.SHOW_EXPLICIT), TIMEOUT); 117 expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 118 expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible", 119 View.VISIBLE, TIMEOUT); 120 PollingCheck.check("Ime insets should be visible", TIMEOUT, 121 () -> editText.getRootWindowInsets().isVisible(WindowInsets.Type.ime())); 122 expectImeVisible(TIMEOUT); 123 124 try (ChildWindowHolder childWindow = createChildTransparentApplicationWindowOnMain( 125 activity, 200 /* width */, 200 /* height */, 126 FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM, 127 WindowInsets.Type.ime() | WindowInsets.Type.statusBars() 128 | WindowInsets.Type.navigationBars())) { 129 // The window will be shown above (in y-axis) the IME. 130 TestUtils.runOnMainSync( 131 () -> childWindow.getRootView().setVisibility(View.VISIBLE)); 132 TestUtils.waitOnMainUntil( 133 () -> editText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()), 134 TIMEOUT, "Ime insets should be visible"); 135 expectImeVisible(TIMEOUT); 136 } 137 } 138 } 139 140 @Test testImeVisibilityWhenImeFocusableGravityBottomChildPopup()141 public void testImeVisibilityWhenImeFocusableGravityBottomChildPopup() throws Exception { 142 Assume.assumeFalse(isPreventImeStartup()); 143 final InputMethodManager imm = getImmOrFail(); 144 145 try (MockImeSession imeSession = MockImeSession.create( 146 InstrumentationRegistry.getInstrumentation().getContext(), 147 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 148 new ImeSettings.Builder().setInputViewHeight(NEW_KEYBOARD_HEIGHT))) { 149 final ImeEventStream stream = imeSession.openEventStream(); 150 151 final String marker = getTestMarker(); 152 final Pair<EditText, TestActivity> editTextTestActivityPair = 153 launchTestActivity(false, marker); 154 final EditText editText = editTextTestActivityPair.first; 155 final TestActivity activity = editTextTestActivityPair.second; 156 157 notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 158 expectImeInvisible(TIMEOUT); 159 160 assertTrue("showSoftInput must success if the View has IME focus", getOnMainSync( 161 () -> editText.requestFocus() && imm.showSoftInput(editText, 0))); 162 163 expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); 164 expectEvent(stream, showSoftInputMatcher(InputMethod.SHOW_EXPLICIT), TIMEOUT); 165 expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 166 PollingCheck.check("Ime insets should be visible", TIMEOUT, 167 () -> editText.getRootWindowInsets().isVisible(WindowInsets.Type.ime())); 168 expectImeVisible(TIMEOUT); 169 170 try (ChildWindowHolder childWindow = createChildBottomPanelWindowOnMain(activity, 171 MATCH_PARENT /* width */, NEW_KEYBOARD_HEIGHT /* height */, 172 FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)) { 173 // The window will be shown above (in y-axis) the IME. 174 TestUtils.runOnMainSync(() -> { 175 childWindow.getRootView().setBackgroundColor(Color.RED); 176 childWindow.getRootView().setVisibility(View.VISIBLE); 177 }); 178 // IME should be on screen without reset. 179 notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 180 181 TestUtils.waitOnMainUntil( 182 () -> editText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()), 183 TIMEOUT, "Ime insets should be visible"); 184 expectImeVisible(TIMEOUT); 185 } 186 } 187 } 188 189 @Test testImeVisibilityWhenImeFocusableChildPopupOverlaps()190 public void testImeVisibilityWhenImeFocusableChildPopupOverlaps() throws Exception { 191 Assume.assumeFalse(isPreventImeStartup()); 192 final InputMethodManager imm = getImmOrFail(); 193 194 try (MockImeSession imeSession = MockImeSession.create( 195 InstrumentationRegistry.getInstrumentation().getContext(), 196 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 197 new ImeSettings.Builder().setInputViewHeight(NEW_KEYBOARD_HEIGHT))) { 198 final ImeEventStream stream = imeSession.openEventStream(); 199 200 final String marker = getTestMarker(); 201 final Pair<EditText, TestActivity> editTextTestActivityPair = 202 launchTestActivity(false, marker); 203 final EditText editText = editTextTestActivityPair.first; 204 final TestActivity activity = editTextTestActivityPair.second; 205 206 notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 207 expectImeInvisible(TIMEOUT); 208 209 assertTrue("showSoftInput must success if the View has IME focus", getOnMainSync( 210 () -> editText.requestFocus() && imm.showSoftInput(editText, 0))); 211 212 expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); 213 expectEvent(stream, showSoftInputMatcher(InputMethod.SHOW_EXPLICIT), TIMEOUT); 214 expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 215 PollingCheck.check("Ime insets should be visible", TIMEOUT, 216 () -> editText.getRootWindowInsets().isVisible(WindowInsets.Type.ime())); 217 expectImeVisible(TIMEOUT); 218 219 try (ChildWindowHolder childWindow = createChildBottomPanelWindowOnMain(activity, 220 MATCH_PARENT /* width */, NEW_KEYBOARD_HEIGHT /* height */, 221 FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM | FLAG_LAYOUT_IN_SCREEN)) { 222 // The window will be shown behind (in z-axis) the IME. 223 TestUtils.runOnMainSync(() -> { 224 childWindow.getRootView().setBackgroundColor(Color.RED); 225 childWindow.getRootView().setVisibility(View.VISIBLE); 226 }); 227 // IME should be on screen without reset. 228 notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 229 230 TestUtils.waitOnMainUntil( 231 () -> editText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()), 232 TIMEOUT, "Ime insets should be visible"); 233 expectImeVisible(TIMEOUT); 234 } 235 } 236 } 237 238 @AppModeFull(reason = "Instant apps cannot rely on ACTION_CLOSE_SYSTEM_DIALOGS") 239 @Test testEditTextPositionAndPersistWhenAboveImeWindowShown()240 public void testEditTextPositionAndPersistWhenAboveImeWindowShown() throws Exception { 241 Assume.assumeFalse(isPreventImeStartup()); 242 final InputMethodManager imm = getImmOrFail(); 243 244 try (MockImeSession imeSession = MockImeSession.create( 245 InstrumentationRegistry.getInstrumentation().getContext(), 246 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 247 new ImeSettings.Builder().setInputViewHeight(NEW_KEYBOARD_HEIGHT))) { 248 final ImeEventStream stream = imeSession.openEventStream(); 249 250 final String marker = getTestMarker(); 251 final Pair<EditText, TestActivity> editTextTestActivityPair = 252 launchTestActivity(true, marker); 253 final EditText editText = editTextTestActivityPair.first; 254 final TestActivity activity = editTextTestActivityPair.second; 255 final WindowInsets[] insetsFromActivity = new WindowInsets[1]; 256 Point curEditPos = getLocationOnScreenForView(editText); 257 258 TestUtils.runOnMainSync(() -> { 259 activity.getWindow().getDecorView().setOnApplyWindowInsetsListener( 260 (v, insets) -> insetsFromActivity[0] = insets); 261 }); 262 263 notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 264 expectImeInvisible(TIMEOUT); 265 266 assertTrue("showSoftInput must success if the View has IME focus", getOnMainSync( 267 () -> editText.requestFocus() && imm.showSoftInput(editText, 0))); 268 269 expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); 270 expectEvent(stream, showSoftInputMatcher(InputMethod.SHOW_EXPLICIT), TIMEOUT); 271 expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 272 expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible", 273 View.VISIBLE, TIMEOUT); 274 expectImeVisible(TIMEOUT); 275 276 Point lastEditTextPos = new Point(curEditPos); 277 curEditPos = getLocationOnScreenForView(editText); 278 // Watch doesn't support navigation bar and has limited screen size, so no transition 279 // in editbox with respect to x and y coordinates 280 Configuration config = InstrumentationRegistry.getInstrumentation() 281 .getContext() 282 .getResources() 283 .getConfiguration(); 284 boolean isSmallScreenLayout = 285 config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_SMALL); 286 287 if (isSmallScreenLayout) { 288 assertTrue("Insets should visible", 289 isInsetsVisible(insetsFromActivity[0], WindowInsets.Type.ime())); 290 } else { 291 assertTrue("Insets should visible and EditText position should be adjusted", 292 isInsetsVisible(insetsFromActivity[0], WindowInsets.Type.ime()) 293 && curEditPos.y < lastEditTextPos.y); 294 } 295 296 imm.showInputMethodPicker(); 297 TestUtils.waitOnMainUntil(() -> isInputMethodPickerShown(imm) && editText.isLaidOut(), 298 TIMEOUT, "InputMethod picker should be shown"); 299 lastEditTextPos = new Point(curEditPos); 300 curEditPos = getLocationOnScreenForView(editText); 301 302 assertTrue("Insets visibility & EditText position should persist when " 303 + "the above IME window shown", 304 isInsetsVisible(insetsFromActivity[0], WindowInsets.Type.ime()) 305 && curEditPos.equals(lastEditTextPos)); 306 307 InstrumentationRegistry.getInstrumentation().getContext().sendBroadcast( 308 new Intent(ACTION_CLOSE_SYSTEM_DIALOGS).setFlags(FLAG_RECEIVER_FOREGROUND)); 309 TestUtils.waitOnMainUntil(() -> !isInputMethodPickerShown(imm), TIMEOUT, 310 "InputMethod picker should be closed"); 311 } 312 } 313 314 /** 315 * Test the IME window won't cover the editor when the app creates a panel window to receive 316 * the IME insets. 317 * 318 * <p>Regression test for Bug 195765264 and Bug 152304051.</p> 319 */ 320 @Test testEditorWontCoveredByImeWhenInputWindowBehindPanel()321 public void testEditorWontCoveredByImeWhenInputWindowBehindPanel() throws Exception { 322 try (MockImeSession imeSession = MockImeSession.create( 323 InstrumentationRegistry.getInstrumentation().getContext(), 324 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 325 new ImeSettings.Builder())) { 326 final ImeEventStream stream = imeSession.openEventStream(); 327 final String marker = getTestMarker(); 328 // Launch a test activity with SOFT_INPUT_ADJUST_NOTHING to not resize by IME insets. 329 final AtomicReference<EditText> editTextRef = new AtomicReference<>(); 330 final TestActivity testActivity = TestActivity.startSync(activity -> { 331 final LinearLayout layout = new LinearLayout(activity); 332 layout.setOrientation(LinearLayout.VERTICAL); 333 layout.setGravity(Gravity.BOTTOM); 334 final EditText editText = new EditText(activity); 335 editText.setHint("focused editText"); 336 editText.setPrivateImeOptions(marker); 337 // Initial editor visibility as GONE for testing IME visibility controlled by panel. 338 editText.setVisibility(View.GONE); 339 editTextRef.set(editText); 340 layout.addView(editText); 341 activity.getWindow().setSoftInputMode( 342 WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING); 343 return layout; 344 }); 345 final EditText editText = editTextRef.get(); 346 // Create a panel window to receive IME insets for adjusting editText position. 347 final View panelView = TestUtils.getOnMainSync(() -> { 348 final View panel = new View(testActivity); 349 panel.setOnApplyWindowInsetsListener((v, insets) -> { 350 if (insets.isVisible(WindowInsets.Type.ime())) { 351 // Request editText focused when IME insets visible. 352 editText.setVisibility(View.VISIBLE); 353 editText.requestFocus(); 354 LinearLayout.LayoutParams lp = 355 (LinearLayout.LayoutParams) editText.getLayoutParams(); 356 lp.setMargins(0, 0, 0, editText.getRootView().getMeasuredHeight() 357 - panel.getMeasuredHeight()); 358 editText.requestLayout(); 359 } else { 360 // Clear editText focused when IME insets invisible. 361 editText.clearFocus(); 362 editText.setVisibility(View.GONE); 363 } 364 return insets; 365 }); 366 final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 367 1, MATCH_PARENT, 368 0, 0, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, 369 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 370 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 371 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 372 PixelFormat.TRANSLUCENT); 373 lp.setFitInsetsTypes(WindowInsets.Type.ime() | WindowInsets.Type.systemBars()); 374 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 375 lp.token = testActivity.getWindow().getDecorView().getWindowToken(); 376 testActivity.getWindowManager().addView(panel, lp); 377 return panel; 378 }); 379 notExpectEvent(stream, editorMatcher("onStartInputView", marker), NOT_EXPECT_TIMEOUT); 380 expectImeInvisible(TIMEOUT); 381 // Show IME by using WindowInsets API. 382 testActivity.getWindow().getInsetsController().show(WindowInsets.Type.ime()); 383 TestUtils.waitOnMainUntil(() -> isInsetsVisible(panelView.getRootWindowInsets(), 384 WindowInsets.Type.ime()), TIMEOUT, "The panel should receive IME insets"); 385 TestUtils.waitOnMainUntil( 386 () -> editText.getVisibility() == View.VISIBLE && editText.hasFocus(), 387 TIMEOUT, "The editor should be shown and visible"); 388 expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); 389 expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); 390 expectImeVisible(TIMEOUT); 391 } 392 } 393 isInsetsVisible(WindowInsets winInsets, int type)394 private boolean isInsetsVisible(WindowInsets winInsets, int type) { 395 if (winInsets == null) { 396 return false; 397 } 398 return winInsets.isVisible(type); 399 } 400 getLocationOnScreenForView(View view)401 private Point getLocationOnScreenForView(View view) { 402 return TestUtils.getOnMainSync(() -> { 403 final int[] tmpPos = new int[2]; 404 view.getLocationOnScreen(tmpPos); 405 return new Point(tmpPos[0], tmpPos[1]); 406 }); 407 } 408 launchTestActivity(boolean useDialogTheme, @NonNull String focusedMarker)409 private Pair<EditText, TestActivity> launchTestActivity(boolean useDialogTheme, 410 @NonNull String focusedMarker) { 411 final AtomicReference<EditText> focusedEditTextRef = new AtomicReference<>(); 412 final AtomicReference<TestActivity> testActivityRef = new AtomicReference<>(); 413 414 TestActivity.startSync(activity -> { 415 final LinearLayout layout = new LinearLayout(activity); 416 layout.setOrientation(LinearLayout.VERTICAL); 417 layout.setGravity(Gravity.BOTTOM); 418 if (useDialogTheme) { 419 // Create a floating Dialog 420 activity.setTheme(android.R.style.Theme_Material_Dialog); 421 TextView textView = new TextView(activity); 422 textView.setText("I'm a TextView"); 423 textView.setHeight(activity.getWindowManager().getMaximumWindowMetrics() 424 .getBounds().height() / 3); 425 layout.addView(textView); 426 } 427 428 final EditText focusedEditText = new EditText(activity); 429 focusedEditText.setHint("focused editText"); 430 focusedEditText.setPrivateImeOptions(focusedMarker); 431 432 focusedEditTextRef.set(focusedEditText); 433 testActivityRef.set(activity); 434 435 layout.addView(focusedEditText); 436 return layout; 437 }); 438 return new Pair<>(focusedEditTextRef.get(), testActivityRef.get()); 439 } 440 441 /** 442 * A utility class to pack the root {@link View} and its clean-up operation that is compatible 443 * with {@link AutoCloseable} protocol. 444 */ 445 private static final class ChildWindowHolder implements AutoCloseable { 446 @NonNull 447 private final View mRootView; 448 ChildWindowHolder(@onNull View rootView)449 private ChildWindowHolder(@NonNull View rootView) { 450 mRootView = rootView; 451 } 452 453 @NonNull 454 @AnyThread getRootView()455 View getRootView() { 456 return mRootView; 457 } 458 459 @Override close()460 public void close() { 461 TestUtils.runOnMainSync(() -> mRootView.getContext() 462 .getSystemService(WindowManager.class).removeView(mRootView)); 463 } 464 } 465 466 @NonNull createChildBottomPanelWindowOnMain(Activity activity, int width, int height, int windowFlags)467 private ChildWindowHolder createChildBottomPanelWindowOnMain(Activity activity, int width, 468 int height, int windowFlags) { 469 return TestUtils.getOnMainSync(() -> { 470 final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(); 471 attrs.token = null; 472 attrs.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 473 attrs.width = width; 474 attrs.height = height; 475 attrs.gravity = Gravity.BOTTOM; 476 attrs.flags = windowFlags; 477 final View childViewRoot = new View(activity); 478 activity.getSystemService(WindowManager.class).addView(childViewRoot, attrs); 479 return new ChildWindowHolder(childViewRoot); 480 }); 481 } 482 483 @NonNull 484 private ChildWindowHolder createChildTransparentApplicationWindowOnMain(Activity activity, 485 int width, int height, int windowFlags, int fitInsetsTypes) { 486 return TestUtils.getOnMainSync(() -> { 487 final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(); 488 attrs.token = activity.getWindow().getAttributes().token; 489 attrs.type = WindowManager.LayoutParams.TYPE_APPLICATION; 490 attrs.width = width; 491 attrs.height = height; 492 attrs.format = PixelFormat.TRANSPARENT; 493 attrs.gravity = Gravity.NO_GRAVITY; 494 attrs.flags = windowFlags; 495 attrs.setFitInsetsTypes(fitInsetsTypes); 496 final View childViewRoot = new View(activity); 497 activity.getSystemService(WindowManager.class).addView(childViewRoot, attrs); 498 return new ChildWindowHolder(childViewRoot); 499 }); 500 } 501 502 @NonNull 503 private static InputMethodManager getImmOrFail() { 504 final InputMethodManager imm = InstrumentationRegistry.getInstrumentation() 505 .getTargetContext().getSystemService(InputMethodManager.class); 506 assertNotNull(imm); 507 return imm; 508 } 509 } 510