diff options
author | Kevin Jin <kjin@google.com> | 2013-11-14 14:11:46 -0800 |
---|---|---|
committer | Kevin Jin <kjin@google.com> | 2013-11-14 14:11:46 -0800 |
commit | ef176eeb3b29df478522c46cc100f421365b008e (patch) | |
tree | 0c9f1ca18e8a1545a2415481031881debb811fca /src | |
parent | 8d19bb634c670a49f7a58636a2a535c86b57d538 (diff) | |
download | droiddriver-ef176eeb3b29df478522c46cc100f421365b008e.tar.gz |
warn the brittle SwipeAction.toFling and remove its use in StepBasedScroller
optimize AccessibilityEventScrollStepStrategy to match UiAutomator
Change-Id: Iacac499659ca6d94daf82f5889700234b231e4d9
Diffstat (limited to 'src')
3 files changed, 64 insertions, 9 deletions
diff --git a/src/com/google/android/droiddriver/actions/SwipeAction.java b/src/com/google/android/droiddriver/actions/SwipeAction.java index ca165f1..186b5dc 100644 --- a/src/com/google/android/droiddriver/actions/SwipeAction.java +++ b/src/com/google/android/droiddriver/actions/SwipeAction.java @@ -22,6 +22,7 @@ import static com.google.android.droiddriver.scroll.Direction.PhysicalDirection. import static com.google.android.droiddriver.scroll.Direction.PhysicalDirection.UP; import android.graphics.Rect; +import android.os.Build; import android.os.SystemClock; import android.view.ViewConfiguration; @@ -31,6 +32,7 @@ import com.google.android.droiddriver.scroll.Direction.PhysicalDirection; import com.google.android.droiddriver.util.Events; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; +import com.google.common.primitives.Ints; /** * A {@link ScrollAction} that swipes the touch screen. Note the scroll @@ -42,13 +44,18 @@ public class SwipeAction extends ScrollAction { // The action is a fling if the ACTION_MOVE velocity is greater than // ViewConfiguration#getScaledMinimumFlingVelocity. The velocity is calculated // as <distance between ACTION_MOVE points> / ACTION_MOVE_INTERVAL + // Note: ACTION_MOVE_INTERVAL is the minimum interval between injected events; + // the actual interval typically is longer. private static final int ACTION_MOVE_INTERVAL = 5; // ViewConfiguration.MINIMUM_FLING_VELOCITY = 50, so if there is no scale, in // theory a swipe of 20 steps is a scroll instead of fling on devices that // have 20 * 50 * 5 = 5000 pixels in one direction. Make it 40 for safety. private static final int SCROLL_STEPS = 40; - // FLING_STEPS = 2 does not work on GingerBread - private static final int FLING_STEPS = 3; + // TODO: Find the exact version-dependent fling steps. It is observed that 2 + // does not work on GINGERBREAD; we haven't tested all versions so <JELLY_BEAN + // is used as a guess. + private static final int FLING_STEPS = Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN ? 3 + : 2; /** * Common instances for convenience. The direction in names reflects the @@ -88,6 +95,13 @@ public class SwipeAction extends ScrollAction { /** * Gets canned common instances for flinging. Note the scroll direction * specifies where the content will move, instead of the finger. + * <p> + * Note: This may not actually fling, depending on the size of the target + * UiElement and the SDK version of the device. If it does not behave as + * expected, you can use SwipeAction instances with custom steps. + * </p> + * + * @see ViewConfiguration#getScaledMinimumFlingVelocity */ // TODO: We may use "smart" steps that depend on the size of the UiElement and // ViewConfiguration#getScaledMinimumFlingVelocity. @@ -127,15 +141,15 @@ public class SwipeAction extends ScrollAction { /** * @param direction the scroll direction specifying where the content will * move, instead of the finger. - * @param steps (steps-1) is the number of {@code ACTION_MOVE} that will be - * injected between {@code ACTION_DOWN} and {@code ACTION_UP} + * @param steps minimum 2; (steps-1) is the number of {@code ACTION_MOVE} that + * will be injected between {@code ACTION_DOWN} and {@code ACTION_UP}. * @param drag whether this is a drag * @param timeoutMillis */ public SwipeAction(PhysicalDirection direction, int steps, boolean drag, long timeoutMillis) { super(timeoutMillis); this.direction = direction; - this.steps = steps; + this.steps = Ints.max(2, steps); this.drag = drag; } diff --git a/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java b/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java index 0333d77..9a4fe05 100644 --- a/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java +++ b/src/com/google/android/droiddriver/scroll/AccessibilityEventScrollStepStrategy.java @@ -17,14 +17,17 @@ package com.google.android.droiddriver.scroll; import android.app.UiAutomation; import android.app.UiAutomation.AccessibilityEventFilter; +import android.util.Log; import android.view.accessibility.AccessibilityEvent; import com.google.android.droiddriver.DroidDriver; import com.google.android.droiddriver.UiElement; import com.google.android.droiddriver.actions.SwipeAction; import com.google.android.droiddriver.finders.Finder; +import com.google.android.droiddriver.scroll.Direction.Axis; import com.google.android.droiddriver.scroll.Direction.DirectionConverter; import com.google.android.droiddriver.scroll.Direction.PhysicalDirection; +import com.google.android.droiddriver.util.Logs; import java.util.concurrent.TimeoutException; @@ -53,6 +56,9 @@ public class AccessibilityEventScrollStepStrategy implements ScrollStepStrategy private final long scrollEventTimeoutMillis; private final DirectionConverter directionConverter; + private Finder containerFinderAtEnd; + private PhysicalDirection directionAtEnd; + public AccessibilityEventScrollStepStrategy(UiAutomation uiAutomation, long scrollEventTimeoutMillis, DirectionConverter converter) { this.uiAutomation = uiAutomation; @@ -63,14 +69,28 @@ public class AccessibilityEventScrollStepStrategy implements ScrollStepStrategy @Override public boolean scroll(DroidDriver driver, Finder containerFinder, final PhysicalDirection direction) { + // Check if we've reached end after last scroll. + if (containerFinderAtEnd == containerFinder && directionAtEnd == direction) { + return false; + } + final UiElement container = driver.on(containerFinder); try { - uiAutomation.executeAndWaitForEvent(new Runnable() { + AccessibilityEvent event = uiAutomation.executeAndWaitForEvent(new Runnable() { @Override public void run() { SwipeAction.toScroll(direction).perform(container.getInjector(), container); } }, SCROLL_EVENT_FILTER, scrollEventTimeoutMillis); + + if (detectEnd(direction.axis(), event)) { + containerFinderAtEnd = containerFinder; + directionAtEnd = direction; + Logs.log(Log.DEBUG, "reached scroll end"); + } else { + containerFinderAtEnd = null; + directionAtEnd = null; + } } catch (TimeoutException e) { // If no TYPE_VIEW_SCROLLED event, no more scrolling is possible return false; @@ -78,6 +98,23 @@ public class AccessibilityEventScrollStepStrategy implements ScrollStepStrategy return true; } + // Copied from UiAutomator. + // AdapterViews have indices we can use to check for the beginning. + private static boolean detectEnd(Axis axis, AccessibilityEvent event) { + boolean foundEnd = false; + if (event.getFromIndex() != -1 && event.getToIndex() != -1 && event.getItemCount() != -1) { + foundEnd = event.getFromIndex() == 0 || (event.getItemCount() - 1) == event.getToIndex(); + } else if (event.getScrollX() != -1 && event.getScrollY() != -1) { + if (axis == Axis.VERTICAL) { + foundEnd = event.getScrollY() == 0 || event.getScrollY() == event.getMaxScrollY(); + } else if (axis == Axis.HORIZONTAL) { + foundEnd = event.getScrollX() == 0 || event.getScrollX() == event.getMaxScrollX(); + } + } + event.recycle(); + return foundEnd; + } + @Override public final DirectionConverter getDirectionConverter() { return directionConverter; diff --git a/src/com/google/android/droiddriver/scroll/StepBasedScroller.java b/src/com/google/android/droiddriver/scroll/StepBasedScroller.java index 2ba8ceb..d7fd368 100644 --- a/src/com/google/android/droiddriver/scroll/StepBasedScroller.java +++ b/src/com/google/android/droiddriver/scroll/StepBasedScroller.java @@ -24,7 +24,6 @@ import com.google.android.droiddriver.DroidDriver; import com.google.android.droiddriver.Poller; import com.google.android.droiddriver.UiElement; import com.google.android.droiddriver.actions.SingleKeyAction; -import com.google.android.droiddriver.actions.SwipeAction; import com.google.android.droiddriver.exceptions.ElementNotFoundException; import com.google.android.droiddriver.exceptions.TimeoutException; import com.google.android.droiddriver.finders.By; @@ -149,8 +148,13 @@ public class StepBasedScroller implements Scroller { container.perform(MOVE_HOME); Logs.log(Log.DEBUG, "MOVE_HOME used"); } else { - // Fling to beginning - container.perform(SwipeAction.toFling(backwardDirection)); + // Fling to beginning is not reliable; scroll to beginning + // container.perform(SwipeAction.toFling(backwardDirection)); + for (int i = 0; i < maxScrolls; i++) { + if (!scrollStepStrategy.scroll(driver, containerFinder, backwardDirection)) { + break; + } + } } } else { // search backward |