diff options
Diffstat (limited to 'src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java')
-rw-r--r-- | src/com/google/android/droiddriver/uiautomation/UiAutomationDriver.java | 112 |
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; } } |