aboutsummaryrefslogtreecommitdiff
path: root/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java')
-rw-r--r--src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java112
1 files changed, 107 insertions, 5 deletions
diff --git a/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java b/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java
index 9291f13..f84ab5f 100644
--- a/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java
+++ b/src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java
@@ -17,20 +17,122 @@
package com.google.android.droiddriver.uiautomation;
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 com.google.android.droiddriver.uiautomation.base.BaseUiAutomationDriver;
+import com.google.android.droiddriver.actions.InputInjector;
+import com.google.android.droiddriver.base.BaseDroidDriver;
+import com.google.android.droiddriver.exceptions.TimeoutException;
+import com.google.android.droiddriver.uiautomation.UiAutomationContext.UiAutomationCallable;
+import com.google.android.droiddriver.util.Logs;
/**
* Implementation of DroidDriver that gets attributes via the Accessibility API
* and is acted upon via synthesized events.
*/
-public class UiAutomationDriver extends BaseUiAutomationDriver<UiAutomationElement> {
+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;
+
public UiAutomationDriver(Instrumentation instrumentation) {
- super(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");
+ uiDevice.sleep();
+ uiDevice.wakeUp();
+ }
+
+ /**
+ * {@link #clearAccessibilityNodeInfoCache} causes the screen to blink. This
+ * method clears the cache without blinking by employing an implementation
+ * detail of AccessibilityNodeInfoCache. This is a hack; use it at your own
+ * discretion.
+ */
+ public void clearAccessibilityNodeInfoCacheHack() {
+ Logs.call(this, "clearAccessibilityNodeInfoCacheHack");
+ AccessibilityManager accessibilityManager =
+ (AccessibilityManager) context.getInstrumentation().getTargetContext()
+ .getSystemService(Context.ACCESSIBILITY_SERVICE);
+ accessibilityManager.sendAccessibilityEvent(AccessibilityEvent
+ .obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED));
}
@Override
- protected UiAutomationContext newContext(Instrumentation instrumentation) {
- return new UiAutomationContext(instrumentation, this);
+ public UiAutomationUiDevice getUiDevice() {
+ return uiDevice;
}
}