summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Xie <dxie@google.com>2016-06-24 20:28:20 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2016-06-24 20:28:20 +0000
commit3a87160af28de950df7931e132d40ff9b0c6dbd8 (patch)
tree65a1171b430c073489f3e0e588e2e17637a50f58
parent057b4181495f6ce20d0644bebbe7b961f1e8b31b (diff)
parent0461b5760cf7c4dd04748eb45edd82d6f827d280 (diff)
downloadplatform_cts-3a87160af28de950df7931e132d40ff9b0c6dbd8.tar.gz
platform_cts-3a87160af28de950df7931e132d40ff9b0c6dbd8.tar.bz2
platform_cts-3a87160af28de950df7931e132d40ff9b0c6dbd8.zip
Merge "DO NOT MERGE Update TextViewTest to use KeyEventUtil for KeyEvents" into marshmallow-cts-dev
-rw-r--r--libs/deviceutil/src/android/cts/util/KeyEventUtil.java197
-rw-r--r--tests/tests/widget/AndroidManifest.xml3
-rw-r--r--tests/tests/widget/src/android/widget/cts/TextViewTest.java58
3 files changed, 230 insertions, 28 deletions
diff --git a/libs/deviceutil/src/android/cts/util/KeyEventUtil.java b/libs/deviceutil/src/android/cts/util/KeyEventUtil.java
new file mode 100644
index 00000000000..961337d3321
--- /dev/null
+++ b/libs/deviceutil/src/android/cts/util/KeyEventUtil.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.util;
+
+import android.app.Instrumentation;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+import java.lang.reflect.Field;
+
+/**
+ * Utility class to send KeyEvents to TextView bypassing the IME. The code is similar to functions
+ * in {@link Instrumentation} and {@link android.test.InstrumentationTestCase} classes. It uses
+ * {@link View#dispatchKeyEvent(KeyEvent)} to send the events.
+ * After sending the events waits for idle.
+ */
+public class KeyEventUtil {
+ private final Instrumentation mInstrumentation;
+
+ public KeyEventUtil(Instrumentation instrumentation) {
+ this.mInstrumentation = instrumentation;
+ }
+
+ /**
+ * Sends the key events corresponding to the text to the app being instrumented.
+ *
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param text The text to be sent. Null value returns immediately.
+ */
+ public final void sendString(final View targetView, final String text) {
+ if (text == null) {
+ return;
+ }
+
+ KeyEvent[] events = getKeyEvents(text);
+
+ if (events != null) {
+ for (int i = 0; i < events.length; i++) {
+ // We have to change the time of an event before injecting it because
+ // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
+ // time stamp and the system rejects too old events. Hence, it is
+ // possible for an event to become stale before it is injected if it
+ // takes too long to inject the preceding ones.
+ sendKey(targetView, KeyEvent.changeTimeRepeat(events[i], SystemClock.uptimeMillis(),
+ 0));
+ }
+ }
+ }
+
+ /**
+ * Sends a series of key events through instrumentation. For instance:
+ * sendKeys(view, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_CENTER).
+ *
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param keys The series of key codes.
+ */
+ public final void sendKeys(final View targetView, final int...keys) {
+ final int count = keys.length;
+
+ for (int i = 0; i < count; i++) {
+ try {
+ sendKeyDownUp(targetView, keys[i]);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
+ }
+ }
+
+ /**
+ * Sends a series of key events through instrumentation. The sequence of keys is a string
+ * containing the key names as specified in KeyEvent, without the KEYCODE_ prefix. For
+ * instance: sendKeys(view, "DPAD_LEFT A B C DPAD_CENTER"). Each key can be repeated by using
+ * the N* prefix. For instance, to send two KEYCODE_DPAD_LEFT, use the following:
+ * sendKeys(view, "2*DPAD_LEFT").
+ *
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param keysSequence The sequence of keys.
+ */
+ public final void sendKeys(final View targetView, final String keysSequence) {
+ final String[] keys = keysSequence.split(" ");
+ final int count = keys.length;
+
+ for (int i = 0; i < count; i++) {
+ String key = keys[i];
+ int repeater = key.indexOf('*');
+
+ int keyCount;
+ try {
+ keyCount = repeater == -1 ? 1 : Integer.parseInt(key.substring(0, repeater));
+ } catch (NumberFormatException e) {
+ Log.w("ActivityTestCase", "Invalid repeat count: " + key);
+ continue;
+ }
+
+ if (repeater != -1) {
+ key = key.substring(repeater + 1);
+ }
+
+ for (int j = 0; j < keyCount; j++) {
+ try {
+ final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
+ final int keyCode = keyCodeField.getInt(null);
+ try {
+ sendKeyDownUp(targetView, keyCode);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
+ } catch (NoSuchFieldException e) {
+ Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
+ break;
+ } catch (IllegalAccessException e) {
+ Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Sends an up and down key events.
+ *
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param key The integer keycode for the event to be send.
+ */
+ public final void sendKeyDownUp(final View targetView, final int key) {
+ sendKey(targetView, new KeyEvent(KeyEvent.ACTION_DOWN, key));
+ sendKey(targetView, new KeyEvent(KeyEvent.ACTION_UP, key));
+ }
+
+ /**
+ * Sends a key event.
+ *
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param event KeyEvent to be send.
+ */
+ public final void sendKey(final View targetView, final KeyEvent event) {
+ long downTime = event.getDownTime();
+ long eventTime = event.getEventTime();
+ int action = event.getAction();
+ int code = event.getKeyCode();
+ int repeatCount = event.getRepeatCount();
+ int metaState = event.getMetaState();
+ int deviceId = event.getDeviceId();
+ int scancode = event.getScanCode();
+ int source = event.getSource();
+ int flags = event.getFlags();
+ if (source == InputDevice.SOURCE_UNKNOWN) {
+ source = InputDevice.SOURCE_KEYBOARD;
+ }
+ if (eventTime == 0) {
+ eventTime = SystemClock.uptimeMillis();
+ }
+ if (downTime == 0) {
+ downTime = eventTime;
+ }
+
+ final KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount,
+ metaState, deviceId, scancode, flags, source);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ targetView.dispatchKeyEvent(newEvent);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ }
+
+ private KeyEvent[] getKeyEvents(final String text) {
+ KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ return keyCharacterMap.getEvents(text.toCharArray());
+ }
+}
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index bc431061909..46cdd4de6c1 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -213,7 +213,8 @@
</activity>
<activity android:name="android.widget.cts.TextViewCtsActivity"
- android:label="TextViewCtsActivity">
+ android:label="TextViewCtsActivity"
+ android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 12818d38052..a4c27f47897 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -27,6 +27,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources.NotFoundException;
+import android.cts.util.KeyEventUtil;
import android.cts.util.PollingCheck;
import android.cts.util.WidgetTestUtils;
import android.graphics.Bitmap;
@@ -119,6 +120,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
+ "this text, I would love to see the kind of devices you guys now use!";
private static final long TIMEOUT = 5000;
private CharSequence mTransformedText;
+ private KeyEventUtil mKeyEventUtil;
public TextViewTest() {
super("com.android.cts.widget", TextViewCtsActivity.class);
@@ -135,6 +137,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
}
}.run();
mInstrumentation = getInstrumentation();
+ mKeyEventUtil = new KeyEventUtil(mInstrumentation);
}
/**
@@ -257,7 +260,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
assertSame(movementMethod, mTextView.getMovementMethod());
assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText()));
- sendKeys(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
+ mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
KeyEvent.KEYCODE_DPAD_UP);
// the selection has been removed.
assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
@@ -276,7 +279,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
assertNull(mTextView.getMovementMethod());
assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText()));
- sendKeys(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
+ mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
KeyEvent.KEYCODE_DPAD_UP);
// the selection will not be changed.
assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
@@ -1270,7 +1273,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Type some text.
- mInstrumentation.sendStringSync("abc");
+ mKeyEventUtil.sendString(mTextView, "abc");
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Precondition: The cursor is at the end of the text.
@@ -1302,8 +1305,9 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Simulate deleting text and undoing it.
- mInstrumentation.sendStringSync("xyz");
- sendKeys(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL);
+ mKeyEventUtil.sendString(mTextView, "xyz");
+ mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, KeyEvent
+ .KEYCODE_DEL);
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Precondition: The text was actually deleted.
@@ -1433,8 +1437,8 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Create two undo operations, an insert and a delete.
- mInstrumentation.sendStringSync("xyz");
- sendKeys(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL);
+ mKeyEventUtil.sendString(mTextView, "xyz");
+ mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL);
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Calling setText() clears both undo operations, so undo doesn't happen.
@@ -1455,7 +1459,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Type some text. This creates an undo entry.
- mInstrumentation.sendStringSync("abc");
+ mKeyEventUtil.sendString(mTextView, "abc");
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Undo the typing to create a redo entry.
@@ -1474,7 +1478,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Type some text.
- mInstrumentation.sendStringSync("abc");
+ mKeyEventUtil.sendString(mTextView, "abc");
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Programmatically append some text.
@@ -1497,7 +1501,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Type some text.
- mInstrumentation.sendStringSync("abc");
+ mKeyEventUtil.sendString(mTextView, "abc");
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Directly modify the underlying Editable to insert some text.
@@ -1546,7 +1550,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
mTextView.addTextChangedListener(new ConvertToSpacesTextWatcher());
// Type some text.
- mInstrumentation.sendStringSync("abc");
+ mKeyEventUtil.sendString(mTextView, "abc");
mActivity.runOnUiThread(new Runnable() {
public void run() {
// TextWatcher altered the text.
@@ -1584,7 +1588,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Type some text.
- mInstrumentation.sendStringSync("abc");
+ mKeyEventUtil.sendString(mTextView, "abc");
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Pressing Control-Z triggers undo.
@@ -1607,7 +1611,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Type some text to create an undo operation.
- mInstrumentation.sendStringSync("abc");
+ mKeyEventUtil.sendString(mTextView, "abc");
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Parcel and unparcel the TextView.
@@ -1618,7 +1622,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
mInstrumentation.waitForIdleSync();
// Delete a character to create a new undo operation.
- sendKeys(KeyEvent.KEYCODE_DEL);
+ mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
mActivity.runOnUiThread(new Runnable() {
public void run() {
assertEquals("ab", mTextView.getText().toString());
@@ -1645,8 +1649,8 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
initTextViewForTyping();
// Type and delete to create two new undo operations.
- mInstrumentation.sendStringSync("a");
- sendKeys(KeyEvent.KEYCODE_DEL);
+ mKeyEventUtil.sendString(mTextView, "a");
+ mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Empty the undo stack then parcel and unparcel the TextView. While the undo
@@ -1660,8 +1664,8 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
mInstrumentation.waitForIdleSync();
// Create two more undo operations.
- mInstrumentation.sendStringSync("b");
- sendKeys(KeyEvent.KEYCODE_DEL);
+ mKeyEventUtil.sendString(mTextView, "b");
+ mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Verify undo still works.
@@ -1896,7 +1900,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
assertEquals(errorText, mTextView.getError().toString());
- mInstrumentation.sendStringSync("a");
+ mKeyEventUtil.sendString(mTextView, "a");
// a key event that will not change the TextView's text
assertEquals("", mTextView.getText().toString());
// The icon and error message will not be reset to null
@@ -1912,7 +1916,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
});
mInstrumentation.waitForIdleSync();
- mInstrumentation.sendStringSync("1");
+ mKeyEventUtil.sendString(mTextView, "1");
// a key event cause changes to the TextView's text
assertEquals("1", mTextView.getText().toString());
// the error message and icon will be cleared.
@@ -1938,13 +1942,13 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
assertSame(expected, mTextView.getFilters());
- mInstrumentation.sendStringSync("a");
+ mKeyEventUtil.sendString(mTextView, "a");
// the text is capitalized by InputFilter.AllCaps
assertEquals("A", mTextView.getText().toString());
- mInstrumentation.sendStringSync("b");
+ mKeyEventUtil.sendString(mTextView, "b");
// the text is capitalized by InputFilter.AllCaps
assertEquals("AB", mTextView.getText().toString());
- mInstrumentation.sendStringSync("c");
+ mKeyEventUtil.sendString(mTextView, "c");
// 'C' could not be accepted, because there is a length filter.
assertEquals("AB", mTextView.getText().toString());
@@ -2141,11 +2145,11 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
public void testPressKey() {
initTextViewForTyping();
- mInstrumentation.sendStringSync("a");
+ mKeyEventUtil.sendString(mTextView, "a");
assertEquals("a", mTextView.getText().toString());
- mInstrumentation.sendStringSync("b");
+ mKeyEventUtil.sendString(mTextView, "b");
assertEquals("ab", mTextView.getText().toString());
- sendKeys(KeyEvent.KEYCODE_DEL);
+ mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
assertEquals("a", mTextView.getText().toString());
}
@@ -2457,7 +2461,7 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewCtsAc
assertSame(PasswordTransformationMethod.getInstance(),
mTextView.getTransformationMethod());
- sendKeys("H E 2*L O");
+ mKeyEventUtil.sendKeys(mTextView, "H E 2*L O");
mActivity.runOnUiThread(new Runnable() {
public void run() {
mTextView.append(" ");