diff --git a/app/build.gradle b/app/build.gradle index d81f1a9..2d159ce 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ android { buildToolsVersion "21.1.2" defaultConfig { - applicationId "io.github.sealor.prototype.android.espresso.ui.testing" + applicationId "io.github.sealor.prototype.android.espresso.keyboard.testing" minSdkVersion 19 targetSdkVersion 21 versionCode 1 diff --git a/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/keyboard/testing/KeyboardSwitchHelper.java b/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/keyboard/testing/KeyboardSwitchHelper.java new file mode 100644 index 0000000..536c9a8 --- /dev/null +++ b/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/keyboard/testing/KeyboardSwitchHelper.java @@ -0,0 +1,59 @@ +package io.github.sealor.prototype.android.espresso.keyboard.testing; + + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; +import android.view.inputmethod.InputMethodManager; + +public class KeyboardSwitchHelper { + + public static final int POLLING_PERIOD_IN_MILLIS = 1000; + + private final Activity activity; + private final String packageName; + private final ContentResolver contentResolver; + private final InputMethodManager inputMethodManager; + + public KeyboardSwitchHelper(Activity activity) { + this.activity = activity; + this.packageName = activity.getPackageName(); + this.contentResolver = activity.getContentResolver(); + this.inputMethodManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + } + + private String retrieveCurrentKeyboardName() { + return Settings.Secure.getString(this.contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD); + } + + private boolean isAppKeyboardTheCurrentKeyboard() { + return retrieveCurrentKeyboardName().startsWith(this.packageName); + } + + private boolean isTestActivityFocused() { + return this.activity.hasWindowFocus(); + } + + private void showKeyboardPickerDialog() { + this.inputMethodManager.showInputMethodPicker(); + } + + private void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public void showKeyboardPickerDialogIfNeededAndWaitForSwitch() { + if (!isAppKeyboardTheCurrentKeyboard()) { + showKeyboardPickerDialog(); + + do { + sleep(POLLING_PERIOD_IN_MILLIS); + } while (!isTestActivityFocused()); + } + } +} diff --git a/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MainActivityInstrumentationTest.java b/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MainActivityInstrumentationTest.java new file mode 100644 index 0000000..934f3b0 --- /dev/null +++ b/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MainActivityInstrumentationTest.java @@ -0,0 +1,57 @@ +package io.github.sealor.prototype.android.espresso.keyboard.testing; + +import android.content.Intent; +import android.os.IBinder; +import android.support.test.InstrumentationRegistry; +import android.support.test.rule.ActivityTestRule; +import android.support.test.rule.ServiceTestRule; + +import org.junit.Rule; +import org.junit.Test; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.util.concurrent.TimeoutException; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +public class MainActivityInstrumentationTest { + + @Rule + public ActivityTestRule activityTestRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public final ServiceTestRule mServiceRule = new ServiceTestRule(); + + @Test + public void validateEditTextWithKeyboardInput() throws TimeoutException { + KeyboardSwitchHelper helper = new KeyboardSwitchHelper(this.activityTestRule.getActivity()); + helper.showKeyboardPickerDialogIfNeededAndWaitForSwitch(); + + Intent serviceIntent = new Intent(InstrumentationRegistry.getTargetContext(), MyKeyboard.class); + IBinder binder = mServiceRule.bindService(serviceIntent); + MyKeyboard keyboard = retrieveMyKeyboardInstance(binder); + + onView(withId(R.id.input)).perform(click()); + keyboard.onText("Stefan"); + onView(withId(R.id.button)).perform(click()); + onView(withId(R.id.output)).check(matches(withText("Hello Stefan!"))); + } + + private MyKeyboard retrieveMyKeyboardInstance(IBinder binder) { + try { + Class wrapperClass = Class.forName("android.inputmethodservice.IInputMethodWrapper"); + Field mTargetField = wrapperClass.getDeclaredField("mTarget"); + mTargetField.setAccessible(true); + + WeakReference weakReference = (WeakReference) mTargetField.get(binder); + return weakReference.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/ui/testing/MainActivityInstrumentationTest.java b/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/ui/testing/MainActivityInstrumentationTest.java deleted file mode 100644 index 1aecaa8..0000000 --- a/app/src/androidTest/java/io/github/sealor/prototype/android/espresso/ui/testing/MainActivityInstrumentationTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.sealor.prototype.android.espresso.ui.testing; - -import android.support.test.rule.ActivityTestRule; - -import org.junit.Rule; -import org.junit.Test; - -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.action.ViewActions.typeText; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; - -public class MainActivityInstrumentationTest { - - @Rule - public ActivityTestRule activityTestRule = new ActivityTestRule<>(MainActivity.class); - - @Test - public void validateEditText() { - onView(withId(R.id.input)).perform(typeText("Stefan")); - onView(withId(R.id.button)).perform(click()); - onView(withId(R.id.output)).check(matches(withText("Hello Stefan!"))); - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 65261bf..41ff31b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="io.github.sealor.prototype.android.espresso.keyboard.testing"> + @@ -17,6 +18,19 @@ + + + + + + + + + diff --git a/app/src/main/java/io/github/sealor/prototype/android/espresso/ui/testing/MainActivity.java b/app/src/main/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MainActivity.java similarity index 95% rename from app/src/main/java/io/github/sealor/prototype/android/espresso/ui/testing/MainActivity.java rename to app/src/main/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MainActivity.java index 6f44cb6..4d6d4de 100644 --- a/app/src/main/java/io/github/sealor/prototype/android/espresso/ui/testing/MainActivity.java +++ b/app/src/main/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MainActivity.java @@ -1,4 +1,4 @@ -package io.github.sealor.prototype.android.espresso.ui.testing; +package io.github.sealor.prototype.android.espresso.keyboard.testing; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; diff --git a/app/src/main/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MyKeyboard.java b/app/src/main/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MyKeyboard.java new file mode 100644 index 0000000..790519a --- /dev/null +++ b/app/src/main/java/io/github/sealor/prototype/android/espresso/keyboard/testing/MyKeyboard.java @@ -0,0 +1,115 @@ +package io.github.sealor.prototype.android.espresso.keyboard.testing; + +import android.inputmethodservice.InputMethodService; +import android.inputmethodservice.Keyboard; +import android.inputmethodservice.KeyboardView; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; + +public class MyKeyboard extends InputMethodService implements KeyboardView.OnKeyboardActionListener { + + public final static int KEYCODE_ENTER = -10; + + private Keyboard keyboardLayout; + private KeyboardView keyboardView; + + private boolean shifted = false; + + @Override + public void onInitializeInterface() { + super.onInitializeInterface(); + + this.keyboardLayout = new Keyboard(this, R.xml.qwertz); + } + + @Override + public void onStartInput(EditorInfo attribute, boolean restarting) { + super.onStartInput(attribute, restarting); + } + + @Override + public void onFinishInput() { + super.onFinishInput(); + + if (this.keyboardView != null) { + this.keyboardView.closing(); + } + } + + @Override + public View onCreateInputView() { + Log.i("tag", "createInputView"); + this.keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboardview, null); + this.keyboardView.setKeyboard(this.keyboardLayout); + this.keyboardView.setOnKeyboardActionListener(this); + return this.keyboardView; + } + + private void sendTypedKey(int keyEventCode) { + getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); + getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); + } + + private void updateShiftState() { + this.keyboardView.setShifted(this.shifted); + this.keyboardLayout.setShifted(this.shifted); + } + + @Override + public void onKey(int primaryCode, int[] keyCodes) { + Log.d("tag", "onKey: " + primaryCode); + + if (primaryCode > 0) { + String text = String.valueOf((char) primaryCode); + if (this.keyboardLayout.isShifted()) { + text = text.toUpperCase(); + } + onText(text); + } else if (primaryCode == Keyboard.KEYCODE_DELETE) { + sendTypedKey(KeyEvent.KEYCODE_DEL); + } else if (primaryCode == Keyboard.KEYCODE_SHIFT) { + shifted = !shifted; + updateShiftState(); + } else if (primaryCode == MyKeyboard.KEYCODE_ENTER) { + sendTypedKey(KeyEvent.KEYCODE_ENTER); + } + } + + @Override + public void onText(CharSequence text) { + Log.d("tag", "onText: " + text); + + getCurrentInputConnection().commitText(text, 1); + + shifted = false; + updateShiftState(); + } + + @Override + public void onPress(int primaryCode) { + Log.d("tag", "onPress: " + primaryCode); + } + + @Override + public void onRelease(int primaryCode) { + Log.d("tag", "onRelease: " + primaryCode); + } + + @Override + public void swipeDown() { + } + + @Override + public void swipeLeft() { + } + + @Override + public void swipeRight() { + } + + @Override + public void swipeUp() { + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e168e91..7072fa9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,7 +7,7 @@ android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" - tools:context="io.github.sealor.prototype.android.espresso.ui.testing.MainActivity"> + tools:context="io.github.sealor.prototype.android.espresso.keyboard.testing.MainActivity"> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 207baab..a7f6059 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - Prototype Android Espresso UI Testing + Prototype Android Espresso Keyboard Testing Settings Type your name: greet me diff --git a/app/src/main/res/xml/method.xml b/app/src/main/res/xml/method.xml new file mode 100644 index 0000000..e72259c --- /dev/null +++ b/app/src/main/res/xml/method.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/xml/qwertz.xml b/app/src/main/res/xml/qwertz.xml new file mode 100644 index 0000000..aa24cdc --- /dev/null +++ b/app/src/main/res/xml/qwertz.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/io/github/sealor/prototype/android/espresso/ui/testing/JavaUnitTest.java b/app/src/test/java/io/github/sealor/prototype/android/espresso/keyboard/testing/JavaUnitTest.java similarity index 74% rename from app/src/test/java/io/github/sealor/prototype/android/espresso/ui/testing/JavaUnitTest.java rename to app/src/test/java/io/github/sealor/prototype/android/espresso/keyboard/testing/JavaUnitTest.java index 383a339..b9b07bb 100644 --- a/app/src/test/java/io/github/sealor/prototype/android/espresso/ui/testing/JavaUnitTest.java +++ b/app/src/test/java/io/github/sealor/prototype/android/espresso/keyboard/testing/JavaUnitTest.java @@ -1,4 +1,4 @@ -package io.github.sealor.prototype.android.espresso.ui.testing; +package io.github.sealor.prototype.android.espresso.keyboard.testing; import org.junit.Test;