Adds UiAutomator 'drag' APIs to UiObject and UiDevice

bug:7409880
Change-Id: I73e61d39f1c689ac8e376b9135ae8447c70cec56
diff --git a/uiautomator/api/current.txt b/uiautomator/api/current.txt
index 609c1b3..664fbbc 100644
--- a/uiautomator/api/current.txt
+++ b/uiautomator/api/current.txt
@@ -11,6 +11,7 @@
   public class UiDevice {
     method public void clearLastTraversedText();
     method public boolean click(int, int);
+    method public boolean drag(int, int, int, int, int);
     method public void dumpWindowHierarchy(java.lang.String);
     method public void freezeRotation() throws android.os.RemoteException;
     method public deprecated java.lang.String getCurrentActivityName();
@@ -67,6 +68,8 @@
     method public boolean clickAndWaitForNewWindow(long) throws com.android.uiautomator.core.UiObjectNotFoundException;
     method public boolean clickBottomRight() throws com.android.uiautomator.core.UiObjectNotFoundException;
     method public boolean clickTopLeft() throws com.android.uiautomator.core.UiObjectNotFoundException;
+    method public boolean dragTo(com.android.uiautomator.core.UiObject, int) throws com.android.uiautomator.core.UiObjectNotFoundException;
+    method public boolean dragTo(int, int, int) throws com.android.uiautomator.core.UiObjectNotFoundException;
     method public boolean exists();
     method protected android.view.accessibility.AccessibilityNodeInfo findAccessibilityNodeInfo(long);
     method public android.graphics.Rect getBounds() throws com.android.uiautomator.core.UiObjectNotFoundException;
diff --git a/uiautomator/library/core-src/com/android/uiautomator/core/InteractionController.java b/uiautomator/library/core-src/com/android/uiautomator/core/InteractionController.java
index 5c8723d..7d19d1e 100644
--- a/uiautomator/library/core-src/com/android/uiautomator/core/InteractionController.java
+++ b/uiautomator/library/core-src/com/android/uiautomator/core/InteractionController.java
@@ -424,6 +424,20 @@
      * @return true if the swipe executed successfully
      */
     public boolean swipe(int downX, int downY, int upX, int upY, int steps) {
+        return swipe(downX, downY, upX, upY, steps, false /*drag*/);
+    }
+
+    /**
+     * Handle swipes/drags in any direction.
+     * @param downX
+     * @param downY
+     * @param upX
+     * @param upY
+     * @param steps
+     * @param drag when true, the swipe becomes a drag swipe
+     * @return true if the swipe executed successfully
+     */
+    public boolean swipe(int downX, int downY, int upX, int upY, int steps, boolean drag) {
         boolean ret = false;
         int swipeSteps = steps;
         double xStep = 0;
@@ -438,6 +452,8 @@
 
         // first touch starts exactly at the point requested
         ret = touchDown(downX, downY);
+        if (drag)
+            SystemClock.sleep(mUiAutomatorBridge.getSystemLongPressTime());
         for(int i = 1; i < swipeSteps; i++) {
             ret &= touchMove(downX + (int)(xStep * i), downY + (int)(yStep * i));
             if(ret == false)
@@ -448,6 +464,8 @@
             // a preset delay.
             SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
         }
+        if (drag)
+            SystemClock.sleep(REGULAR_CLICK_LENGTH);
         ret &= touchUp(upX, upY);
         return(ret);
     }
diff --git a/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
index b86edb4..1b716f0 100644
--- a/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
+++ b/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
@@ -403,7 +403,26 @@
     public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
         Tracer.trace(startX, startY, endX, endY, steps);
         return mUiAutomationBridge.getInteractionController()
-                .scrollSwipe(startX, startY, endX, endY, steps);
+                .swipe(startX, startY, endX, endY, steps);
+    }
+
+    /**
+     * Performs a swipe from one coordinate to another using the number of steps
+     * to determine smoothness and speed. Each step execution is throttled to 5ms
+     * per step. So for a 100 steps, the swipe will take about 1/2 second to complete.
+     *
+     * @param startX
+     * @param startY
+     * @param endX
+     * @param endY
+     * @param steps is the number of move steps sent to the system
+     * @return false if the operation fails or the coordinates are invalid
+     * @since API Level 18
+     */
+    public boolean drag(int startX, int startY, int endX, int endY, int steps) {
+        Tracer.trace(startX, startY, endX, endY, steps);
+        return mUiAutomationBridge.getInteractionController()
+                .swipe(startX, startY, endX, endY, steps, true);
     }
 
     /**
diff --git a/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java b/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
index 8a49a88..ab95545 100644
--- a/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
+++ b/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
@@ -188,6 +188,42 @@
     }
 
     /**
+     * Performs a drag of this object to a destination UiObject. Note that the number of steps
+     * used can influence the drag speed and varying speeds may impact the results. Consider
+     * evaluating different speeds when testing this method.
+     *
+     * @param destObj
+     * @param steps usually 40 steps. More or less to change the speed.
+     * @return true of successful
+     * @throws UiObjectNotFoundException
+     * @since API Level 18
+     */
+    public boolean dragTo(UiObject destObj, int steps) throws UiObjectNotFoundException {
+        Rect srcRect = getVisibleBounds();
+        Rect dstRect = destObj.getVisibleBounds();
+        return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(),
+                dstRect.centerX(), dstRect.centerY(), steps, true);
+    }
+
+    /**
+     * Performs a drag of this object to arbitrary coordinates. Note that the number of steps
+     * used will influence the drag speed and varying speeds may impact the results. Consider
+     * evaluating different speeds when testing this method.
+     *
+     * @param destX
+     * @param destY
+     * @param steps
+     * @return true of successful
+     * @throws UiObjectNotFoundException
+     * @since API Level 18
+     */
+    public boolean dragTo(int destX, int destY, int steps) throws UiObjectNotFoundException {
+        Rect srcRect = getVisibleBounds();
+        return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(), destX, destY,
+                steps, true);
+    }
+
+    /**
      * Perform the action on the UI element that is represented by this UiObject. Also see
      * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
      * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}.