diff options
Diffstat (limited to 'src/io/appium/droiddriver/actions/SwipeAction.java')
-rw-r--r-- | src/io/appium/droiddriver/actions/SwipeAction.java | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/io/appium/droiddriver/actions/SwipeAction.java b/src/io/appium/droiddriver/actions/SwipeAction.java new file mode 100644 index 0000000..f43f546 --- /dev/null +++ b/src/io/appium/droiddriver/actions/SwipeAction.java @@ -0,0 +1,220 @@ +/* + * 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.actions; + +import android.graphics.Rect; +import android.os.SystemClock; +import android.view.ViewConfiguration; + +import io.appium.droiddriver.UiElement; +import io.appium.droiddriver.exceptions.ActionException; +import io.appium.droiddriver.scroll.Direction.PhysicalDirection; +import io.appium.droiddriver.util.Events; +import io.appium.droiddriver.util.Strings; +import io.appium.droiddriver.util.Strings.ToStringHelper; + +/** + * An action that swipes the touch screen. + */ +public class SwipeAction extends EventAction implements ScrollAction { + // Milliseconds between synthesized ACTION_MOVE events. + // 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; + /** + * The magic number from UiAutomator. This value is empirical. If it actually + * results in a fling, you can change it with {@link #setScrollSteps}. + */ + private static int scrollSteps = 55; + private static int flingSteps = 3; + + /** Returns the {@link #scrollSteps} used in {@link #toScroll}. */ + public static int getScrollSteps() { + return scrollSteps; + } + + /** Sets the {@link #scrollSteps} used in {@link #toScroll}. */ + public static void setScrollSteps(int scrollSteps) { + SwipeAction.scrollSteps = scrollSteps; + } + + /** Returns the {@link #flingSteps} used in {@link #toFling}. */ + public static int getFlingSteps() { + return flingSteps; + } + + /** Sets the {@link #flingSteps} used in {@link #toFling}. */ + public static void setFlingSteps(int flingSteps) { + SwipeAction.flingSteps = flingSteps; + } + + /** + * Gets {@link SwipeAction} instances for scrolling. + * <p> + * Note: This may result in flinging instead of scrolling, depending on the + * size of the target UiElement and the SDK version of the device. If it does + * not behave as expected, you can change steps with {@link #setScrollSteps}. + * </p> + * + * @param direction specifies where the view port will move, instead of the + * finger. + * @see ViewConfiguration#getScaledMinimumFlingVelocity + */ + public static SwipeAction toScroll(PhysicalDirection direction) { + return new SwipeAction(direction, scrollSteps); + } + + /** + * Gets {@link SwipeAction} instances for flinging. + * <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 change steps with {@link #setFlingSteps}. + * </p> + * + * @param direction specifies where the view port will move, instead of the + * finger. + * @see ViewConfiguration#getScaledMinimumFlingVelocity + */ + public static SwipeAction toFling(PhysicalDirection direction) { + return new SwipeAction(direction, flingSteps); + } + + private final PhysicalDirection direction; + private final boolean drag; + private final int steps; + private final float topMarginRatio; + private final float leftMarginRatio; + private final float bottomMarginRatio; + private final float rightMarginRatio; + + /** + * Defaults timeoutMillis to 1000 and no drag. + */ + public SwipeAction(PhysicalDirection direction, int steps) { + this(direction, steps, false, 1000L); + } + + /** + * Defaults all margin ratios to 0.1F. + */ + public SwipeAction(PhysicalDirection direction, int steps, boolean drag, long timeoutMillis) { + this(direction, steps, drag, timeoutMillis, 0.1F, 0.1F, 0.1F, 0.1F); + } + + /** + * @param direction the scroll direction specifying where the view port will + * move, instead of the finger. + * @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 the value returned by {@link #getTimeoutMillis} + * @param topMarginRatio margin ratio from top + * @param leftMarginRatio margin ratio from left + * @param bottomMarginRatio margin ratio from bottom + * @param rightMarginRatio margin ratio from right + */ + public SwipeAction(PhysicalDirection direction, int steps, boolean drag, long timeoutMillis, + float topMarginRatio, float leftMarginRatio, float bottomMarginRatio, float rightMarginRatio) { + super(timeoutMillis); + this.direction = direction; + this.steps = Math.max(2, steps); + this.drag = drag; + this.topMarginRatio = topMarginRatio; + this.bottomMarginRatio = bottomMarginRatio; + this.leftMarginRatio = leftMarginRatio; + this.rightMarginRatio = rightMarginRatio; + } + + @Override + public boolean perform(InputInjector injector, UiElement element) { + Rect elementRect = element.getVisibleBounds(); + + int topMargin = (int) (elementRect.height() * topMarginRatio); + int bottomMargin = (int) (elementRect.height() * bottomMarginRatio); + int leftMargin = (int) (elementRect.width() * leftMarginRatio); + int rightMargin = (int) (elementRect.width() * rightMarginRatio); + int adjustedbottom = elementRect.bottom - bottomMargin; + int adjustedTop = elementRect.top + topMargin; + int adjustedLeft = elementRect.left + leftMargin; + int adjustedRight = elementRect.right - rightMargin; + int startX; + int startY; + int endX; + int endY; + + switch (direction) { + case DOWN: + startX = elementRect.centerX(); + startY = adjustedbottom; + endX = elementRect.centerX(); + endY = adjustedTop; + break; + case UP: + startX = elementRect.centerX(); + startY = adjustedTop; + endX = elementRect.centerX(); + endY = adjustedbottom; + break; + case LEFT: + startX = adjustedLeft; + startY = elementRect.centerY(); + endX = adjustedRight; + endY = elementRect.centerY(); + break; + case RIGHT: + startX = adjustedRight; + startY = elementRect.centerY(); + endX = adjustedLeft; + endY = elementRect.centerY(); + break; + default: + throw new ActionException("Unknown scroll direction: " + direction); + } + + double xStep = ((double) (endX - startX)) / steps; + double yStep = ((double) (endY - startY)) / steps; + + // First touch starts exactly at the point requested + long downTime = Events.touchDown(injector, startX, startY); + SystemClock.sleep(ACTION_MOVE_INTERVAL); + if (drag) { + SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5f)); + } + for (int i = 1; i < steps; i++) { + Events.touchMove(injector, downTime, startX + (int) (xStep * i), startY + (int) (yStep * i)); + SystemClock.sleep(ACTION_MOVE_INTERVAL); + } + if (drag) { + // Hold final position for a little bit to simulate drag. + SystemClock.sleep(100); + } + Events.touchUp(injector, downTime, endX, endY); + return true; + } + + @Override + public String toString() { + ToStringHelper toStringHelper = Strings.toStringHelper(this); + toStringHelper.addValue(direction); + toStringHelper.add("steps", steps); + if (drag) { + toStringHelper.addValue("drag"); + } + return toStringHelper.toString(); + } +} |