aboutsummaryrefslogtreecommitdiff
path: root/src/io/appium/droiddriver/uiautomation/UiAutomationDriver.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/io/appium/droiddriver/uiautomation/UiAutomationDriver.java')
-rw-r--r--src/io/appium/droiddriver/uiautomation/UiAutomationDriver.java160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/io/appium/droiddriver/uiautomation/UiAutomationDriver.java b/src/io/appium/droiddriver/uiautomation/UiAutomationDriver.java
new file mode 100644
index 0000000..2e5d799
--- /dev/null
+++ b/src/io/appium/droiddriver/uiautomation/UiAutomationDriver.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2013 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.uiautomation;
+
+import android.annotation.TargetApi;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.os.SystemClock;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import io.appium.droiddriver.actions.InputInjector;
+import io.appium.droiddriver.base.BaseDroidDriver;
+import io.appium.droiddriver.exceptions.TimeoutException;
+import io.appium.droiddriver.uiautomation.UiAutomationContext.UiAutomationCallable;
+import io.appium.droiddriver.util.Logs;
+
+/**
+ * Implementation of DroidDriver that gets attributes via the Accessibility API
+ * and is acted upon via synthesized events.
+ */
+@TargetApi(18)
+public class UiAutomationDriver extends BaseDroidDriver<AccessibilityNodeInfo, UiAutomationElement> {
+ // TODO: magic const from UiAutomator, but may not be useful
+ /**
+ * This value has the greatest bearing on the appearance of test execution
+ * speeds. This value is used as the minimum time to wait before considering
+ * the UI idle after each action.
+ */
+ private static final long QUIET_TIME_TO_BE_CONSIDERD_IDLE_STATE = 500;// ms
+
+ private final UiAutomationContext context;
+ private final InputInjector injector;
+ private final UiAutomationUiDevice uiDevice;
+ private AccessibilityNodeInfoCacheClearer clearer =
+ new WindowStateAccessibilityNodeInfoCacheClearer();
+
+ public UiAutomationDriver(Instrumentation instrumentation) {
+ context = new UiAutomationContext(instrumentation, this);
+ injector = new UiAutomationInputInjector(context);
+ uiDevice = new UiAutomationUiDevice(context);
+ }
+
+ @Override
+ public InputInjector getInjector() {
+ return injector;
+ }
+
+ @Override
+ protected UiAutomationElement newRootElement() {
+ return context.newRootElement(getRootNode());
+ }
+
+ @Override
+ protected UiAutomationElement newUiElement(AccessibilityNodeInfo rawElement,
+ UiAutomationElement parent) {
+ return new UiAutomationElement(context, rawElement, parent);
+ }
+
+ private AccessibilityNodeInfo getRootNode() {
+ final long timeoutMillis = getPoller().getTimeoutMillis();
+ context.callUiAutomation(new UiAutomationCallable<Void>() {
+ @Override
+ public Void call(UiAutomation uiAutomation) {
+ try {
+ uiAutomation.waitForIdle(QUIET_TIME_TO_BE_CONSIDERD_IDLE_STATE, timeoutMillis);
+ return null;
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException(e);
+ }
+ }
+ });
+
+ long end = SystemClock.uptimeMillis() + timeoutMillis;
+ while (true) {
+ AccessibilityNodeInfo root =
+ context.callUiAutomation(new UiAutomationCallable<AccessibilityNodeInfo>() {
+ @Override
+ public AccessibilityNodeInfo call(UiAutomation uiAutomation) {
+ return uiAutomation.getRootInActiveWindow();
+ }
+ });
+ if (root != null) {
+ return root;
+ }
+ long remainingMillis = end - SystemClock.uptimeMillis();
+ if (remainingMillis < 0) {
+ throw new TimeoutException(
+ String.format("Timed out after %d milliseconds waiting for root AccessibilityNodeInfo",
+ timeoutMillis));
+ }
+ SystemClock.sleep(Math.min(250, remainingMillis));
+ }
+ }
+
+ /**
+ * Some widgets fail to trigger some AccessibilityEvent's after actions,
+ * resulting in stale AccessibilityNodeInfo's. As a work-around, force to
+ * clear the AccessibilityNodeInfoCache.
+ */
+ public void clearAccessibilityNodeInfoCache() {
+ Logs.call(this, "clearAccessibilityNodeInfoCache");
+ clearer.clearAccessibilityNodeInfoCache(this);
+ }
+
+ public interface AccessibilityNodeInfoCacheClearer {
+ void clearAccessibilityNodeInfoCache(UiAutomationDriver driver);
+ }
+
+ /**
+ * Clears AccessibilityNodeInfoCache by turning screen off then on.
+ */
+ public static class ScreenOffAccessibilityNodeInfoCacheClearer implements
+ AccessibilityNodeInfoCacheClearer {
+ public void clearAccessibilityNodeInfoCache(UiAutomationDriver driver) {
+ driver.getUiDevice().sleep();
+ driver.getUiDevice().wakeUp();
+ }
+ }
+
+ /**
+ * Clears AccessibilityNodeInfoCache by exploiting an implementation detail of
+ * AccessibilityNodeInfoCache. This is a hack; use it at your own discretion.
+ */
+ public static class WindowStateAccessibilityNodeInfoCacheClearer implements
+ AccessibilityNodeInfoCacheClearer {
+ public void clearAccessibilityNodeInfoCache(UiAutomationDriver driver) {
+ AccessibilityManager accessibilityManager =
+ (AccessibilityManager) driver.context.getInstrumentation().getTargetContext()
+ .getSystemService(Context.ACCESSIBILITY_SERVICE);
+ accessibilityManager.sendAccessibilityEvent(AccessibilityEvent
+ .obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED));
+ }
+ }
+
+ public void setAccessibilityNodeInfoCacheClearer(AccessibilityNodeInfoCacheClearer clearer) {
+ this.clearer = clearer;
+ }
+
+ @Override
+ public UiAutomationUiDevice getUiDevice() {
+ return uiDevice;
+ }
+}