diff --git a/.gitignore b/.gitignore index e55f86e7f..04f0265e5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ app/android/AndroidManifest.xml .*~ *~ uiautomator/bootstrap/target/ +org.eclipse.ltk.core.refactoring.prefs diff --git a/uiautomator/bootstrap/src/io/appium/android/bootstrap/AndroidElement.java b/uiautomator/bootstrap/src/io/appium/android/bootstrap/AndroidElement.java index 9c6ef813b..eb2afad79 100644 --- a/uiautomator/bootstrap/src/io/appium/android/bootstrap/AndroidElement.java +++ b/uiautomator/bootstrap/src/io/appium/android/bootstrap/AndroidElement.java @@ -1,6 +1,8 @@ package io.appium.android.bootstrap; +import io.appium.android.bootstrap.exceptions.InvalidCoordinatesException; import io.appium.android.bootstrap.exceptions.NoAttributeFoundException; +import io.appium.android.bootstrap.utils.Point; import android.graphics.Rect; import com.android.uiautomator.core.UiObject; @@ -29,6 +31,63 @@ public class AndroidElement { return el.click(); } + public Point getAbsolutePosition(final Double X, final Double Y) + throws UiObjectNotFoundException, InvalidCoordinatesException { + final Point point = new Point(X, Y); + return getAbsolutePosition(point, false); + } + + public Point getAbsolutePosition(final Double X, final Double Y, + final boolean boundsChecking) throws UiObjectNotFoundException, + InvalidCoordinatesException { + final Point point = new Point(X, Y); + return getAbsolutePosition(point, boundsChecking); + } + + public Point getAbsolutePosition(final Point point) + throws UiObjectNotFoundException, InvalidCoordinatesException { + return getAbsolutePosition(point, false); + } + + public Point getAbsolutePosition(final Point point, + final boolean boundsChecking) throws UiObjectNotFoundException, + InvalidCoordinatesException { + final Rect rect = el.getBounds(); + final Point pos = new Point(); + + if (point.x == 0) { + pos.x = rect.width() * 0.5 + rect.left; + } else if (point.x <= 1) { + pos.x = rect.width() * point.x + rect.left; + } else { + pos.x = rect.left + point.x; + } + if (boundsChecking == true) { + if (pos.x > rect.right || pos.x < rect.left) { + throw new InvalidCoordinatesException("X coordinate (" + + pos.x.toString() + " is outside of element rect: " + + rect.toShortString()); + } + } + + if (point.y == 0) { + pos.y = rect.height() * 0.5 + rect.top; + } else if (point.y <= 1) { + pos.y = rect.height() * point.y + rect.top; + } else { + pos.y = rect.left + point.y; + } + if (boundsChecking == true) { + if (pos.y > rect.bottom || pos.y < rect.top) { + throw new InvalidCoordinatesException("Y coordinate (" + + pos.y.toString() + " is outside of element rect: " + + rect.toShortString()); + } + } + + return pos; + } + public boolean getBoolAttribute(final String attr) throws UiObjectNotFoundException, NoAttributeFoundException { boolean res = false; @@ -98,4 +157,5 @@ public class AndroidElement { public boolean setText(final String text) throws UiObjectNotFoundException { return el.setText(text); } + } diff --git a/uiautomator/bootstrap/src/io/appium/android/bootstrap/CommandHandler.java b/uiautomator/bootstrap/src/io/appium/android/bootstrap/CommandHandler.java index ee46ac299..1684bb485 100644 --- a/uiautomator/bootstrap/src/io/appium/android/bootstrap/CommandHandler.java +++ b/uiautomator/bootstrap/src/io/appium/android/bootstrap/CommandHandler.java @@ -1,5 +1,8 @@ package io.appium.android.bootstrap; +import io.appium.android.bootstrap.exceptions.InvalidCoordinatesException; +import io.appium.android.bootstrap.utils.Point; + import java.util.ArrayList; import org.json.JSONException; @@ -38,6 +41,36 @@ public abstract class CommandHandler { return retPos; } + protected static Point GetAbsPos(final Point point) + throws InvalidCoordinatesException { + final UiDevice d = UiDevice.getInstance(); + final Point retPos = new Point(point); // copy inputed point + + final Double width = new Double(d.getDisplayWidth()); + if (point.x < 1) { + retPos.x = width * point.x; + } + + if (retPos.x > width || retPos.x < 0) { + throw new InvalidCoordinatesException("X coordinate (" + + retPos.x.toString() + " is outside of screen width: " + + width.toString()); + } + + final Double height = new Double(d.getDisplayHeight()); + if (point.y < 1) { + retPos.y = height * point.y; + } + + if (retPos.y > height || retPos.y < 0) { + throw new InvalidCoordinatesException("Y coordinate (" + + retPos.y.toString() + " is outside of screen height: " + + height.toString()); + } + + return retPos; + } + /** * Abstract method that handlers must implement. * diff --git a/uiautomator/bootstrap/src/io/appium/android/bootstrap/exceptions/InvalidCoordinatesException.java b/uiautomator/bootstrap/src/io/appium/android/bootstrap/exceptions/InvalidCoordinatesException.java new file mode 100644 index 000000000..cd3c47ff6 --- /dev/null +++ b/uiautomator/bootstrap/src/io/appium/android/bootstrap/exceptions/InvalidCoordinatesException.java @@ -0,0 +1,14 @@ +package io.appium.android.bootstrap.exceptions; + +/** + * An exception that is thrown when an invalid coordinate is used. + * + * @author xuru + * @param msg + * A descriptive message describing the error. + */ +public class InvalidCoordinatesException extends Exception { + public InvalidCoordinatesException(final String msg) { + super(msg); + } +} diff --git a/uiautomator/bootstrap/src/io/appium/android/bootstrap/handler/Swipe.java b/uiautomator/bootstrap/src/io/appium/android/bootstrap/handler/Swipe.java index ca93d7a52..7efc6039c 100644 --- a/uiautomator/bootstrap/src/io/appium/android/bootstrap/handler/Swipe.java +++ b/uiautomator/bootstrap/src/io/appium/android/bootstrap/handler/Swipe.java @@ -2,14 +2,19 @@ package io.appium.android.bootstrap.handler; import io.appium.android.bootstrap.AndroidCommand; import io.appium.android.bootstrap.AndroidCommandResult; +import io.appium.android.bootstrap.AndroidElement; import io.appium.android.bootstrap.CommandHandler; +import io.appium.android.bootstrap.Logger; +import io.appium.android.bootstrap.exceptions.ElementNotInHashException; +import io.appium.android.bootstrap.exceptions.InvalidCoordinatesException; +import io.appium.android.bootstrap.utils.Point; -import java.util.ArrayList; import java.util.Hashtable; import org.json.JSONException; import com.android.uiautomator.core.UiDevice; +import com.android.uiautomator.core.UiObjectNotFoundException; /** * This handler is used to swipe. @@ -33,26 +38,40 @@ public class Swipe extends CommandHandler { public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { final Hashtable params = command.params(); - final Double startX = Double.parseDouble(params.get("startX").toString()); - final Double startY = Double.parseDouble(params.get("startY").toString()); - final Double endX = Double.parseDouble(params.get("endX").toString()); - final Double endY = Double.parseDouble(params.get("endY").toString()); + final Point start = new Point(params.get("startX"), params.get("startY")); + final Point end = new Point(params.get("endX"), params.get("endY")); final Integer steps = (Integer) params.get("steps"); + final UiDevice device = UiDevice.getInstance(); + + Point absStartPos = new Point(); + Point absEndPos = new Point(); if (command.isElementCommand()) { - // Can this command run on the element it's self? - // swipe on an element is handled by 4 different commands: - // swipeDown, swipeLeft, swipeRight, and swipeUp - // We have to figure out which to call and position it correctly... + try { + final AndroidElement el = command.getElement(); + absStartPos = el.getAbsolutePosition(start); + absEndPos = el.getAbsolutePosition(end, false); + } catch (final ElementNotInHashException e) { + return getErrorResult(e.getMessage()); + } catch (final UiObjectNotFoundException e) { + return getErrorResult(e.getMessage()); + } catch (final InvalidCoordinatesException e) { + return getErrorResult(e.getMessage()); + } } else { - final UiDevice device = UiDevice.getInstance(); - final Double[] coords = { startX, startY, endX, endY }; - final ArrayList posVals = absPosFromCoords(coords); - final boolean rv = device.swipe(posVals.get(0), posVals.get(1), - posVals.get(2), posVals.get(3), steps); - return getSuccessResult(rv); + try { + absStartPos = GetAbsPos(start); + absEndPos = GetAbsPos(end); + } catch (final InvalidCoordinatesException e) { + return getErrorResult(e.getMessage()); + } } - return getErrorResult("Error in swiping..."); + Logger.info("Swiping from " + absStartPos.toString() + " to " + + absEndPos.toString() + " with steps: " + steps.toString()); + final boolean rv = device.swipe(absStartPos.x.intValue(), + absStartPos.y.intValue(), absEndPos.x.intValue(), + absEndPos.y.intValue(), steps); + return getSuccessResult(rv); } } diff --git a/uiautomator/bootstrap/src/io/appium/android/bootstrap/utils/Point.java b/uiautomator/bootstrap/src/io/appium/android/bootstrap/utils/Point.java new file mode 100644 index 000000000..a90367d76 --- /dev/null +++ b/uiautomator/bootstrap/src/io/appium/android/bootstrap/utils/Point.java @@ -0,0 +1,71 @@ +package io.appium.android.bootstrap.utils; + +public class Point { + + public Double x; + public Double y; + + public Point() { + x = 0.0; + y = 0.0; + } + + public Point(final Double x, final Double y) { + this.x = x; + this.y = y; + } + + public Point(final Object x, final Object y) { + this.x = Double.parseDouble(x.toString()); + this.y = Double.parseDouble(y.toString()); + } + + public Point(final Point other) { + x = other.x; + y = other.y; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Point other = (Point) obj; + if (x == null) { + if (other.x != null) { + return false; + } + } else if (!x.equals(other.x)) { + return false; + } + if (y == null) { + if (other.y != null) { + return false; + } + } else if (!y.equals(other.y)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (x == null ? 0 : x.hashCode()); + result = prime * result + (y == null ? 0 : y.hashCode()); + return result; + } + + @Override + public String toString() { + return "[x=" + x + ", y=" + y + "]"; + } + +}