1 /* 2 * Copyright (C) 2021 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 com.android.inputmethod.stresstest; 18 19 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED; 20 21 import static com.android.inputmethod.stresstest.ImeStressTestUtil.INPUT_METHOD_MANAGER_HIDE_ON_CREATE; 22 import static com.android.inputmethod.stresstest.ImeStressTestUtil.INPUT_METHOD_MANAGER_SHOW_ON_CREATE; 23 import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE; 24 import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity; 25 import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent; 26 import static com.android.inputmethod.stresstest.ImeStressTestUtil.WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE; 27 import static com.android.inputmethod.stresstest.ImeStressTestUtil.WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE; 28 import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync; 29 import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters; 30 import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags; 31 import static com.android.inputmethod.stresstest.ImeStressTestUtil.isImeShown; 32 import static com.android.inputmethod.stresstest.ImeStressTestUtil.requestFocusAndVerify; 33 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet; 34 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden; 35 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus; 36 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil; 37 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden; 38 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown; 39 40 import static com.google.common.truth.Truth.assertThat; 41 42 import static org.junit.Assume.assumeFalse; 43 import static org.junit.Assume.assumeTrue; 44 45 import android.app.Instrumentation; 46 import android.content.Intent; 47 import android.os.Build; 48 import android.os.SystemClock; 49 import android.platform.test.annotations.RootPermissionTest; 50 import android.platform.test.rule.UnlockScreenRule; 51 import android.support.test.uiautomator.UiDevice; 52 import android.util.Log; 53 import android.view.WindowManager; 54 import android.widget.EditText; 55 56 import androidx.test.platform.app.InstrumentationRegistry; 57 58 import org.junit.Rule; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 import org.junit.runners.Parameterized; 62 63 import java.util.ArrayList; 64 import java.util.Arrays; 65 import java.util.Collections; 66 import java.util.List; 67 68 @RootPermissionTest 69 @RunWith(Parameterized.class) 70 public final class ImeOpenCloseStressTest { 71 72 private static final String TAG = "ImeOpenCloseStressTest"; 73 private static final int NUM_TEST_ITERATIONS = 10; 74 75 @Rule(order = 0) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule(); 76 @Rule(order = 1) public ImeStressTestRule mImeStressTestRule = 77 new ImeStressTestRule(true /* useSimpleTestIme */); 78 @Rule(order = 2) public ScreenCaptureRule mScreenCaptureRule = 79 new ScreenCaptureRule("/sdcard/InputMethodStressTest"); 80 81 private final Instrumentation mInstrumentation; 82 private final int mSoftInputFlags; 83 private final int mWindowFocusFlags; 84 85 @Parameterized.Parameters( 86 name = "windowFocusFlags={0}, softInputVisibility={1}, softInputAdjustment={2}") windowAndSoftInputFlagParameters()87 public static List<Object[]> windowAndSoftInputFlagParameters() { 88 return getWindowAndSoftInputFlagParameters(); 89 } 90 ImeOpenCloseStressTest( int windowFocusFlags, int softInputVisibility, int softInputAdjustment)91 public ImeOpenCloseStressTest( 92 int windowFocusFlags, int softInputVisibility, int softInputAdjustment) { 93 mSoftInputFlags = softInputVisibility | softInputAdjustment; 94 mWindowFocusFlags = windowFocusFlags; 95 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 96 } 97 98 @Test testShowHideWithInputMethodManager_waitingVisibilityChange()99 public void testShowHideWithInputMethodManager_waitingVisibilityChange() { 100 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 101 TestActivity activity = TestActivity.start(intent); 102 // Request focus after app starts to avoid triggering auto-show behavior. 103 requestFocusAndVerify(activity); 104 105 // Test only once if window flags set to save time. 106 int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS; 107 for (int i = 0; i < iterNum; i++) { 108 String msgPrefix = "Iteration #" + i + " "; 109 Log.i(TAG, msgPrefix + "start"); 110 callOnMainSync(activity::showImeWithInputMethodManager); 111 verifyShowBehavior(activity); 112 113 callOnMainSync(activity::hideImeWithInputMethodManager); 114 verifyHideBehavior(activity); 115 } 116 } 117 118 @Test testShowHideWithInputMethodManager_waitingAnimationEnd()119 public void testShowHideWithInputMethodManager_waitingAnimationEnd() { 120 assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags)); 121 122 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 123 TestActivity activity = TestActivity.start(intent); 124 // Request focus after app starts to avoid triggering auto-show behavior. 125 requestFocusAndVerify(activity); 126 127 activity.enableAnimationMonitoring(); 128 EditText editText = activity.getEditText(); 129 for (int i = 0; i < NUM_TEST_ITERATIONS; i++) { 130 String msgPrefix = "Iteration #" + i + " "; 131 Log.i(TAG, msgPrefix + "start"); 132 callOnMainSync(activity::showImeWithInputMethodManager); 133 waitOnMainUntil( 134 msgPrefix + "IME should have been shown", 135 () -> !activity.isAnimating() && isImeShown(editText)); 136 137 callOnMainSync(activity::hideImeWithInputMethodManager); 138 waitOnMainUntil( 139 msgPrefix + "IME should have been hidden", 140 () -> !activity.isAnimating() && !isImeShown(editText)); 141 } 142 } 143 144 @Test testShowHideWithInputMethodManager_intervalAfterHide()145 public void testShowHideWithInputMethodManager_intervalAfterHide() { 146 // Regression test for b/221483132 147 assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags)); 148 149 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 150 TestActivity activity = TestActivity.start(intent); 151 // Request focus after app starts to avoid triggering auto-show behavior. 152 requestFocusAndVerify(activity); 153 154 // Intervals = 10, 20, 30, ..., 100, 150, 200, ... 155 List<Integer> intervals = new ArrayList<>(); 156 for (int i = 10; i < 100; i += 10) intervals.add(i); 157 for (int i = 100; i < 1000; i += 50) intervals.add(i); 158 for (int intervalMillis : intervals) { 159 String msgPrefix = "Interval = " + intervalMillis + " "; 160 Log.i(TAG, msgPrefix + " start"); 161 callOnMainSync(activity::hideImeWithInputMethodManager); 162 SystemClock.sleep(intervalMillis); 163 164 callOnMainSync(activity::showImeWithInputMethodManager); 165 verifyShowBehavior(activity); 166 } 167 } 168 169 @Test testShowHideWithInputMethodManager_inSameFrame()170 public void testShowHideWithInputMethodManager_inSameFrame() { 171 assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags)); 172 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 173 TestActivity activity = TestActivity.start(intent); 174 // Request focus after app starts to avoid triggering auto-show behavior. 175 requestFocusAndVerify(activity); 176 177 // hidden -> show -> hide 178 mInstrumentation.runOnMainSync( 179 () -> { 180 Log.i(TAG, "Calling showIme() and hideIme()"); 181 activity.showImeWithInputMethodManager(); 182 activity.hideImeWithInputMethodManager(); 183 }); 184 // Wait until IMMS / IMS handles messages. 185 SystemClock.sleep(1000); 186 mInstrumentation.waitForIdleSync(); 187 verifyHideBehavior(activity); 188 189 mInstrumentation.runOnMainSync(activity::showImeWithInputMethodManager); 190 verifyShowBehavior(activity); 191 mInstrumentation.waitForIdleSync(); 192 193 // shown -> hide -> show 194 mInstrumentation.runOnMainSync( 195 () -> { 196 Log.i(TAG, "Calling hideIme() and showIme()"); 197 activity.hideImeWithInputMethodManager(); 198 activity.showImeWithInputMethodManager(); 199 }); 200 // Wait until IMMS / IMS handles messages. 201 SystemClock.sleep(1000); 202 mInstrumentation.waitForIdleSync(); 203 verifyShowBehavior(activity); 204 } 205 206 /** 207 * Test IME hidden by calling show and hide IME consecutively with 208 * {@link android.view.inputmethod.InputMethodManager} APIs in 209 * {@link android.app.Activity#onCreate}. 210 * 211 * <p> Note for developers: Use {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_UNCHANGED} 212 * window flag to avoid some softInputMode visibility flags may take presence over 213 * {@link android.view.inputmethod.InputMethodManager} APIs (e.g. use showSoftInput to show 214 * IME in {@link android.app.Activity#onCreate} but being hidden by 215 * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN} window flag after the 216 * activity window focused).</p> 217 */ 218 @Test testShowHideWithInputMethodManager_onCreate()219 public void testShowHideWithInputMethodManager_onCreate() { 220 if (mSoftInputFlags != SOFT_INPUT_STATE_UNCHANGED) { 221 return; 222 } 223 // Show and hide with InputMethodManager at onCreate() 224 Intent intent = 225 createIntent( 226 mWindowFocusFlags, 227 mSoftInputFlags, 228 Arrays.asList( 229 REQUEST_FOCUS_ON_CREATE, 230 INPUT_METHOD_MANAGER_SHOW_ON_CREATE, 231 INPUT_METHOD_MANAGER_HIDE_ON_CREATE)); 232 TestActivity activity = TestActivity.start(intent); 233 234 verifyHideBehavior(activity); 235 } 236 237 @Test testShowWithInputMethodManager_notRequestFocus()238 public void testShowWithInputMethodManager_notRequestFocus() { 239 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 240 TestActivity activity = TestActivity.start(intent); 241 242 // Show InputMethodManager without requesting focus 243 callOnMainSync(activity::showImeWithInputMethodManager); 244 245 int windowFlags = activity.getWindow().getAttributes().flags; 246 EditText editText = activity.getEditText(); 247 if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) { 248 verifyWindowAndViewFocus( 249 editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false); 250 } else { 251 verifyWindowAndViewFocus( 252 editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false); 253 } 254 // The Ime should always be hidden because view never gains focus. 255 verifyImeIsAlwaysHidden(editText); 256 } 257 258 @Test testShowHideWithWindowInsetsController_waitingVisibilityChange()259 public void testShowHideWithWindowInsetsController_waitingVisibilityChange() { 260 assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); 261 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 262 TestActivity activity = TestActivity.start(intent); 263 // Request focus after app starts to avoid triggering auto-show behavior. 264 requestFocusAndVerify(activity); 265 266 // Test only once if window flags set to save time. 267 int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS; 268 for (int i = 0; i < iterNum; i++) { 269 String msgPrefix = "Iteration #" + i + " "; 270 Log.i(TAG, msgPrefix + "start"); 271 mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController); 272 verifyShowBehavior(activity); 273 mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController); 274 verifyHideBehavior(activity); 275 } 276 } 277 278 @Test testShowHideWithWindowInsetsController_waitingAnimationEnd()279 public void testShowHideWithWindowInsetsController_waitingAnimationEnd() { 280 assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); 281 assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags)); 282 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 283 TestActivity activity = TestActivity.start(intent); 284 // Request focus after app starts to avoid triggering auto-show behavior. 285 requestFocusAndVerify(activity); 286 287 activity.enableAnimationMonitoring(); 288 EditText editText = activity.getEditText(); 289 for (int i = 0; i < NUM_TEST_ITERATIONS; i++) { 290 String msgPrefix = "Iteration #" + i + " "; 291 Log.i(TAG, msgPrefix + "start"); 292 mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController); 293 waitOnMainUntil( 294 msgPrefix + "IME should have been shown", 295 () -> !activity.isAnimating() && isImeShown(editText)); 296 297 mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController); 298 waitOnMainUntil( 299 msgPrefix + "IME should have been hidden", 300 () -> !activity.isAnimating() && !isImeShown(editText)); 301 } 302 } 303 304 @Test testShowHideWithWindowInsetsController_intervalAfterHide()305 public void testShowHideWithWindowInsetsController_intervalAfterHide() { 306 assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); 307 assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags)); 308 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 309 TestActivity activity = TestActivity.start(intent); 310 // Request focus after app starts to avoid triggering auto-show behavior. 311 requestFocusAndVerify(activity); 312 313 // Intervals = 10, 20, 30, ..., 100, 150, 200, ... 314 List<Integer> intervals = new ArrayList<>(); 315 for (int i = 10; i < 100; i += 10) intervals.add(i); 316 for (int i = 100; i < 1000; i += 50) intervals.add(i); 317 for (int intervalMillis : intervals) { 318 String msgPrefix = "Interval = " + intervalMillis + " "; 319 Log.i(TAG, msgPrefix + " start"); 320 mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController); 321 SystemClock.sleep(intervalMillis); 322 323 mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController); 324 verifyShowBehavior(activity); 325 } 326 } 327 328 @Test testShowHideWithWindowInsetsController_inSameFrame()329 public void testShowHideWithWindowInsetsController_inSameFrame() { 330 assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); 331 assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags)); 332 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 333 TestActivity activity = TestActivity.start(intent); 334 // Request focus after app starts to avoid triggering auto-show behavior. 335 requestFocusAndVerify(activity); 336 337 // hidden -> show -> hide 338 mInstrumentation.runOnMainSync( 339 () -> { 340 Log.i(TAG, "Calling showIme() and hideIme()"); 341 activity.showImeWithWindowInsetsController(); 342 activity.hideImeWithWindowInsetsController(); 343 }); 344 // Wait until IMMS / IMS handles messages. 345 SystemClock.sleep(1000); 346 mInstrumentation.waitForIdleSync(); 347 verifyHideBehavior(activity); 348 349 mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController); 350 verifyShowBehavior(activity); 351 mInstrumentation.waitForIdleSync(); 352 353 // shown -> hide -> show 354 mInstrumentation.runOnMainSync( 355 () -> { 356 Log.i(TAG, "Calling hideIme() and showIme()"); 357 activity.hideImeWithWindowInsetsController(); 358 activity.showImeWithWindowInsetsController(); 359 }); 360 // Wait until IMMS / IMS handles messages. 361 SystemClock.sleep(1000); 362 mInstrumentation.waitForIdleSync(); 363 verifyShowBehavior(activity); 364 } 365 366 @Test testShowWithWindowInsetsController_onCreate_requestFocus()367 public void testShowWithWindowInsetsController_onCreate_requestFocus() { 368 assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); 369 // Show with InputMethodManager at onCreate() 370 Intent intent = 371 createIntent( 372 mWindowFocusFlags, 373 mSoftInputFlags, 374 Arrays.asList( 375 REQUEST_FOCUS_ON_CREATE, WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE)); 376 TestActivity activity = TestActivity.start(intent); 377 378 verifyShowBehavior(activity); 379 } 380 381 @Test testShowWithWindowInsetsController_onCreate_notRequestFocus()382 public void testShowWithWindowInsetsController_onCreate_notRequestFocus() { 383 assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); 384 // Show and hide with WindowInsetsController at onCreate() 385 Intent intent = 386 createIntent( 387 mWindowFocusFlags, 388 mSoftInputFlags, 389 Collections.singletonList(WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE)); 390 TestActivity activity = TestActivity.start(intent); 391 392 // Ime is shown but with a fallback InputConnection 393 verifyShowBehaviorNotRequestFocus(activity); 394 } 395 396 @Test testShowWithWindowInsetsController_afterStart_notRequestFocus()397 public void testShowWithWindowInsetsController_afterStart_notRequestFocus() { 398 assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); 399 // Show and hide with WindowInsetsController at onCreate() 400 Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList()); 401 TestActivity activity = TestActivity.start(intent); 402 mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController); 403 404 // Ime is shown but with a fallback InputConnection 405 verifyShowBehaviorNotRequestFocus(activity); 406 } 407 408 /** 409 * Test IME hidden by calling show and hide IME consecutively with 410 * {@link android.view.WindowInsetsController} APIs in 411 * {@link android.app.Activity#onCreate}. 412 * 413 * <p> Note for developers: Use {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_UNCHANGED} 414 * window flag to avoid some softInputMode visibility flags may take presence over 415 * {@link android.view.WindowInsetsController} APIs (e.g. use showSoftInput to show 416 * IME in {@link android.app.Activity#onCreate} but being hidden by 417 * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN} window flag after the 418 * activity window focused).</p> 419 */ 420 @Test testHideWithWindowInsetsController_onCreate_requestFocus()421 public void testHideWithWindowInsetsController_onCreate_requestFocus() { 422 assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); 423 if (mSoftInputFlags != SOFT_INPUT_STATE_UNCHANGED) { 424 return; 425 } 426 // Show and hide with WindowInsetsController at onCreate() 427 Intent intent = 428 createIntent( 429 mWindowFocusFlags, 430 mSoftInputFlags, 431 Arrays.asList( 432 REQUEST_FOCUS_ON_CREATE, 433 WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE, 434 WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE)); 435 TestActivity activity = TestActivity.start(intent); 436 437 verifyHideBehavior(activity); 438 } 439 440 @Test testScreenOffOn()441 public void testScreenOffOn() throws Exception { 442 Intent intent1 = 443 createIntent( 444 mWindowFocusFlags, 445 mSoftInputFlags, 446 Collections.singletonList(REQUEST_FOCUS_ON_CREATE)); 447 TestActivity activity = TestActivity.start(intent1); 448 // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity 449 callOnMainSync(activity::showImeWithInputMethodManager); 450 451 Thread.sleep(1000); 452 verifyShowBehavior(activity); 453 454 UiDevice uiDevice = UiDevice.getInstance(mInstrumentation); 455 456 if (uiDevice.isScreenOn()) { 457 uiDevice.sleep(); 458 } 459 Thread.sleep(1000); 460 if (!uiDevice.isScreenOn()) { 461 uiDevice.wakeUp(); 462 } 463 464 verifyShowBehavior(activity); 465 } 466 467 // TODO: Add tests for activities that don't handle the rotation. 468 @Test testRotateScreenWithKeyboardOn()469 public void testRotateScreenWithKeyboardOn() throws Exception { 470 Intent intent = 471 createIntent( 472 mWindowFocusFlags, 473 mSoftInputFlags, 474 Collections.singletonList(REQUEST_FOCUS_ON_CREATE)); 475 TestActivity activity = TestActivity.start(intent); 476 // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity 477 callOnMainSync(activity::showImeWithInputMethodManager); 478 Thread.sleep(2000); 479 verifyShowBehavior(activity); 480 481 UiDevice uiDevice = UiDevice.getInstance(mInstrumentation); 482 483 uiDevice.setOrientationRight(); 484 uiDevice.waitForIdle(); 485 Thread.sleep(1000); 486 Log.i(TAG, "Rotate screen right"); 487 assertThat(uiDevice.isNaturalOrientation()).isFalse(); 488 verifyRotateBehavior(activity); 489 490 uiDevice.setOrientationLeft(); 491 uiDevice.waitForIdle(); 492 Thread.sleep(1000); 493 Log.i(TAG, "Rotate screen left"); 494 assertThat(uiDevice.isNaturalOrientation()).isFalse(); 495 verifyRotateBehavior(activity); 496 497 uiDevice.setOrientationNatural(); 498 uiDevice.waitForIdle(); 499 } 500 verifyShowBehavior(TestActivity activity)501 private static void verifyShowBehavior(TestActivity activity) { 502 if (hasUnfocusableWindowFlags(activity)) { 503 verifyImeAlwaysHiddenWithWindowFlagSet(activity); 504 return; 505 } 506 EditText editText = activity.getEditText(); 507 508 verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true); 509 waitOnMainUntilImeIsShown(editText); 510 } 511 verifyHideBehavior(TestActivity activity)512 private static void verifyHideBehavior(TestActivity activity) { 513 if (hasUnfocusableWindowFlags(activity)) { 514 verifyImeAlwaysHiddenWithWindowFlagSet(activity); 515 return; 516 } 517 EditText editText = activity.getEditText(); 518 519 verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true); 520 waitOnMainUntilImeIsHidden(editText); 521 } 522 verifyShowBehaviorNotRequestFocus(TestActivity activity)523 private static void verifyShowBehaviorNotRequestFocus(TestActivity activity) { 524 int windowFlags = activity.getWindow().getAttributes().flags; 525 EditText editText = activity.getEditText(); 526 527 if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) { 528 verifyWindowAndViewFocus( 529 editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false); 530 verifyImeIsAlwaysHidden(editText); 531 } else if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0 532 || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) { 533 verifyWindowAndViewFocus( 534 editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false); 535 verifyImeIsAlwaysHidden(editText); 536 } else { 537 verifyWindowAndViewFocus( 538 editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false); 539 // Ime is shown but with a fallback InputConnection 540 waitOnMainUntilImeIsShown(editText); 541 } 542 } 543 verifyRotateBehavior(TestActivity activity)544 private static void verifyRotateBehavior(TestActivity activity) { 545 // Get the new TestActivity after recreation. 546 TestActivity newActivity = TestActivity.getLastCreatedInstance(); 547 assertThat(newActivity).isNotNull(); 548 assertThat(newActivity).isNotEqualTo(activity); 549 550 EditText newEditText = newActivity.getEditText(); 551 int softInputMode = newActivity.getWindow().getAttributes().softInputMode; 552 int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE; 553 554 if (hasUnfocusableWindowFlags(newActivity)) { 555 verifyImeAlwaysHiddenWithWindowFlagSet(newActivity); 556 return; 557 } 558 559 if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) { 560 // After rotation, the keyboard would be hidden only when the flag is 561 // SOFT_INPUT_STATE_ALWAYS_HIDDEN. However, SOFT_INPUT_STATE_HIDDEN is different because 562 // it requires appending SOFT_INPUT_IS_FORWARD_NAVIGATION flag, which won't be added 563 // when rotating the devices (rotating doesn't navigate forward to the next app window.) 564 verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/ 565 true); 566 waitOnMainUntilImeIsHidden(newEditText); 567 568 } else { 569 // Other cases, keyboard would be shown. 570 verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/ 571 true); 572 waitOnMainUntilImeIsShown(newEditText); 573 } 574 } 575 } 576