diff options
author | 2025-02-04 18:48:18 +0100 | |
---|---|---|
committer | 2025-02-05 16:32:43 +0100 | |
commit | eeccf514a5de1ddf9ecff587f23750116b5871ee (patch) | |
tree | 77c07262f849cb3afe2f3ea41d0ff6ae62100999 | |
parent | 8c3f859c6d4f9e186b725265965a8cd56ac5e4cb (diff) |
Cleanup InputMethodServiceTest and SimpleIMS
This cleans up the InputMethodServiceTest code, as well as SimpleIMS and
its TestActivity, by clarifying method/field names, adding nullability
annotations, and introducing assertion messages.
Additionally removed the return value from showing/hiding the IME via
the InsetsController APIs, as these were always returning true.
Extracted separate methods for checking whether there is a navigation
bar, and setting the IME NavButtonFlags manually. Ensured the
countDownLatchForTesting is reset after usage. Explicilty handled
exceptions by failing the test with a clear message. Added the missing
testShowHideKeyboard_byInputMethodManager test. Removed an incorrect
assertion from testImeSwitchButtonLongClick.
Flag: EXEMPT testfix
Bug: 394328311
Test: atest InputMethodServiceTest
Change-Id: Id4aaa987114d4a90213f20a2e0895776b4eab0a5
5 files changed, 490 insertions, 392 deletions
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index c954b185914c..27adec003355 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -19,11 +19,12 @@ package com.android.inputmethodservice; import static android.view.WindowInsets.Type.captionBar; import static com.android.compatibility.common.util.SystemUtil.eventually; +import static com.android.internal.inputmethod.InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR; +import static com.android.internal.inputmethod.InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; @@ -49,6 +50,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; @@ -56,7 +58,6 @@ import androidx.test.uiautomator.Until; import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; import com.android.apps.inputmethod.simpleime.testing.TestActivity; import com.android.compatibility.common.util.SystemUtil; -import com.android.internal.inputmethod.InputMethodNavButtonFlags; import org.junit.After; import org.junit.Before; @@ -64,7 +65,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.IOException; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -72,6 +72,7 @@ import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @MediumTest public class InputMethodServiceTest { + private static final String TAG = "SimpleIMSTest"; private static final String INPUT_METHOD_SERVICE_NAME = ".SimpleInputMethodService"; private static final String EDIT_TEXT_DESC = "Input box"; @@ -87,45 +88,50 @@ public class InputMethodServiceTest { private final DeviceFlagsValueProvider mFlagsValueProvider = new DeviceFlagsValueProvider(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = new CheckFlagsRule(mFlagsValueProvider); + private Instrumentation mInstrumentation; private UiDevice mUiDevice; private Context mContext; + private InputMethodManager mImm; private String mTargetPackageName; + private String mInputMethodId; private TestActivity mActivity; private InputMethodServiceWrapper mInputMethodService; - private String mInputMethodId; private boolean mShowImeWithHardKeyboardEnabled; - @Rule - public final CheckFlagsRule mCheckFlagsRule = new CheckFlagsRule(mFlagsValueProvider); - @Before public void setUp() throws Exception { mInstrumentation = InstrumentationRegistry.getInstrumentation(); mUiDevice = UiDevice.getInstance(mInstrumentation); mContext = mInstrumentation.getContext(); + mImm = mContext.getSystemService(InputMethodManager.class); mTargetPackageName = mInstrumentation.getTargetContext().getPackageName(); mInputMethodId = getInputMethodId(); prepareIme(); - prepareEditor(); + prepareActivity(); mInstrumentation.waitForIdleSync(); mUiDevice.freezeRotation(); mUiDevice.setOrientationNatural(); // Waits for input binding ready. eventually(() -> { - mInputMethodService = - InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting(); - assertThat(mInputMethodService).isNotNull(); + mInputMethodService = InputMethodServiceWrapper.getInstance(); + assertWithMessage("IME is not null").that(mInputMethodService).isNotNull(); // The activity gets focus. - assertThat(mActivity.hasWindowFocus()).isTrue(); - assertThat(mInputMethodService.getCurrentInputEditorInfo()).isNotNull(); - assertThat(mInputMethodService.getCurrentInputEditorInfo().packageName) - .isEqualTo(mTargetPackageName); - - // The editor won't bring up keyboard by default. - assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); - assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse(); + assertWithMessage("Activity window has input focus") + .that(mActivity.hasWindowFocus()).isTrue(); + final var editorInfo = mInputMethodService.getCurrentInputEditorInfo(); + assertWithMessage("EditorInfo is not null").that(editorInfo).isNotNull(); + assertWithMessage("EditorInfo package matches target package") + .that(editorInfo.packageName).isEqualTo(mTargetPackageName); + + assertWithMessage("Input connection is started") + .that(mInputMethodService.getCurrentInputStarted()).isTrue(); + // The editor won't bring up the IME by default. + assertWithMessage("IME is not shown during setup") + .that(mInputMethodService.getCurrentInputViewStarted()).isFalse(); }); // Save the original value of show_ime_with_hard_keyboard from Settings. mShowImeWithHardKeyboardEnabled = Settings.Secure.getInt( @@ -148,30 +154,63 @@ public class InputMethodServiceTest { * (i.e. tapping on an EditText, tapping the Home button). */ @Test - public void testShowHideKeyboard_byUserAction() throws Exception { + public void testShowHideKeyboard_byUserAction() { setShowImeWithHardKeyboard(true /* enabled */); - // Performs click on editor box to bring up the soft keyboard. - Log.i(TAG, "Click on EditText."); + // Performs click on EditText to bring up the IME. + Log.i(TAG, "Click on EditText"); verifyInputViewStatus( - () -> clickOnEditorText(), + this::clickOnEditText, true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); - // Press home key to hide soft keyboard. + // Press home key to hide IME. Log.i(TAG, "Press home"); if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { - assertThat(mUiDevice.pressHome()).isTrue(); + assertWithMessage("Home key press was handled").that(mUiDevice.pressHome()).isTrue(); // The IME visibility is only sent at the end of the animation. Therefore, we have to // wait until the visibility was sent to the server and the IME window hidden. - eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); + eventually(() -> assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse()); } else { verifyInputViewStatus( - () -> assertThat(mUiDevice.pressHome()).isTrue(), + () -> assertWithMessage("Home key press was handled") + .that(mUiDevice.pressHome()).isTrue(), true /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); + } + } + + /** + * This checks that the IME can be shown and hidden using the InputMethodManager APIs. + */ + @Test + public void testShowHideKeyboard_byInputMethodManager() { + setShowImeWithHardKeyboard(true /* enabled */); + + // Triggers to show IME via public API. + verifyInputViewStatusOnMainSync( + () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), + true /* expected */, + true /* inputViewStarted */); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); + + // Triggers to hide IME via public API. + verifyInputViewStatusOnMainSync( + () -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(), + true /* expected */, + false /* inputViewStarted */); + if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { + // The IME visibility is only sent at the end of the animation. Therefore, we have to + // wait until the visibility was sent to the server and the IME window hidden. + eventually(() -> assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse()); + } else { + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); } } @@ -179,39 +218,41 @@ public class InputMethodServiceTest { * This checks that the IME can be shown and hidden using the WindowInsetsController APIs. */ @Test - public void testShowHideKeyboard_byApi() throws Exception { + public void testShowHideKeyboard_byInsetsController() { setShowImeWithHardKeyboard(true /* enabled */); // Triggers to show IME via public API. verifyInputViewStatusOnMainSync( - () -> assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(), + () -> mActivity.showImeWithWindowInsetsController(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); // Triggers to hide IME via public API. verifyInputViewStatusOnMainSync( - () -> assertThat(mActivity.hideImeWithWindowInsetsController()).isTrue(), + () -> mActivity.hideImeWithWindowInsetsController(), true /* expected */, false /* inputViewStarted */); if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { // The IME visibility is only sent at the end of the animation. Therefore, we have to // wait until the visibility was sent to the server and the IME window hidden. - eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); + eventually(() -> assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse()); } else { - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); } } /** * This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf. * - * With the refactor in b/298172246, all calls to IMMS#{show,hide}MySoftInputLocked + * <p>With the refactor in b/298172246, all calls to IMMS#{show,hide}MySoftInputLocked * will be just apply the requested visibility (by using the callback). Therefore, we will * lose flags like HIDE_IMPLICIT_ONLY. */ @Test - public void testShowHideSelf() throws Exception { + public void testShowHideSelf() { setShowImeWithHardKeyboard(true /* enabled */); // IME request to show itself without any flags, expect shown. @@ -220,7 +261,7 @@ public class InputMethodServiceTest { () -> mInputMethodService.requestShowSelf(0 /* flags */), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); if (!mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect not hide (shown). @@ -230,7 +271,8 @@ public class InputMethodServiceTest { InputMethodManager.HIDE_IMPLICIT_ONLY), false /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is still shown after HIDE_IMPLICIT_ONLY") + .that(mInputMethodService.isInputViewShown()).isTrue(); } // IME request to hide itself without any flags, expect hidden. @@ -242,9 +284,11 @@ public class InputMethodServiceTest { if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { // The IME visibility is only sent at the end of the animation. Therefore, we have to // wait until the visibility was sent to the server and the IME window hidden. - eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); + eventually(() -> assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse()); } else { - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); } if (!mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { @@ -254,7 +298,8 @@ public class InputMethodServiceTest { () -> mInputMethodService.requestShowSelf(InputMethodManager.SHOW_IMPLICIT), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown with SHOW_IMPLICIT") + .that(mInputMethodService.isInputViewShown()).isTrue(); // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect hidden. Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)"); @@ -263,7 +308,8 @@ public class InputMethodServiceTest { InputMethodManager.HIDE_IMPLICIT_ONLY), true /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown after HIDE_IMPLICIT_ONLY") + .that(mInputMethodService.isInputViewShown()).isFalse(); } } @@ -272,53 +318,60 @@ public class InputMethodServiceTest { * when show_ime_with_hard_keyboard is enabled. */ @Test - public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() throws Exception { + public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() { setShowImeWithHardKeyboard(true /* enabled */); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; - eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); + eventually(() -> assertWithMessage("InputView should show with visible hardware keyboard") + .that(mInputMethodService.onEvaluateInputViewShown()).isTrue()); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; - eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); + eventually(() -> assertWithMessage("InputView should show without hardware keyboard") + .that(mInputMethodService.onEvaluateInputViewShown()).isTrue()); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES; - eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); + eventually(() -> assertWithMessage("InputView should show with hidden hardware keyboard") + .that(mInputMethodService.onEvaluateInputViewShown()).isTrue()); } /** - * This checks the return value of IMSonEvaluateInputViewShown, + * This checks the return value of IMS#onEvaluateInputViewShown, * when show_ime_with_hard_keyboard is disabled. */ @Test - public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() throws Exception { + public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() { setShowImeWithHardKeyboard(false /* enabled */); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; - eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isFalse()); + eventually(() -> + assertWithMessage("InputView should not show with visible hardware keyboard") + .that(mInputMethodService.onEvaluateInputViewShown()).isFalse()); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; - eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); + eventually(() -> assertWithMessage("InputView should show without hardware keyboard") + .that(mInputMethodService.onEvaluateInputViewShown()).isTrue()); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES; - eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); + eventually(() -> assertWithMessage("InputView should show with hidden hardware keyboard") + .that(mInputMethodService.onEvaluateInputViewShown()).isTrue()); } /** @@ -326,51 +379,53 @@ public class InputMethodServiceTest { * when IMS#onEvaluateInputViewShown returns false, results in the IME not being shown. */ @Test - public void testShowSoftInput_disableShowImeWithHardKeyboard() throws Exception { + public void testShowSoftInput_disableShowImeWithHardKeyboard() { setShowImeWithHardKeyboard(false /* enabled */); - // Simulate connecting a hard keyboard. + // Simulate connecting a hardware keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; - // When InputMethodService#onEvaluateInputViewShown() returns false, the Ime should not be + // When InputMethodService#onEvaluateInputViewShown() returns false, the IME should not be // shown no matter what the show flag is. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown after SHOW_IMPLICIT") + .that(mInputMethodService.isInputViewShown()).isFalse(); verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), false /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown after SHOW_EXPLICIT") + .that(mInputMethodService.isInputViewShown()).isFalse(); } /** * This checks that an explicit show request results in the IME being shown. */ @Test - public void testShowSoftInputExplicitly() throws Exception { + public void testShowSoftInputExplicitly() { setShowImeWithHardKeyboard(true /* enabled */); // When InputMethodService#onEvaluateInputViewShown() returns true and flag is EXPLICIT, the - // Ime should be shown. + // IME should be shown. verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); } /** * This checks that an implicit show request results in the IME being shown. */ @Test - public void testShowSoftInputImplicitly() throws Exception { + public void testShowSoftInputImplicitly() { setShowImeWithHardKeyboard(true /* enabled */); // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT, @@ -379,7 +434,7 @@ public class InputMethodServiceTest { mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); } /** @@ -387,73 +442,74 @@ public class InputMethodServiceTest { * and it should be shown in fullscreen mode, results in the IME being shown. */ @Test - public void testShowSoftInputExplicitly_fullScreenMode() throws Exception { + public void testShowSoftInputExplicitly_fullScreenMode() { setShowImeWithHardKeyboard(true /* enabled */); // Set orientation landscape to enable fullscreen mode. setOrientation(2); - eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse()); + eventually(() -> assertWithMessage("No longer in natural orientation") + .that(mUiDevice.isNaturalOrientation()).isFalse()); // Wait for the TestActivity to be recreated. - eventually(() -> - assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); + eventually(() -> assertWithMessage("Activity was re-created after rotation") + .that(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); // Get the new TestActivity. mActivity = TestActivity.getLastCreatedInstance(); - assertThat(mActivity).isNotNull(); - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); + assertWithMessage("Re-created activity is not null").that(mActivity).isNotNull(); // Wait for the new EditText to be served by InputMethodManager. - eventually(() -> assertThat( - imm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); + eventually(() -> assertWithMessage("Has an input connection to the re-created Activity") + .that(mImm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); } /** * This checks that an implicit show request when the IME is not previously shown, * and it should be shown in fullscreen mode, results in the IME not being shown. * - * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput + * <p>With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like * SHOW_IMPLICIT. */ @Test @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER) - public void testShowSoftInputImplicitly_fullScreenMode() throws Exception { + public void testShowSoftInputImplicitly_fullScreenMode() { setShowImeWithHardKeyboard(true /* enabled */); // Set orientation landscape to enable fullscreen mode. setOrientation(2); - eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse()); + eventually(() -> assertWithMessage("No longer in natural orientation") + .that(mUiDevice.isNaturalOrientation()).isFalse()); // Wait for the TestActivity to be recreated. - eventually(() -> - assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); + eventually(() -> assertWithMessage("Activity was re-created after rotation") + .that(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); // Get the new TestActivity. mActivity = TestActivity.getLastCreatedInstance(); - assertThat(mActivity).isNotNull(); - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); + assertWithMessage("Re-created activity is not null").that(mActivity).isNotNull(); // Wait for the new EditText to be served by InputMethodManager. - eventually(() -> assertThat( - imm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); + eventually(() -> assertWithMessage("Has an input connection to the re-created Activity") + .that(mImm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); } /** - * This checks that an explicit show request when a hard keyboard is connected, + * This checks that an explicit show request when a hardware keyboard is connected, * results in the IME being shown. */ @Test - public void testShowSoftInputExplicitly_withHardKeyboard() throws Exception { + public void testShowSoftInputExplicitly_withHardKeyboard() { setShowImeWithHardKeyboard(false /* enabled */); - // Simulate connecting a hard keyboard. + // Simulate connecting a hardware keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = @@ -463,23 +519,23 @@ public class InputMethodServiceTest { mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); } /** - * This checks that an implicit show request when a hard keyboard is connected, + * This checks that an implicit show request when a hardware keyboard is connected, * results in the IME not being shown. * - * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput + * <p>With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like * SHOW_IMPLICIT. */ @Test @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER) - public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception { + public void testShowSoftInputImplicitly_withHardKeyboard() { setShowImeWithHardKeyboard(false /* enabled */); - // Simulate connecting a hard keyboard. + // Simulate connecting a hardware keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = @@ -489,18 +545,19 @@ public class InputMethodServiceTest { mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); } /** - * This checks that an explicit show request followed by connecting a hard keyboard + * This checks that an explicit show request followed by connecting a hardware keyboard * and a configuration change, still results in the IME being shown. */ @Test - public void testShowSoftInputExplicitly_thenConfigurationChanged() throws Exception { + public void testShowSoftInputExplicitly_thenConfigurationChanged() { setShowImeWithHardKeyboard(false /* enabled */); - // Start with no hard keyboard. + // Start with no hardware keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = @@ -510,9 +567,9 @@ public class InputMethodServiceTest { () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); - // Simulate connecting a hard keyboard. + // Simulate connecting a hardware keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = @@ -526,24 +583,25 @@ public class InputMethodServiceTest { mInputMethodService.getResources().getConfiguration()), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is still shown after a configuration change") + .that(mInputMethodService.isInputViewShown()).isTrue(); } /** - * This checks that an implicit show request followed by connecting a hard keyboard + * This checks that an implicit show request followed by connecting a hardware keyboard * and a configuration change, does not trigger IMS#onFinishInputView, * but results in the IME being hidden. * - * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput + * <p>With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like * SHOW_IMPLICIT. */ @Test @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER) - public void testShowSoftInputImplicitly_thenConfigurationChanged() throws Exception { + public void testShowSoftInputImplicitly_thenConfigurationChanged() { setShowImeWithHardKeyboard(false /* enabled */); - // Start with no hard keyboard. + // Start with no hardware keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = @@ -553,9 +611,9 @@ public class InputMethodServiceTest { mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); - // Simulate connecting a hard keyboard. + // Simulate connecting a hardware keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().keyboard = @@ -574,7 +632,8 @@ public class InputMethodServiceTest { mInputMethodService.getResources().getConfiguration()), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown after a configuration change") + .that(mInputMethodService.isInputViewShown()).isFalse(); } /** @@ -583,11 +642,10 @@ public class InputMethodServiceTest { * (i.e. the implicit show request is treated as explicit). */ @Test - public void testShowSoftInputExplicitly_thenShowSoftInputImplicitly_withHardKeyboard() - throws Exception { + public void testShowSoftInputExplicitly_thenShowSoftInputImplicitly_withHardKeyboard() { setShowImeWithHardKeyboard(false /* enabled */); - // Simulate connecting a hard keyboard. + // Simulate connecting a hardware keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = @@ -598,23 +656,25 @@ public class InputMethodServiceTest { mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); // Implicit show request. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is still shown") + .that(mInputMethodService.isInputViewShown()).isTrue(); // Simulate a fake configuration change to avoid triggering the recreation of TestActivity. // This should now consider the implicit show request, but keep the state from the - // explicit show request, and thus not hide the keyboard. + // explicit show request, and thus not hide the IME. verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged( mInputMethodService.getResources().getConfiguration()), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is still shown after a configuration change") + .that(mInputMethodService.isInputViewShown()).isTrue(); } /** @@ -622,40 +682,42 @@ public class InputMethodServiceTest { * and then a hide not always request, still results in the IME being shown * (i.e. the explicit show request retains the forced state). * - * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput + * <p>With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like * HIDE_NOT_ALWAYS. */ @Test @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER) - public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways() - throws Exception { + public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways() { setShowImeWithHardKeyboard(true /* enabled */); verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_FORCED)).isTrue(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), false /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is still shown") + .that(mInputMethodService.isInputViewShown()).isTrue(); - verifyInputViewStatusOnMainSync(() -> - mActivity.hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS), + verifyInputViewStatusOnMainSync(() -> assertThat( + mActivity.hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS)) + .isTrue(), false /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is still shown after HIDE_NOT_ALWAYS") + .that(mInputMethodService.isInputViewShown()).isTrue(); } /** * This checks that the IME fullscreen mode state is updated after changing orientation. */ @Test - public void testFullScreenMode() throws Exception { + public void testFullScreenMode() { setShowImeWithHardKeyboard(true /* enabled */); Log.i(TAG, "Set orientation natural"); @@ -679,13 +741,13 @@ public class InputMethodServiceTest { * then the IME caption bar is also not created. */ @Test - public void testNoNavigationBar_thenImeNoCaptionBar() throws Exception { - boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() - .hasNavigationBar(mInputMethodService.getDisplayId()); - assumeFalse("Must not have a navigation bar", hasNavigationBar); + public void testNoNavigationBar_thenImeNoCaptionBar() { + assumeFalse("Must not have a navigation bar", hasNavigationBar()); - assertEquals(Insets.NONE, mInputMethodService.getWindow().getWindow().getDecorView() - .getRootWindowInsets().getInsetsIgnoringVisibility(captionBar())); + assertWithMessage("No IME caption bar insets") + .that(mInputMethodService.getWindow().getWindow().getDecorView() + .getRootWindowInsets().getInsetsIgnoringVisibility(captionBar())) + .isEqualTo(Insets.NONE); } /** @@ -693,112 +755,104 @@ public class InputMethodServiceTest { * when the IME does draw the IME navigation bar. */ @Test - public void testShowHideImeNavigationBar_doesDrawImeNavBar() throws Exception { - boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() - .hasNavigationBar(mInputMethodService.getDisplayId()); - assumeTrue("Must have a navigation bar", hasNavigationBar); + public void testShowHideImeNavigationBar_doesDrawImeNavBar() { + assumeTrue("Must have a navigation bar", hasNavigationBar()); setShowImeWithHardKeyboard(true /* enabled */); // Show IME verifyInputViewStatusOnMainSync( () -> { - mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged( - InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR - | InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN - ); - assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(); + setDrawsImeNavBarAndSwitcherButton(true /* enabled */); + mActivity.showImeWithWindowInsetsController(); }, true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME navigation bar is initially shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue(); // Try to hide IME nav bar - mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow() - .getInsetsController().hide(captionBar())); + mInstrumentation.runOnMainSync(() -> setShowImeNavigationBar(false /* show */)); mInstrumentation.waitForIdleSync(); - assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); + assertWithMessage("IME navigation bar is not shown after hide request") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); // Try to show IME nav bar - mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow() - .getInsetsController().show(captionBar())); + mInstrumentation.runOnMainSync(() -> setShowImeNavigationBar(true /* show */)); mInstrumentation.waitForIdleSync(); - assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue(); + assertWithMessage("IME navigation bar is shown after show request") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue(); } + /** * This checks that trying to show and hide the navigation bar has no effect * when the IME does not draw the IME navigation bar. * - * Note: The IME navigation bar is *never* visible in 3 button navigation mode. + * <p>Note: The IME navigation bar is *never* visible in 3 button navigation mode. */ @Test - public void testShowHideImeNavigationBar_doesNotDrawImeNavBar() throws Exception { - boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() - .hasNavigationBar(mInputMethodService.getDisplayId()); - assumeTrue("Must have a navigation bar", hasNavigationBar); + public void testShowHideImeNavigationBar_doesNotDrawImeNavBar() { + assumeTrue("Must have a navigation bar", hasNavigationBar()); setShowImeWithHardKeyboard(true /* enabled */); // Show IME - verifyInputViewStatusOnMainSync(() -> { - mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged( - 0 /* navButtonFlags */); - assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(); - }, + verifyInputViewStatusOnMainSync( + () -> { + setDrawsImeNavBarAndSwitcherButton(false /* enabled */); + mActivity.showImeWithWindowInsetsController(); + }, true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME navigation bar is initially not shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); // Try to hide IME nav bar - mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow() - .getInsetsController().hide(captionBar())); + mInstrumentation.runOnMainSync(() -> setShowImeNavigationBar(false /* show */)); mInstrumentation.waitForIdleSync(); - assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); + assertWithMessage("IME navigation bar is not shown after hide request") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); // Try to show IME nav bar - mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow() - .getInsetsController().show(captionBar())); + mInstrumentation.runOnMainSync(() -> setShowImeNavigationBar(true /* show */)); mInstrumentation.waitForIdleSync(); - assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); + assertWithMessage("IME navigation bar is not shown after show request") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); } /** * Verifies that clicking on the IME navigation bar back button hides the IME. */ @Test - public void testBackButtonClick() throws Exception { - boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() - .hasNavigationBar(mInputMethodService.getDisplayId()); - assumeTrue("Must have a navigation bar", hasNavigationBar); + public void testBackButtonClick() { + assumeTrue("Must have a navigation bar", hasNavigationBar()); assumeTrue("Must be in gesture navigation mode", isGestureNavEnabled()); setShowImeWithHardKeyboard(true /* enabled */); verifyInputViewStatusOnMainSync( () -> { - // Ensure the IME navigation bar and the IME switch button are drawn. - mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged( - InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR - | InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN - ); - assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(); + setDrawsImeNavBarAndSwitcherButton(true /* enabled */); + mActivity.showImeWithWindowInsetsController(); }, true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); - final var backButtonUiObject = getUiObjectById(INPUT_METHOD_NAV_BACK_ID); + final var backButtonUiObject = getUiObject(By.res(INPUT_METHOD_NAV_BACK_ID)); backButtonUiObject.click(); mInstrumentation.waitForIdleSync(); if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { // The IME visibility is only sent at the end of the animation. Therefore, we have to // wait until the visibility was sent to the server and the IME window hidden. - eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); + eventually(() -> assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse()); } else { - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); } } @@ -806,37 +860,33 @@ public class InputMethodServiceTest { * Verifies that long clicking on the IME navigation bar back button hides the IME. */ @Test - public void testBackButtonLongClick() throws Exception { - boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() - .hasNavigationBar(mInputMethodService.getDisplayId()); - assumeTrue("Must have a navigation bar", hasNavigationBar); + public void testBackButtonLongClick() { + assumeTrue("Must have a navigation bar", hasNavigationBar()); assumeTrue("Must be in gesture navigation mode", isGestureNavEnabled()); setShowImeWithHardKeyboard(true /* enabled */); verifyInputViewStatusOnMainSync( () -> { - // Ensure the IME navigation bar and the IME switch button are drawn. - mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged( - InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR - | InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN - ); - assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(); + setDrawsImeNavBarAndSwitcherButton(true /* enabled */); + mActivity.showImeWithWindowInsetsController(); }, true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); - final var backButtonUiObject = getUiObjectById(INPUT_METHOD_NAV_BACK_ID); + final var backButtonUiObject = getUiObject(By.res(INPUT_METHOD_NAV_BACK_ID)); backButtonUiObject.longClick(); mInstrumentation.waitForIdleSync(); if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { // The IME visibility is only sent at the end of the animation. Therefore, we have to // wait until the visibility was sent to the server and the IME window hidden. - eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); + eventually(() -> assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse()); } else { - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); } } @@ -845,43 +895,37 @@ public class InputMethodServiceTest { * or switches the input method. */ @Test - public void testImeSwitchButtonClick() throws Exception { - boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() - .hasNavigationBar(mInputMethodService.getDisplayId()); - assumeTrue("Must have a navigation bar", hasNavigationBar); + public void testImeSwitchButtonClick() { + assumeTrue("Must have a navigation bar", hasNavigationBar()); assumeTrue("Must be in gesture navigation mode", isGestureNavEnabled()); setShowImeWithHardKeyboard(true /* enabled */); verifyInputViewStatusOnMainSync( () -> { - // Ensure the IME navigation bar and the IME switch button are drawn. - mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged( - InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR - | InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN - ); - assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(); + setDrawsImeNavBarAndSwitcherButton(true /* enabled */); + mActivity.showImeWithWindowInsetsController(); }, true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); - final var imm = mContext.getSystemService(InputMethodManager.class); - final var initialInfo = imm.getCurrentInputMethodInfo(); + final var initialInfo = mImm.getCurrentInputMethodInfo(); - final var imeSwitchButtonUiObject = getUiObjectById(INPUT_METHOD_NAV_IME_SWITCHER_ID); + final var imeSwitchButtonUiObject = getUiObject(By.res(INPUT_METHOD_NAV_IME_SWITCHER_ID)); imeSwitchButtonUiObject.click(); mInstrumentation.waitForIdleSync(); - final var newInfo = imm.getCurrentInputMethodInfo(); + final var newInfo = mImm.getCurrentInputMethodInfo(); assertWithMessage("Input Method Switcher Menu is shown or input method was switched") - .that(isInputMethodPickerShown(imm) || !Objects.equals(initialInfo, newInfo)) + .that(isInputMethodPickerShown(mImm) || !Objects.equals(initialInfo, newInfo)) .isTrue(); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is still shown after IME Switcher button was clicked") + .that(mInputMethodService.isInputViewShown()).isTrue(); - // Hide the Picker menu before finishing. + // Hide the IME Switcher Menu before finishing. mUiDevice.pressBack(); } @@ -889,58 +933,42 @@ public class InputMethodServiceTest { * Verifies that long clicking on the IME switch button shows the Input Method Switcher Menu. */ @Test - public void testImeSwitchButtonLongClick() throws Exception { - boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() - .hasNavigationBar(mInputMethodService.getDisplayId()); - assumeTrue("Must have a navigation bar", hasNavigationBar); + public void testImeSwitchButtonLongClick() { + assumeTrue("Must have a navigation bar", hasNavigationBar()); assumeTrue("Must be in gesture navigation mode", isGestureNavEnabled()); setShowImeWithHardKeyboard(true /* enabled */); verifyInputViewStatusOnMainSync( () -> { - // Ensure the IME navigation bar and the IME switch button are drawn. - mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged( - InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR - | InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN - ); - assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(); + setDrawsImeNavBarAndSwitcherButton(true /* enabled */); + mActivity.showImeWithWindowInsetsController(); }, true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); - final var imm = mContext.getSystemService(InputMethodManager.class); - - final var imeSwitchButtonUiObject = getUiObjectById(INPUT_METHOD_NAV_IME_SWITCHER_ID); + final var imeSwitchButtonUiObject = getUiObject(By.res(INPUT_METHOD_NAV_IME_SWITCHER_ID)); imeSwitchButtonUiObject.longClick(); mInstrumentation.waitForIdleSync(); assertWithMessage("Input Method Switcher Menu is shown") - .that(isInputMethodPickerShown(imm)) - .isTrue(); - if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { - // The IME visibility is only sent at the end of the animation. Therefore, we have to - // wait until the visibility was sent to the server and the IME window hidden. - eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); - } else { - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - } + .that(isInputMethodPickerShown(mImm)).isTrue(); + assertWithMessage("IME is still shown after IME Switcher button was long clicked") + .that(mInputMethodService.isInputViewShown()).isTrue(); - // Hide the Picker menu before finishing. + // Hide the IME Switcher Menu before finishing. mUiDevice.pressBack(); } - private void verifyInputViewStatus( - Runnable runnable, boolean expected, boolean inputViewStarted) - throws InterruptedException { + private void verifyInputViewStatus(@NonNull Runnable runnable, boolean expected, + boolean inputViewStarted) { verifyInputViewStatusInternal(runnable, expected, inputViewStarted, false /* runOnMainSync */); } - private void verifyInputViewStatusOnMainSync( - Runnable runnable, boolean expected, boolean inputViewStarted) - throws InterruptedException { + private void verifyInputViewStatusOnMainSync(@NonNull Runnable runnable, boolean expected, + boolean inputViewStarted) { verifyInputViewStatusInternal(runnable, expected, inputViewStarted, true /* runOnMainSync */); } @@ -948,24 +976,32 @@ public class InputMethodServiceTest { /** * Verifies the status of the Input View after executing the given runnable. * - * @param runnable the runnable to execute for showing or hiding the IME. - * @param expected whether the runnable is expected to trigger the signal. + * @param runnable the runnable to execute for showing or hiding the IME. + * @param expected whether the runnable is expected to trigger the signal. * @param inputViewStarted the expected state of the Input View after executing the runnable. - * @param runOnMainSync whether to execute the runnable on the main thread. + * @param runOnMainSync whether to execute the runnable on the main thread. */ - private void verifyInputViewStatusInternal( - Runnable runnable, boolean expected, boolean inputViewStarted, boolean runOnMainSync) - throws InterruptedException { - CountDownLatch signal = new CountDownLatch(1); - mInputMethodService.setCountDownLatchForTesting(signal); - // Runnable to trigger onStartInputView() / onFinishInputView() / onConfigurationChanged() - if (runOnMainSync) { - mInstrumentation.runOnMainSync(runnable); - } else { - runnable.run(); + private void verifyInputViewStatusInternal(@NonNull Runnable runnable, boolean expected, + boolean inputViewStarted, boolean runOnMainSync) { + final boolean completed; + try { + final var latch = new CountDownLatch(1); + mInputMethodService.setCountDownLatchForTesting(latch); + // Trigger onStartInputView() / onFinishInputView() / onConfigurationChanged() + if (runOnMainSync) { + mInstrumentation.runOnMainSync(runnable); + } else { + runnable.run(); + } + mInstrumentation.waitForIdleSync(); + completed = latch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail("Interrupted while waiting for latch: " + e.getMessage()); + return; + } finally { + mInputMethodService.setCountDownLatchForTesting(null); } - mInstrumentation.waitForIdleSync(); - boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); + if (expected && !completed) { fail("Timed out waiting for" + " onStartInputView() / onFinishInputView() / onConfigurationChanged()"); @@ -974,8 +1010,10 @@ public class InputMethodServiceTest { + " onStartInputView() / onFinishInputView() / onConfigurationChanged()"); } // Input is not finished. - assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); - assertThat(mInputMethodService.getCurrentInputViewStarted()).isEqualTo(inputViewStarted); + assertWithMessage("Input connection is still started") + .that(mInputMethodService.getCurrentInputStarted()).isTrue(); + assertWithMessage("IME visibility matches expected") + .that(mInputMethodService.getCurrentInputViewStarted()).isEqualTo(inputViewStarted); } private void setOrientation(int orientation) { @@ -999,37 +1037,45 @@ public class InputMethodServiceTest { /** * Verifies the IME fullscreen mode state after executing the given runnable. * - * @param runnable the runnable to execute for setting the orientation. - * @param expected whether the runnable is expected to trigger the signal. + * @param runnable the runnable to execute for setting the orientation. + * @param expected whether the runnable is expected to trigger the signal. * @param orientationPortrait whether the orientation is expected to be portrait. */ private void verifyFullscreenMode(@NonNull Runnable runnable, boolean expected, - boolean orientationPortrait) throws InterruptedException { + boolean orientationPortrait) { verifyInputViewStatus(runnable, expected, false /* inputViewStarted */); if (expected) { // Wait for the TestActivity to be recreated. - eventually(() -> - assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); + eventually(() -> assertWithMessage("Activity was re-created after rotation") + .that(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); // Get the new TestActivity. mActivity = TestActivity.getLastCreatedInstance(); + assertWithMessage("Re-created activity is not null").that(mActivity).isNotNull(); } verifyInputViewStatusOnMainSync( () -> mActivity.showImeWithWindowInsetsController(), true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue(); - assertThat(mInputMethodService.getResources().getConfiguration().orientation) + assertWithMessage("IME orientation matches expected") + .that(mInputMethodService.getResources().getConfiguration().orientation) .isEqualTo(orientationPortrait ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE); - EditorInfo editorInfo = mInputMethodService.getCurrentInputEditorInfo(); - assertThat(editorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN).isEqualTo(0); - assertThat(editorInfo.internalImeOptions & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) + final var editorInfo = mInputMethodService.getCurrentInputEditorInfo(); + assertWithMessage("IME_FLAG_NO_FULLSCREEN not set") + .that(editorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN).isEqualTo(0); + assertWithMessage("IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT matches expected") + .that(editorInfo.internalImeOptions + & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) .isEqualTo( orientationPortrait ? EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT : 0); - assertThat(mInputMethodService.onEvaluateFullscreenMode()).isEqualTo(!orientationPortrait); - assertThat(mInputMethodService.isFullscreenMode()).isEqualTo(!orientationPortrait); + assertWithMessage("onEvaluateFullscreenMode matches orientation") + .that(mInputMethodService.onEvaluateFullscreenMode()) + .isEqualTo(!orientationPortrait); + assertWithMessage("isFullscreenMode matches orientation") + .that(mInputMethodService.isFullscreenMode()).isEqualTo(!orientationPortrait); // Hide IME before finishing the run. verifyInputViewStatusOnMainSync( @@ -1040,24 +1086,27 @@ public class InputMethodServiceTest { if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { // The IME visibility is only sent at the end of the animation. Therefore, we have to // wait until the visibility was sent to the server and the IME window hidden. - eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); + eventually(() -> assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse()); } else { - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + assertWithMessage("IME is not shown") + .that(mInputMethodService.isInputViewShown()).isFalse(); } } - private void prepareIme() throws Exception { + private void prepareIme() { executeShellCommand("ime enable " + mInputMethodId); executeShellCommand("ime set " + mInputMethodId); mInstrumentation.waitForIdleSync(); Log.i(TAG, "Finish preparing IME"); } - private void prepareEditor() { - mActivity = TestActivity.start(mInstrumentation); + private void prepareActivity() { + mActivity = TestActivity.startSync(mInstrumentation); Log.i(TAG, "Finish preparing activity with editor."); } + @NonNull private String getInputMethodId() { return mTargetPackageName + "/" + INPUT_METHOD_SERVICE_NAME; } @@ -1067,7 +1116,7 @@ public class InputMethodServiceTest { * * @param enabled the value to be set. */ - private void setShowImeWithHardKeyboard(boolean enabled) throws IOException { + private void setShowImeWithHardKeyboard(boolean enabled) { if (mShowImeWithHardKeyboardEnabled != enabled) { executeShellCommand(enabled ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD @@ -1076,9 +1125,9 @@ public class InputMethodServiceTest { } } - private String executeShellCommand(String cmd) throws IOException { + private static void executeShellCommand(@NonNull String cmd) { Log.i(TAG, "Run command: " + cmd); - return SystemUtil.runShellCommandOrThrow(cmd); + SystemUtil.runShellCommandOrThrow(cmd); } /** @@ -1090,31 +1139,63 @@ public class InputMethodServiceTest { } @NonNull - private UiObject2 getUiObjectById(@NonNull String id) { - final var uiObject = mUiDevice.wait( - Until.findObject(By.res(id)), + private UiObject2 getUiObject(@NonNull BySelector bySelector) { + final var uiObject = mUiDevice.wait(Until.findObject(bySelector), TimeUnit.SECONDS.toMillis(TIMEOUT_IN_SECONDS)); - assertThat(uiObject).isNotNull(); + assertWithMessage("UiObject with " + bySelector + " was found").that(uiObject).isNotNull(); return uiObject; } - /** - * Returns {@code true} if the navigation mode is gesture nav, and {@code false} otherwise. - */ + /** Checks whether gesture navigation move is enabled. */ private boolean isGestureNavEnabled() { return mContext.getResources().getInteger( com.android.internal.R.integer.config_navBarInteractionMode) == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; } - private void clickOnEditorText() { + /** Checks whether the device has a navigation bar on the IME's display. */ + private boolean hasNavigationBar() { + try { + return WindowManagerGlobal.getWindowManagerService() + .hasNavigationBar(mInputMethodService.getDisplayId()); + } catch (RemoteException e) { + fail("Failed to check whether the device has a navigation bar: " + e.getMessage()); + return false; + } + } + + /** + * Manually sets whether the IME draws the IME navigation bar and IME Switcher button, + * regardless of the current navigation bar mode. + * + * <p>Note, neither of these are normally drawn when in three button navigation mode. + * + * @param enabled whether the IME nav bar and IME Switcher button are drawn. + */ + private void setDrawsImeNavBarAndSwitcherButton(boolean enabled) { + final int flags = enabled ? IME_DRAWS_IME_NAV_BAR | SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0; + mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged(flags); + } + + /** + * Set whether the IME navigation bar should be shown or not. + * + * <p>Note, this has no effect when the IME does not draw the IME navigation bar. + * + * @param show whether the IME navigation bar should be shown. + */ + private void setShowImeNavigationBar(boolean show) { + final var controller = mInputMethodService.getWindow().getWindow().getInsetsController(); + if (show) { + controller.show(captionBar()); + } else { + controller.hide(captionBar()); + } + } + + private void clickOnEditText() { // Find the editText and click it. - UiObject2 editTextUiObject = - mUiDevice.wait( - Until.findObject(By.desc(EDIT_TEXT_DESC)), - TimeUnit.SECONDS.toMillis(TIMEOUT_IN_SECONDS)); - assertThat(editTextUiObject).isNotNull(); - editTextUiObject.click(); + getUiObject(By.desc(EDIT_TEXT_DESC)).click(); mInstrumentation.waitForIdleSync(); } } diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/SimpleInputMethodService.java b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/SimpleInputMethodService.java index 48942a31658f..61d1bb931a96 100644 --- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/SimpleInputMethodService.java +++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/SimpleInputMethodService.java @@ -23,13 +23,15 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; import android.widget.FrameLayout; +import androidx.annotation.NonNull; + import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; -/** The {@link InputMethodService} implementation for SimpeTestIme app. */ -public class SimpleInputMethodService extends InputMethodServiceWrapper { +/** The {@link InputMethodService} implementation for SimpleTestIme app. */ +public final class SimpleInputMethodService extends InputMethodServiceWrapper { + private static final String TAG = "SimpleIMS"; private FrameLayout mInputView; @@ -45,23 +47,18 @@ public class SimpleInputMethodService extends InputMethodServiceWrapper { public void onStartInputView(EditorInfo info, boolean restarting) { super.onStartInputView(info, restarting); mInputView.removeAllViews(); - SimpleKeyboard keyboard = new SimpleKeyboard(this, R.layout.qwerty_10_9_9); + final var keyboard = new SimpleKeyboard(this, R.layout.qwerty_10_9_9); mInputView.addView(keyboard.inflateKeyboardView(LayoutInflater.from(this), mInputView)); } - void handle(String data, int keyboardState) { - InputConnection inputConnection = getCurrentInputConnection(); - Integer keyCode = KeyCodeConstants.KEY_NAME_TO_CODE_MAP.get(data); + void handleKeyPress(@NonNull String keyCodeName, int keyboardState) { + final Integer keyCode = KeyCodeConstants.KEY_NAME_TO_CODE_MAP.get(keyCodeName); Log.v(TAG, "keyCode: " + keyCode); if (keyCode != null) { - inputConnection.sendKeyEvent( - new KeyEvent( - SystemClock.uptimeMillis(), - SystemClock.uptimeMillis(), - KeyEvent.ACTION_DOWN, - keyCode, - 0, - KeyCodeConstants.isAlphaKeyCode(keyCode) ? keyboardState : 0)); + final var downTime = SystemClock.uptimeMillis(); + getCurrentInputConnection().sendKeyEvent(new KeyEvent(downTime, downTime, + KeyEvent.ACTION_DOWN, keyCode, 0 /* repeat */, + KeyCodeConstants.isAlphaKeyCode(keyCode) ? keyboardState : 0) /* metaState */); } } } diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/SimpleKeyboard.java b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/SimpleKeyboard.java index b16ec9ebd718..62d90c8d56b5 100644 --- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/SimpleKeyboard.java +++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/SimpleKeyboard.java @@ -25,60 +25,65 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + /** Controls the visible virtual keyboard view. */ final class SimpleKeyboard { + private static final String TAG = "SimpleKeyboard"; - private static final int[] SOFT_KEY_IDS = - new int[] { - R.id.key_pos_0_0, - R.id.key_pos_0_1, - R.id.key_pos_0_2, - R.id.key_pos_0_3, - R.id.key_pos_0_4, - R.id.key_pos_0_5, - R.id.key_pos_0_6, - R.id.key_pos_0_7, - R.id.key_pos_0_8, - R.id.key_pos_0_9, - R.id.key_pos_1_0, - R.id.key_pos_1_1, - R.id.key_pos_1_2, - R.id.key_pos_1_3, - R.id.key_pos_1_4, - R.id.key_pos_1_5, - R.id.key_pos_1_6, - R.id.key_pos_1_7, - R.id.key_pos_1_8, - R.id.key_pos_2_0, - R.id.key_pos_2_1, - R.id.key_pos_2_2, - R.id.key_pos_2_3, - R.id.key_pos_2_4, - R.id.key_pos_2_5, - R.id.key_pos_2_6, - R.id.key_pos_shift, - R.id.key_pos_del, - R.id.key_pos_symbol, - R.id.key_pos_comma, - R.id.key_pos_space, - R.id.key_pos_period, - R.id.key_pos_enter, - }; + private static final int[] SOFT_KEY_IDS = new int[]{ + R.id.key_pos_0_0, + R.id.key_pos_0_1, + R.id.key_pos_0_2, + R.id.key_pos_0_3, + R.id.key_pos_0_4, + R.id.key_pos_0_5, + R.id.key_pos_0_6, + R.id.key_pos_0_7, + R.id.key_pos_0_8, + R.id.key_pos_0_9, + R.id.key_pos_1_0, + R.id.key_pos_1_1, + R.id.key_pos_1_2, + R.id.key_pos_1_3, + R.id.key_pos_1_4, + R.id.key_pos_1_5, + R.id.key_pos_1_6, + R.id.key_pos_1_7, + R.id.key_pos_1_8, + R.id.key_pos_2_0, + R.id.key_pos_2_1, + R.id.key_pos_2_2, + R.id.key_pos_2_3, + R.id.key_pos_2_4, + R.id.key_pos_2_5, + R.id.key_pos_2_6, + R.id.key_pos_shift, + R.id.key_pos_del, + R.id.key_pos_symbol, + R.id.key_pos_comma, + R.id.key_pos_space, + R.id.key_pos_period, + R.id.key_pos_enter, + }; + @NonNull private final SimpleInputMethodService mSimpleInputMethodService; private final int mViewResId; private final SparseArray<TextView> mSoftKeyViews = new SparseArray<>(); private View mKeyboardView; private int mKeyboardState; - SimpleKeyboard(SimpleInputMethodService simpleInputMethodService, int viewResId) { - this.mSimpleInputMethodService = simpleInputMethodService; - this.mViewResId = viewResId; - this.mKeyboardState = 0; + SimpleKeyboard(@NonNull SimpleInputMethodService simpleInputMethodService, int viewResId) { + mSimpleInputMethodService = simpleInputMethodService; + mViewResId = viewResId; + mKeyboardState = 0; } - View inflateKeyboardView(LayoutInflater inflater, ViewGroup inputView) { + @NonNull + View inflateKeyboardView(@NonNull LayoutInflater inflater, @NonNull ViewGroup inputView) { mKeyboardView = inflater.inflate(mViewResId, inputView, false); mapSoftKeys(); return mKeyboardView; @@ -86,30 +91,31 @@ final class SimpleKeyboard { private void mapSoftKeys() { for (int id : SOFT_KEY_IDS) { - TextView softKeyView = mKeyboardView.findViewById(id); + final TextView softKeyView = mKeyboardView.requireViewById(id); mSoftKeyViews.put(id, softKeyView); - String tagData = softKeyView.getTag() != null ? softKeyView.getTag().toString() : null; - softKeyView.setOnClickListener(v -> handle(tagData)); + final var keyCodeName = softKeyView.getTag() != null + ? softKeyView.getTag().toString() : null; + softKeyView.setOnClickListener(v -> handleKeyPress(keyCodeName)); } } - private void handle(String data) { - Log.i(TAG, "handle(): " + data); - if (TextUtils.isEmpty(data)) { + private void handleKeyPress(@Nullable String keyCodeName) { + Log.i(TAG, "handle(): " + keyCodeName); + if (TextUtils.isEmpty(keyCodeName)) { return; } - if ("KEYCODE_SHIFT".equals(data)) { + if ("KEYCODE_SHIFT".equals(keyCodeName)) { handleShift(); return; } - mSimpleInputMethodService.handle(data, mKeyboardState); + mSimpleInputMethodService.handleKeyPress(keyCodeName, mKeyboardState); } private void handleShift() { mKeyboardState = toggleShiftState(mKeyboardState); Log.v(TAG, "currentKeyboardState: " + mKeyboardState); - boolean isShiftOn = isShiftOn(mKeyboardState); + final boolean isShiftOn = isShiftOn(mKeyboardState); for (int i = 0; i < mSoftKeyViews.size(); i++) { TextView softKeyView = mSoftKeyViews.valueAt(i); softKeyView.setAllCaps(isShiftOn); diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java index 67212b60f1b8..be59dd2cb7a3 100644 --- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java +++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java @@ -25,21 +25,35 @@ import java.util.concurrent.CountDownLatch; /** Wrapper of {@link InputMethodService} to expose interfaces for testing purpose. */ public class InputMethodServiceWrapper extends InputMethodService { - private static final String TAG = "InputMethodServiceWrapper"; - private static InputMethodServiceWrapper sInputMethodServiceWrapper; + private static final String TAG = "InputMethodServiceWrapper"; - public static InputMethodServiceWrapper getInputMethodServiceWrapperForTesting() { - return sInputMethodServiceWrapper; - } + /** Last created instance of this wrapper. */ + private static InputMethodServiceWrapper sInstance; private boolean mInputViewStarted; + + /** + * @see #setCountDownLatchForTesting + */ private CountDownLatch mCountDownLatchForTesting; + /** Gets the last created instance of this wrapper, if available. */ + public static InputMethodServiceWrapper getInstance() { + return sInstance; + } + public boolean getCurrentInputViewStarted() { return mInputViewStarted; } + /** + * Sets the latch used to wait for the IME to start showing ({@link #onStartInputView}, + * start hiding ({@link #onFinishInputView}) or receive a configuration change + * ({@link #onConfigurationChanged}). + * + * @param countDownLatchForTesting the latch to wait on. + */ public void setCountDownLatchForTesting(CountDownLatch countDownLatchForTesting) { mCountDownLatchForTesting = countDownLatchForTesting; } @@ -48,7 +62,7 @@ public class InputMethodServiceWrapper extends InputMethodService { public void onCreate() { Log.i(TAG, "onCreate()"); super.onCreate(); - sInputMethodServiceWrapper = this; + sInstance = this; } @Override @@ -102,12 +116,13 @@ public class InputMethodServiceWrapper extends InputMethodService { } private String dumpEditorInfo(EditorInfo info) { - var sb = new StringBuilder(); - sb.append("EditorInfo{packageName=").append(info.packageName); - sb.append(" fieldId=").append(info.fieldId); - sb.append(" hintText=").append(info.hintText); - sb.append(" privateImeOptions=").append(info.privateImeOptions); - sb.append("}"); - return sb.toString(); + if (info == null) { + return "null"; + } + return "EditorInfo{packageName=" + info.packageName + + " fieldId=" + info.fieldId + + " hintText=" + info.hintText + + " privateImeOptions=" + info.privateImeOptions + + "}"; } } diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/testing/TestActivity.java b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/testing/TestActivity.java index 37b05ff79977..5a73ed5b25e1 100644 --- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/testing/TestActivity.java +++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/testing/TestActivity.java @@ -25,12 +25,12 @@ import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.WindowInsets; -import android.view.WindowInsetsController; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.LinearLayout; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.lang.ref.WeakReference; @@ -42,10 +42,10 @@ import java.lang.ref.WeakReference; * in the instruments package. More details see {@link * Instrumentation#startActivitySync(Intent)}.</> */ -public class TestActivity extends Activity { +public final class TestActivity extends Activity { + private static final String TAG = "TestActivity"; - private static WeakReference<TestActivity> sLastCreatedInstance = - new WeakReference<>(null); + private static WeakReference<TestActivity> sLastCreatedInstance = new WeakReference<>(null); /** * Start a new test activity with an editor and wait for it to begin running before returning. @@ -53,13 +53,13 @@ public class TestActivity extends Activity { * @param instrumentation application instrumentation * @return the newly started activity */ - public static TestActivity start(Instrumentation instrumentation) { - Intent intent = - new Intent() - .setAction(Intent.ACTION_MAIN) - .setClass(instrumentation.getTargetContext(), TestActivity.class) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + @NonNull + public static TestActivity startSync(@NonNull Instrumentation instrumentation) { + final var intent = new Intent() + .setAction(Intent.ACTION_MAIN) + .setClass(instrumentation.getTargetContext(), TestActivity.class) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); return (TestActivity) instrumentation.startActivitySync(intent); } @@ -70,10 +70,10 @@ public class TestActivity extends Activity { } @Override - protected void onCreate(Bundle savedInstanceState) { + protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - LinearLayout rootView = new LinearLayout(this); + final var rootView = new LinearLayout(this); mEditText = new EditText(this); mEditText.setContentDescription("Input box"); rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); @@ -83,40 +83,39 @@ public class TestActivity extends Activity { sLastCreatedInstance = new WeakReference<>(this); } - /** Get the last created TestActivity instance. */ - public static @Nullable TestActivity getLastCreatedInstance() { + /** Get the last created TestActivity instance, if available. */ + @Nullable + public static TestActivity getLastCreatedInstance() { return sLastCreatedInstance.get(); } /** Shows soft keyboard via InputMethodManager. */ public boolean showImeWithInputMethodManager(int flags) { - InputMethodManager imm = getSystemService(InputMethodManager.class); - boolean result = imm.showSoftInput(mEditText, flags); + final var imm = getSystemService(InputMethodManager.class); + final boolean result = imm.showSoftInput(mEditText, flags); Log.i(TAG, "showIme() via InputMethodManager, result=" + result); return result; } /** Shows soft keyboard via WindowInsetsController. */ - public boolean showImeWithWindowInsetsController() { - WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController(); - windowInsetsController.show(WindowInsets.Type.ime()); + public void showImeWithWindowInsetsController() { + final var controller = mEditText.getWindowInsetsController(); + controller.show(WindowInsets.Type.ime()); Log.i(TAG, "showIme() via WindowInsetsController"); - return true; } /** Hides soft keyboard via InputMethodManager. */ public boolean hideImeWithInputMethodManager(int flags) { - InputMethodManager imm = getSystemService(InputMethodManager.class); - boolean result = imm.hideSoftInputFromWindow(mEditText.getWindowToken(), flags); + final var imm = getSystemService(InputMethodManager.class); + final boolean result = imm.hideSoftInputFromWindow(mEditText.getWindowToken(), flags); Log.i(TAG, "hideIme() via InputMethodManager, result=" + result); return result; } /** Hides soft keyboard via WindowInsetsController. */ - public boolean hideImeWithWindowInsetsController() { - WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController(); - windowInsetsController.hide(WindowInsets.Type.ime()); + public void hideImeWithWindowInsetsController() { + final var controller = mEditText.getWindowInsetsController(); + controller.hide(WindowInsets.Type.ime()); Log.i(TAG, "hideIme() via WindowInsetsController"); - return true; } } |