Loading services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +175 −16 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.app.Instrumentation; import android.content.Context; import android.content.Context; import android.content.res.Configuration; import android.content.res.Configuration; import android.os.RemoteException; import android.os.RemoteException; import android.provider.Settings; import android.support.test.uiautomator.By; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.UiObject2; Loading @@ -31,7 +32,6 @@ import android.support.test.uiautomator.Until; import android.util.Log; import android.util.Log; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest; Loading @@ -40,6 +40,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; import com.android.apps.inputmethod.simpleime.testing.TestActivity; import com.android.apps.inputmethod.simpleime.testing.TestActivity; import org.junit.After; import org.junit.Before; import org.junit.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; Loading @@ -54,15 +55,19 @@ public class InputMethodServiceTest { private static final String INPUT_METHOD_SERVICE_NAME = ".SimpleInputMethodService"; private static final String INPUT_METHOD_SERVICE_NAME = ".SimpleInputMethodService"; private static final String EDIT_TEXT_DESC = "Input box"; private static final String EDIT_TEXT_DESC = "Input box"; private static final long TIMEOUT_IN_SECONDS = 3; private static final long TIMEOUT_IN_SECONDS = 3; private static final String ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD = public Instrumentation mInstrumentation; "settings put secure show_ime_with_hard_keyboard 1"; public UiDevice mUiDevice; private static final String DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD = public Context mContext; "settings put secure show_ime_with_hard_keyboard 0"; public String mTargetPackageName; public TestActivity mActivity; private Instrumentation mInstrumentation; public EditText mEditText; private UiDevice mUiDevice; public InputMethodServiceWrapper mInputMethodService; private Context mContext; public String mInputMethodId; private String mTargetPackageName; private TestActivity mActivity; private InputMethodServiceWrapper mInputMethodService; private String mInputMethodId; private boolean mShowImeWithHardKeyboardEnabled; @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { Loading @@ -73,7 +78,9 @@ public class InputMethodServiceTest { mInputMethodId = getInputMethodId(); mInputMethodId = getInputMethodId(); prepareIme(); prepareIme(); prepareEditor(); prepareEditor(); mInstrumentation.waitForIdleSync(); mUiDevice.freezeRotation(); mUiDevice.setOrientationNatural(); // Waits for input binding ready. // Waits for input binding ready. eventually( eventually( () -> { () -> { Loading @@ -85,6 +92,28 @@ public class InputMethodServiceTest { assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse(); assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse(); }); }); // Save the original value of show_ime_with_hard_keyboard in Settings. mShowImeWithHardKeyboardEnabled = Settings.Secure.getInt( mInputMethodService.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0; // Disable showing Ime with hard keyboard because it is the precondition the for most test // cases if (mShowImeWithHardKeyboardEnabled) { executeShellCommand(DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); } mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES; } @After public void tearDown() throws Exception { mUiDevice.unfreezeRotation(); executeShellCommand("ime disable " + mInputMethodId); // Change back the original value of show_ime_with_hard_keyboard in Settings. executeShellCommand(mShowImeWithHardKeyboardEnabled ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); } } @Test @Test Loading @@ -107,8 +136,6 @@ public class InputMethodServiceTest { true /* inputViewStarted */); true /* inputViewStarted */); // Triggers to hide IME via public API. // Triggers to hide IME via public API. // TODO(b/242838873): investigate why WIC#hide(ime()) does not work, likely related to // triggered from IME process. verifyInputViewStatusOnMainSync( verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(), () -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(), false /* inputViewStarted */); false /* inputViewStarted */); Loading Loading @@ -145,6 +172,141 @@ public class InputMethodServiceTest { false /* inputViewStarted */); false /* inputViewStarted */); } } @Test public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() throws Exception { executeShellCommand(ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); mInstrumentation.waitForIdleSync(); // Simulate connecting a hard keyboard mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); } @Test public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() { mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isFalse()); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); } @Test public void testShowSoftInput_disableShowImeWithHardKeyboard() throws Exception { // Simulate connecting a hard 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 // shown no matter what the show flag is. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* inputViewStarted */); verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), false /* inputViewStarted */); } @Test public void testShowSoftInputExplicitly() throws Exception { // When InputMethodService#onEvaluateInputViewShown() returns true and flag is EXPLICIT, the // Ime should be shown. verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* inputViewStarted */); } @Test public void testShowSoftInputImplicitly() throws Exception { // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT, the // Ime should be shown. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), true /* inputViewStarted */); } @Test public void testShowSoftInputImplicitly_fullScreenMode() throws Exception { // When keyboard is off, InputMethodService#onEvaluateInputViewShown returns true, flag is // IMPLICIT and InputMethodService#onEvaluateFullScreenMode returns true, the Ime should not // be shown. setOrientation(2); eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse()); // Wait for the TestActivity to be recreated eventually(() -> assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); // Get the new TestActivity mActivity = TestActivity.getLastCreatedInstance(); assertThat(mActivity).isNotNull(); InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); // Wait for the new EditText to be served by InputMethodManager eventually(() -> assertThat(imm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* inputViewStarted */); } @Test public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception { mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; // When connecting to a hard keyboard and the flag is IMPLICIT, the Ime should not be shown. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* inputViewStarted */); } @Test public void testConfigurationChanged_withKeyboardShownExplicitly() throws InterruptedException { verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* inputViewStarted */); // Simulate a fake configuration change to avoid triggering the recreation of TestActivity. mInputMethodService.getResources().getConfiguration().orientation = Configuration.ORIENTATION_LANDSCAPE; verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged( mInputMethodService.getResources().getConfiguration()), true /* inputViewStarted */); } @Test public void testConfigurationChanged_withKeyboardShownImplicitly() throws InterruptedException { verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), true /* inputViewStarted */); // Simulate a fake configuration change to avoid triggering the recreation of TestActivity. mInputMethodService.getResources().getConfiguration().orientation = Configuration.ORIENTATION_LANDSCAPE; mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; // Normally, IMS#onFinishInputView will be called when finishing the input view by the user. // But if IMS#hideWindow is called when receiving a new configuration change, we don't // expect that it's user-driven to finish the lifecycle of input view with // IMS#onFinishInputView, because the input view will be re-initialized according to the // last mShowSoftRequested state. So in this case we treat the input view is still alive. verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged( mInputMethodService.getResources().getConfiguration()), true /* inputViewStarted */); assertThat(mInputMethodService.isInputViewShown()).isFalse(); } private void verifyInputViewStatus(Runnable runnable, boolean inputViewStarted) private void verifyInputViewStatus(Runnable runnable, boolean inputViewStarted) throws InterruptedException { throws InterruptedException { verifyInputViewStatusInternal(runnable, inputViewStarted, false /*runOnMainSync*/); verifyInputViewStatusInternal(runnable, inputViewStarted, false /*runOnMainSync*/); Loading Loading @@ -184,8 +346,6 @@ public class InputMethodServiceTest { Log.i(TAG, "Set orientation right"); Log.i(TAG, "Set orientation right"); verifyFullscreenMode(() -> setOrientation(2), false /* orientationPortrait */); verifyFullscreenMode(() -> setOrientation(2), false /* orientationPortrait */); mUiDevice.unfreezeRotation(); } } private void setOrientation(int orientation) { private void setOrientation(int orientation) { Loading Loading @@ -249,7 +409,6 @@ public class InputMethodServiceTest { private void prepareEditor() { private void prepareEditor() { mActivity = TestActivity.start(mInstrumentation); mActivity = TestActivity.start(mInstrumentation); mEditText = mActivity.mEditText; Log.i(TAG, "Finish preparing activity with editor."); Log.i(TAG, "Finish preparing activity with editor."); } } Loading services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp +3 −0 Original line number Original line Diff line number Diff line Loading @@ -58,5 +58,8 @@ android_library { srcs: [ srcs: [ "src/com/android/apps/inputmethod/simpleime/testing/*.java", "src/com/android/apps/inputmethod/simpleime/testing/*.java", ], ], static_libs: [ "androidx.annotation_annotation", ], sdk_version: "current", sdk_version: "current", } } services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml +2 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,8 @@ <uses-sdk android:targetSdkVersion="31" /> <uses-sdk android:targetSdkVersion="31" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <application android:debuggable="true" <application android:debuggable="true" android:label="@string/app_name"> android:label="@string/app_name"> <service <service Loading services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/testing/TestActivity.java +19 −3 Original line number Original line Diff line number Diff line Loading @@ -31,6 +31,10 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.LinearLayout; import androidx.annotation.Nullable; import java.lang.ref.WeakReference; /** /** * A special activity for testing purpose. * A special activity for testing purpose. * * Loading @@ -40,6 +44,8 @@ import android.widget.LinearLayout; */ */ public class TestActivity extends Activity { public class TestActivity extends Activity { private static final String TAG = "TestActivity"; private static final String TAG = "TestActivity"; 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. * Start a new test activity with an editor and wait for it to begin running before returning. Loading @@ -57,10 +63,15 @@ public class TestActivity extends Activity { return (TestActivity) instrumentation.startActivitySync(intent); return (TestActivity) instrumentation.startActivitySync(intent); } } public EditText mEditText; private EditText mEditText; public EditText getEditText() { return mEditText; } @Override @Override protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); LinearLayout rootView = new LinearLayout(this); LinearLayout rootView = new LinearLayout(this); mEditText = new EditText(this); mEditText = new EditText(this); Loading @@ -68,14 +79,19 @@ public class TestActivity extends Activity { rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); setContentView(rootView); setContentView(rootView); mEditText.requestFocus(); mEditText.requestFocus(); super.onCreate(savedInstanceState); sLastCreatedInstance = new WeakReference<>(this); } /** Get the last created TestActivity instance. */ public static @Nullable TestActivity getLastCreatedInstance() { return sLastCreatedInstance.get(); } } /** Shows soft keyboard via InputMethodManager. */ /** Shows soft keyboard via InputMethodManager. */ public boolean showImeWithInputMethodManager(int flags) { public boolean showImeWithInputMethodManager(int flags) { InputMethodManager imm = getSystemService(InputMethodManager.class); InputMethodManager imm = getSystemService(InputMethodManager.class); boolean result = imm.showSoftInput(mEditText, flags); boolean result = imm.showSoftInput(mEditText, flags); Log.i(TAG, "hideIme() via InputMethodManager, result=" + result); Log.i(TAG, "showIme() via InputMethodManager, result=" + result); return result; return result; } } Loading Loading
services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +175 −16 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.app.Instrumentation; import android.content.Context; import android.content.Context; import android.content.res.Configuration; import android.content.res.Configuration; import android.os.RemoteException; import android.os.RemoteException; import android.provider.Settings; import android.support.test.uiautomator.By; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.UiObject2; Loading @@ -31,7 +32,6 @@ import android.support.test.uiautomator.Until; import android.util.Log; import android.util.Log; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest; Loading @@ -40,6 +40,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; import com.android.apps.inputmethod.simpleime.testing.TestActivity; import com.android.apps.inputmethod.simpleime.testing.TestActivity; import org.junit.After; import org.junit.Before; import org.junit.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; Loading @@ -54,15 +55,19 @@ public class InputMethodServiceTest { private static final String INPUT_METHOD_SERVICE_NAME = ".SimpleInputMethodService"; private static final String INPUT_METHOD_SERVICE_NAME = ".SimpleInputMethodService"; private static final String EDIT_TEXT_DESC = "Input box"; private static final String EDIT_TEXT_DESC = "Input box"; private static final long TIMEOUT_IN_SECONDS = 3; private static final long TIMEOUT_IN_SECONDS = 3; private static final String ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD = public Instrumentation mInstrumentation; "settings put secure show_ime_with_hard_keyboard 1"; public UiDevice mUiDevice; private static final String DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD = public Context mContext; "settings put secure show_ime_with_hard_keyboard 0"; public String mTargetPackageName; public TestActivity mActivity; private Instrumentation mInstrumentation; public EditText mEditText; private UiDevice mUiDevice; public InputMethodServiceWrapper mInputMethodService; private Context mContext; public String mInputMethodId; private String mTargetPackageName; private TestActivity mActivity; private InputMethodServiceWrapper mInputMethodService; private String mInputMethodId; private boolean mShowImeWithHardKeyboardEnabled; @Before @Before public void setUp() throws Exception { public void setUp() throws Exception { Loading @@ -73,7 +78,9 @@ public class InputMethodServiceTest { mInputMethodId = getInputMethodId(); mInputMethodId = getInputMethodId(); prepareIme(); prepareIme(); prepareEditor(); prepareEditor(); mInstrumentation.waitForIdleSync(); mUiDevice.freezeRotation(); mUiDevice.setOrientationNatural(); // Waits for input binding ready. // Waits for input binding ready. eventually( eventually( () -> { () -> { Loading @@ -85,6 +92,28 @@ public class InputMethodServiceTest { assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse(); assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse(); }); }); // Save the original value of show_ime_with_hard_keyboard in Settings. mShowImeWithHardKeyboardEnabled = Settings.Secure.getInt( mInputMethodService.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0; // Disable showing Ime with hard keyboard because it is the precondition the for most test // cases if (mShowImeWithHardKeyboardEnabled) { executeShellCommand(DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); } mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES; } @After public void tearDown() throws Exception { mUiDevice.unfreezeRotation(); executeShellCommand("ime disable " + mInputMethodId); // Change back the original value of show_ime_with_hard_keyboard in Settings. executeShellCommand(mShowImeWithHardKeyboardEnabled ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); } } @Test @Test Loading @@ -107,8 +136,6 @@ public class InputMethodServiceTest { true /* inputViewStarted */); true /* inputViewStarted */); // Triggers to hide IME via public API. // Triggers to hide IME via public API. // TODO(b/242838873): investigate why WIC#hide(ime()) does not work, likely related to // triggered from IME process. verifyInputViewStatusOnMainSync( verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(), () -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(), false /* inputViewStarted */); false /* inputViewStarted */); Loading Loading @@ -145,6 +172,141 @@ public class InputMethodServiceTest { false /* inputViewStarted */); false /* inputViewStarted */); } } @Test public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() throws Exception { executeShellCommand(ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); mInstrumentation.waitForIdleSync(); // Simulate connecting a hard keyboard mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); } @Test public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() { mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isFalse()); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); } @Test public void testShowSoftInput_disableShowImeWithHardKeyboard() throws Exception { // Simulate connecting a hard 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 // shown no matter what the show flag is. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* inputViewStarted */); verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), false /* inputViewStarted */); } @Test public void testShowSoftInputExplicitly() throws Exception { // When InputMethodService#onEvaluateInputViewShown() returns true and flag is EXPLICIT, the // Ime should be shown. verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* inputViewStarted */); } @Test public void testShowSoftInputImplicitly() throws Exception { // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT, the // Ime should be shown. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), true /* inputViewStarted */); } @Test public void testShowSoftInputImplicitly_fullScreenMode() throws Exception { // When keyboard is off, InputMethodService#onEvaluateInputViewShown returns true, flag is // IMPLICIT and InputMethodService#onEvaluateFullScreenMode returns true, the Ime should not // be shown. setOrientation(2); eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse()); // Wait for the TestActivity to be recreated eventually(() -> assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); // Get the new TestActivity mActivity = TestActivity.getLastCreatedInstance(); assertThat(mActivity).isNotNull(); InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); // Wait for the new EditText to be served by InputMethodManager eventually(() -> assertThat(imm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* inputViewStarted */); } @Test public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception { mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; // When connecting to a hard keyboard and the flag is IMPLICIT, the Ime should not be shown. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), false /* inputViewStarted */); } @Test public void testConfigurationChanged_withKeyboardShownExplicitly() throws InterruptedException { verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), true /* inputViewStarted */); // Simulate a fake configuration change to avoid triggering the recreation of TestActivity. mInputMethodService.getResources().getConfiguration().orientation = Configuration.ORIENTATION_LANDSCAPE; verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged( mInputMethodService.getResources().getConfiguration()), true /* inputViewStarted */); } @Test public void testConfigurationChanged_withKeyboardShownImplicitly() throws InterruptedException { verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), true /* inputViewStarted */); // Simulate a fake configuration change to avoid triggering the recreation of TestActivity. mInputMethodService.getResources().getConfiguration().orientation = Configuration.ORIENTATION_LANDSCAPE; mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; // Normally, IMS#onFinishInputView will be called when finishing the input view by the user. // But if IMS#hideWindow is called when receiving a new configuration change, we don't // expect that it's user-driven to finish the lifecycle of input view with // IMS#onFinishInputView, because the input view will be re-initialized according to the // last mShowSoftRequested state. So in this case we treat the input view is still alive. verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged( mInputMethodService.getResources().getConfiguration()), true /* inputViewStarted */); assertThat(mInputMethodService.isInputViewShown()).isFalse(); } private void verifyInputViewStatus(Runnable runnable, boolean inputViewStarted) private void verifyInputViewStatus(Runnable runnable, boolean inputViewStarted) throws InterruptedException { throws InterruptedException { verifyInputViewStatusInternal(runnable, inputViewStarted, false /*runOnMainSync*/); verifyInputViewStatusInternal(runnable, inputViewStarted, false /*runOnMainSync*/); Loading Loading @@ -184,8 +346,6 @@ public class InputMethodServiceTest { Log.i(TAG, "Set orientation right"); Log.i(TAG, "Set orientation right"); verifyFullscreenMode(() -> setOrientation(2), false /* orientationPortrait */); verifyFullscreenMode(() -> setOrientation(2), false /* orientationPortrait */); mUiDevice.unfreezeRotation(); } } private void setOrientation(int orientation) { private void setOrientation(int orientation) { Loading Loading @@ -249,7 +409,6 @@ public class InputMethodServiceTest { private void prepareEditor() { private void prepareEditor() { mActivity = TestActivity.start(mInstrumentation); mActivity = TestActivity.start(mInstrumentation); mEditText = mActivity.mEditText; Log.i(TAG, "Finish preparing activity with editor."); Log.i(TAG, "Finish preparing activity with editor."); } } Loading
services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp +3 −0 Original line number Original line Diff line number Diff line Loading @@ -58,5 +58,8 @@ android_library { srcs: [ srcs: [ "src/com/android/apps/inputmethod/simpleime/testing/*.java", "src/com/android/apps/inputmethod/simpleime/testing/*.java", ], ], static_libs: [ "androidx.annotation_annotation", ], sdk_version: "current", sdk_version: "current", } }
services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml +2 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,8 @@ <uses-sdk android:targetSdkVersion="31" /> <uses-sdk android:targetSdkVersion="31" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <application android:debuggable="true" <application android:debuggable="true" android:label="@string/app_name"> android:label="@string/app_name"> <service <service Loading
services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/testing/TestActivity.java +19 −3 Original line number Original line Diff line number Diff line Loading @@ -31,6 +31,10 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.LinearLayout; import androidx.annotation.Nullable; import java.lang.ref.WeakReference; /** /** * A special activity for testing purpose. * A special activity for testing purpose. * * Loading @@ -40,6 +44,8 @@ import android.widget.LinearLayout; */ */ public class TestActivity extends Activity { public class TestActivity extends Activity { private static final String TAG = "TestActivity"; private static final String TAG = "TestActivity"; 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. * Start a new test activity with an editor and wait for it to begin running before returning. Loading @@ -57,10 +63,15 @@ public class TestActivity extends Activity { return (TestActivity) instrumentation.startActivitySync(intent); return (TestActivity) instrumentation.startActivitySync(intent); } } public EditText mEditText; private EditText mEditText; public EditText getEditText() { return mEditText; } @Override @Override protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); LinearLayout rootView = new LinearLayout(this); LinearLayout rootView = new LinearLayout(this); mEditText = new EditText(this); mEditText = new EditText(this); Loading @@ -68,14 +79,19 @@ public class TestActivity extends Activity { rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); setContentView(rootView); setContentView(rootView); mEditText.requestFocus(); mEditText.requestFocus(); super.onCreate(savedInstanceState); sLastCreatedInstance = new WeakReference<>(this); } /** Get the last created TestActivity instance. */ public static @Nullable TestActivity getLastCreatedInstance() { return sLastCreatedInstance.get(); } } /** Shows soft keyboard via InputMethodManager. */ /** Shows soft keyboard via InputMethodManager. */ public boolean showImeWithInputMethodManager(int flags) { public boolean showImeWithInputMethodManager(int flags) { InputMethodManager imm = getSystemService(InputMethodManager.class); InputMethodManager imm = getSystemService(InputMethodManager.class); boolean result = imm.showSoftInput(mEditText, flags); boolean result = imm.showSoftInput(mEditText, flags); Log.i(TAG, "hideIme() via InputMethodManager, result=" + result); Log.i(TAG, "showIme() via InputMethodManager, result=" + result); return result; return result; } } Loading