aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Jin <kjin@google.com>2015-03-13 17:47:59 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-03-13 17:48:00 +0000
commitdfb7bd50344340bf195ad3ba1f7b33ff2957271a (patch)
treed669dd59d5d6c3f20215ea76f761ea038b5a807f
parent10e9cfd437860ad3013262c33d4b452853dfb5f5 (diff)
parent4c349e0101d6b7a1057722aeb21d7955b0eb0aeb (diff)
downloaddroiddriver-dfb7bd50344340bf195ad3ba1f7b33ff2957271a.tar.gz
Merge "Add CloseKeyboardAction for InstrumentationDriver"
-rw-r--r--src/io/appium/droiddriver/UiElement.java22
-rw-r--r--src/io/appium/droiddriver/actions/EventAction.java2
-rw-r--r--src/io/appium/droiddriver/actions/accessibility/AccessibilityAction.java2
-rw-r--r--src/io/appium/droiddriver/actions/view/CloseKeyboardAction.java102
-rw-r--r--src/io/appium/droiddriver/actions/view/ViewAction.java48
-rw-r--r--src/io/appium/droiddriver/util/InstrumentationUtils.java5
6 files changed, 166 insertions, 15 deletions
diff --git a/src/io/appium/droiddriver/UiElement.java b/src/io/appium/droiddriver/UiElement.java
index e160c35..a003367 100644
--- a/src/io/appium/droiddriver/UiElement.java
+++ b/src/io/appium/droiddriver/UiElement.java
@@ -145,19 +145,15 @@ public interface UiElement {
boolean perform(Action action);
/**
- * Sets the text of this element. The implementation may not work on all
- * UiElements if the underlying view is not EditText.
- * <p>
- * If this element already has text, it is cleared first if the device has API 11 or higher.
- * <p>
- * TODO: Support this behavior on older devices.
- * <p>
- * If the {@code text} ends with {@code '\n'}, the IME may be closed automatically after this
- * call. If the IME is open after this call, you can call
- * <pre>
- * perform(SingleKeyAction.BACK);
- * </pre>
- * to close the IME.
+ * Sets the text of this element. The implementation may not work on all UiElements if the
+ * underlying view is not EditText. <p> If this element already has text, it is cleared first if
+ * the device has API 11 or higher. <p> TODO: Support this behavior on older devices. <p> The IME
+ * (soft keyboard) may be shown after this call. If the {@code text} ends with {@code '\n'}, the
+ * IME may be closed automatically. If the IME is open, you can call {@link UiDevice#pressBack()}
+ * to close it. <p> If you are using {@link io.appium.droiddriver.instrumentation.InstrumentationDriver},
+ * you may use {@link io.appium.droiddriver.actions.view.CloseKeyboardAction} to close it. The
+ * advantage of {@code CloseKeyboardAction} is that it is a no-op if the IME is hidden. This is
+ * useful when the state of the IME cannot be determined.
*
* @param text the text to enter
*/
diff --git a/src/io/appium/droiddriver/actions/EventAction.java b/src/io/appium/droiddriver/actions/EventAction.java
index b271646..79e5060 100644
--- a/src/io/appium/droiddriver/actions/EventAction.java
+++ b/src/io/appium/droiddriver/actions/EventAction.java
@@ -29,7 +29,7 @@ public abstract class EventAction extends BaseAction {
}
@Override
- public boolean perform(UiElement element) {
+ public final boolean perform(UiElement element) {
return perform(element.getInjector(), element);
}
diff --git a/src/io/appium/droiddriver/actions/accessibility/AccessibilityAction.java b/src/io/appium/droiddriver/actions/accessibility/AccessibilityAction.java
index 34ba85a..8d5f7ad 100644
--- a/src/io/appium/droiddriver/actions/accessibility/AccessibilityAction.java
+++ b/src/io/appium/droiddriver/actions/accessibility/AccessibilityAction.java
@@ -32,7 +32,7 @@ public abstract class AccessibilityAction extends BaseAction {
}
@Override
- public boolean perform(UiElement element) {
+ public final boolean perform(UiElement element) {
return perform(((UiAutomationElement) element).getRawElement(), element);
}
diff --git a/src/io/appium/droiddriver/actions/view/CloseKeyboardAction.java b/src/io/appium/droiddriver/actions/view/CloseKeyboardAction.java
new file mode 100644
index 0000000..d653397
--- /dev/null
+++ b/src/io/appium/droiddriver/actions/view/CloseKeyboardAction.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 DroidDriver committers
+ *
+ * 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 io.appium.droiddriver.actions.view;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import io.appium.droiddriver.UiElement;
+import io.appium.droiddriver.exceptions.ActionException;
+import io.appium.droiddriver.exceptions.DroidDriverException;
+import io.appium.droiddriver.util.InstrumentationUtils;
+import io.appium.droiddriver.util.Logs;
+
+/**
+ * Closes soft keyboard. Based on the <a href="https://code.google.com/p/android-test-kit/wiki/Espresso">Espresso</a>
+ * code under the same name.
+ */
+public class CloseKeyboardAction extends ViewAction {
+ /** Defaults timeoutMillis to 2000 */
+ public static final CloseKeyboardAction DEFAULT_INSTANCE = new CloseKeyboardAction(2000L, 1000L);
+
+ private final long keyboardDismissalDelayMillis;
+
+ /**
+ * @param timeoutMillis the value returned by {@link #getTimeoutMillis}
+ * @param keyboardDismissalDelayMillis <a href="https://code.google.com/p/android-test-kit/issues/detail?id=79#c7">a
+ * delay for the soft keyboard to finish closing</a>
+ */
+ public CloseKeyboardAction(long timeoutMillis, long keyboardDismissalDelayMillis) {
+ super(timeoutMillis);
+ this.keyboardDismissalDelayMillis = keyboardDismissalDelayMillis;
+ }
+
+ protected boolean perform(View view, UiElement element) {
+ InputMethodManager imm = (InputMethodManager) InstrumentationUtils.getTargetContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ final AtomicInteger resultCodeHolder = new AtomicInteger();
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ ResultReceiver resultReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ resultCodeHolder.set(resultCode);
+ latch.countDown();
+ }
+ };
+
+ if (!imm.hideSoftInputFromWindow(view.getWindowToken(), 0, resultReceiver)) {
+ Logs.log(Log.INFO, "InputMethodManager.hideSoftInputFromWindow returned false");
+ // Soft keyboard is not shown if hideSoftInputFromWindow returned false
+ return true;
+ }
+
+ try {
+ if (!latch.await(getTimeoutMillis(), TimeUnit.MILLISECONDS)) {
+ throw new ActionException("Timed out after " + getTimeoutMillis() + " milliseconds" +
+ " waiting for resultCode from InputMethodManager.hideSoftInputFromWindow");
+ }
+ } catch (InterruptedException e) {
+ throw DroidDriverException.propagate(e);
+ }
+
+ int resultCode = resultCodeHolder.get();
+ if (resultCode != InputMethodManager.RESULT_UNCHANGED_HIDDEN
+ && resultCode != InputMethodManager.RESULT_HIDDEN) {
+ throw new ActionException("resultCode from InputMethodManager.hideSoftInputFromWindow="
+ + resultCode);
+ }
+
+ // Wait for the soft keyboard to finish closing
+ SystemClock.sleep(keyboardDismissalDelayMillis);
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/src/io/appium/droiddriver/actions/view/ViewAction.java b/src/io/appium/droiddriver/actions/view/ViewAction.java
new file mode 100644
index 0000000..07fd7b9
--- /dev/null
+++ b/src/io/appium/droiddriver/actions/view/ViewAction.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 DroidDriver committers
+ *
+ * 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 io.appium.droiddriver.actions.view;
+
+import android.view.View;
+
+import io.appium.droiddriver.UiElement;
+import io.appium.droiddriver.actions.BaseAction;
+import io.appium.droiddriver.instrumentation.ViewElement;
+
+/**
+ * Implements {@link io.appium.droiddriver.actions.Action} using the associated {@link View}. This
+ * can only be used with {@link ViewElement}.
+ */
+public abstract class ViewAction extends BaseAction {
+ protected ViewAction(long timeoutMillis) {
+ super(timeoutMillis);
+ }
+
+ @Override
+ public final boolean perform(UiElement element) {
+ return perform(((ViewElement) element).getRawElement(), element);
+ }
+
+ /**
+ * Performs the action on the associated {@link View}.
+ *
+ * @param view the View associated with the UiElement
+ * @param element the UiElement to perform the action on
+ * @return Whether the action is successful. Some actions throw exceptions in case of failure,
+ * when that behavior is more appropriate.
+ */
+ protected abstract boolean perform(View view, UiElement element);
+}
diff --git a/src/io/appium/droiddriver/util/InstrumentationUtils.java b/src/io/appium/droiddriver/util/InstrumentationUtils.java
index 95eb48c..c4f280d 100644
--- a/src/io/appium/droiddriver/util/InstrumentationUtils.java
+++ b/src/io/appium/droiddriver/util/InstrumentationUtils.java
@@ -17,6 +17,7 @@
package io.appium.droiddriver.util;
import android.app.Instrumentation;
+import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
@@ -73,6 +74,10 @@ public class InstrumentationUtils {
return instrumentation;
}
+ public static Context getTargetContext() {
+ return getInstrumentation().getTargetContext();
+ }
+
/**
* Gets the <a href= "http://developer.android.com/tools/testing/testing_otheride.html#AMOptionsSyntax"
* >am instrument options</a>.