Skip to content

Multitouch #8

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 3 commits into from
Apr 18, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 187 additions & 1 deletion src/io/appium/java_client/AppiumDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package io.appium.java_client;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.openqa.selenium.*;
import org.openqa.selenium.remote.*;
Expand Down Expand Up @@ -50,6 +51,8 @@ public AppiumDriver(URL remoteAddress, Capabilities desiredCapabilities){
.put(HIDE_KEYBOARD, postC("/session/:sessionId/appium/device/hide_keyboard"))
.put(PUSH_FILE, postC("/session/:sessionId/appium/device/push_file"))
.put(RUN_APP_IN_BACKGROUND, postC("/session/:sessionId/appium/app/background"))
.put(PERFORM_TOUCH_ACTION, postC("/session/:sessionId/touch/perform"))
.put(PERFORM_MULTI_TOUCH, postC("/session/:sessionId/touch/multi/perform"))
;
ImmutableMap<String, CommandInfo> mobileCommands = builder.build();

Expand Down Expand Up @@ -176,14 +179,187 @@ public void runAppInBackground(int seconds) {
execute(RUN_APP_IN_BACKGROUND, ImmutableMap.of("seconds", seconds));
}

/**
* Performs a chain of touch actions, which together can be considered an entire gesture.
* See the Webriver 3 spec https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html
*
* It's more convenient to call the perform() method of the TouchAction object itself.
*
* @param touchAction A TouchAction object, which contains a list of individual touch actions to perform
* @return the same touchaction object
*/
public TouchAction performTouchAction(TouchAction touchAction) {
ImmutableMap<String, ImmutableList> parameters = touchAction.getParameters();
touchAction.clearParameters();
execute(PERFORM_TOUCH_ACTION, parameters);

return touchAction;
}

/**
* Performs multiple TouchAction gestures at the same time, to simulate multiple fingers/touch inputs.
* See the Webriver 3 spec https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html
*
* It's more convenient to call the perform() method of the MultiTouchAction object.
*
* @param multiAction the MultiTouchAction object to perform.
*/
public void performMultiTouchAction(MultiTouchAction multiAction) {
ImmutableMap<String, ImmutableList> parameters = multiAction.getParameters();
execute(PERFORM_MULTI_TOUCH, parameters);
}

/**
* Convenience method for tapping the center of an element on the screen
* @param fingers number of fingers/appendages to tap with
* @param element element to tap
* @param duration how long between pressing down, and lifting fingers/appendages
*/
public void tap(int fingers, WebElement element, int duration) {
MultiTouchAction multiTouch = new MultiTouchAction(this);

for (int i = 0; i < fingers; i++) {
multiTouch.add(createTap(element, duration));
}

multiTouch.perform();
}

/**
* Convenience method for tapping a position on the screen
* @param fingers number of fingers/appendages to tap with
* @param x x coordinate
* @param y y coordinate
* @param duration
*/
public void tap(int fingers, int x, int y, int duration) {
MultiTouchAction multiTouch = new MultiTouchAction(this);

for (int i = 0; i < fingers; i++) {
multiTouch.add(createTap(x, y, duration));
}

multiTouch.perform();
}

/**
* Convenience method for swiping across the screen
* @param startx starting x coordinate
* @param starty starting y coordinate
* @param endx ending x coordinate
* @param endy ending y coordinate
* @param duration amount of time in milliseconds for the entire swipe action to take
*/
public void swipe(int startx, int starty, int endx, int endy, int duration) {
TouchAction touchAction = new TouchAction(this);

//appium converts press-wait-moveto-release to a swipe action
touchAction.press(startx, starty).waitAction(duration).moveTo(endx, endy).release();

touchAction.perform();
}

/**
* Convenience method for pinching an element on the screen.
* "pinching" refers to the action of two appendages pressing the screen and sliding towards each other.
* NOTE:
* This convenience method places the initial touches around the element, if this would happen to place one of them
* off the screen, appium with return an outOfBounds error. In this case, revert to using the MultiTouchAction api
* instead of this method.
*
* @param el The element to pinch
*/
public void pinch(WebElement el) {
MultiTouchAction multiTouch = new MultiTouchAction(this);

Dimension dimensions = el.getSize();
Point upperLeft = el.getLocation();
Point center = new Point(upperLeft.getX() + dimensions.getWidth() / 2, upperLeft.getY() + dimensions.getHeight() / 2);

TouchAction action0 = new TouchAction(this).press(el, center.getX(), center.getY() - 100).moveTo(el).release();
TouchAction action1 = new TouchAction(this).press(el, center.getX(), center.getY() + 100).moveTo(el).release();

multiTouch.add(action0).add(action1);

multiTouch.perform();
}

/**
* Convenience method for pinching an element on the screen.
* "pinching" refers to the action of two appendages pressing the screen and sliding towards each other.
* NOTE:
* This convenience method places the initial touches around the element at a distance, if this would happen to place
* one of them off the screen, appium will return an outOfBounds error. In this case, revert to using the
* MultiTouchAction api instead of this method.
*
* @param x x coordinate to terminate the pinch on
* @param y y coordinate to terminate the pinch on
*/
public void pinch(int x, int y) {
MultiTouchAction multiTouch = new MultiTouchAction(this);

TouchAction action0 = new TouchAction(this).press(x, y-100).moveTo(x, y).release();
TouchAction action1 = new TouchAction(this).press(x, y+100).moveTo(x, y).release();

multiTouch.add(action0).add(action1);

multiTouch.perform();
}

/**
* Convenience method for "zooming in" on an element on the screen.
* "zooming in" refers to the action of two appendages pressing the screen and sliding away from each other.
* NOTE:
* This convenience method slides touches away from the element, if this would happen to place one of them
* off the screen, appium will return an outOfBounds error. In this case, revert to using the MultiTouchAction api
* instead of this method.
*
* @param el The element to pinch
*/
public void zoom(WebElement el) {
MultiTouchAction multiTouch = new MultiTouchAction(this);

Dimension dimensions = el.getSize();
Point upperLeft = el.getLocation();
Point center = new Point(upperLeft.getX() + dimensions.getWidth() / 2, upperLeft.getY() + dimensions.getHeight() / 2);

TouchAction action0 = new TouchAction(this).press(el).moveTo(el, center.getX(), center.getY() - 100).release();
TouchAction action1 = new TouchAction(this).press(el).moveTo(el, center.getX(), center.getY() + 100).release();

multiTouch.add(action0).add(action1);

multiTouch.perform();
}

/**
* Convenience method for "zooming in" on an element on the screen.
* "zooming in" refers to the action of two appendages pressing the screen and sliding away from each other.
* NOTE:
* This convenience method slides touches away from the element, if this would happen to place one of them
* off the screen, appium will return an outOfBounds error. In this case, revert to using the MultiTouchAction api
* instead of this method.
*
* @param x x coordinate to start zoom on
* @param y y coordinate to start zoom on
*/
public void zoom(int x, int y) {
MultiTouchAction multiTouch = new MultiTouchAction(this);

TouchAction action0 = new TouchAction(this).press(x, y).moveTo(x, y-100).release();
TouchAction action1 = new TouchAction(this).press(x, y).moveTo(x, y+100).release();

multiTouch.add(action0).add(action1);

multiTouch.perform();
}


@Override
public WebDriver context(String name) {
if (name == null) {
throw new IllegalArgumentException("Must supply a context name");
}


execute(DriverCommand.SWITCH_TO_CONTEXT, ImmutableMap.of("name", name));
return AppiumDriver.this;
}
Expand Down Expand Up @@ -239,6 +415,16 @@ public List<WebElement> findElementsByAccessibilityId(String using) {
return findElements("accessibility id", using);
}

private TouchAction createTap(WebElement element, int duration) {
TouchAction tap = new TouchAction(this);
return tap.press(element).waitAction(duration).release();
}

private TouchAction createTap(int x, int y, int duration) {
TouchAction tap = new TouchAction(this);
return tap.press(x, y).waitAction(duration).release();
}

private static CommandInfo getC(String url) {
return new CommandInfo(url, HttpVerb.GET);
}
Expand Down
2 changes: 2 additions & 0 deletions src/io/appium/java_client/MobileCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public interface MobileCommand {
String PUSH_FILE = "pushFile";
String HIDE_KEYBOARD = "hideKeyboard";
String RUN_APP_IN_BACKGROUND = "runAppInBackground";
String PERFORM_TOUCH_ACTION = "performTouchAction";
String PERFORM_MULTI_TOUCH = "performMultiTouch";


}
3 changes: 3 additions & 0 deletions src/io/appium/java_client/MobileDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ public interface MobileDriver extends WebDriver, ContextAware {

public Response execute(String driverCommand, Map<String, ?> parameters);

public TouchAction performTouchAction(TouchAction touchAction);

public void performMultiTouchAction(MultiTouchAction multiAction);
}
81 changes: 81 additions & 0 deletions src/io/appium/java_client/MultiTouchAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
+Copyright 2014 Appium contributors
+Copyright 2014 Software Freedom Conservancy
+
+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.java_client;


import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

/**
* Used for Webdriver 3 multi-touch gestures
* See the Webriver 3 spec https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html
*
* The MultiTouchAction object is a collection of TouchAction objects
* (remember that TouchAction objects are in turn, a chain of individual actions)
*
* Add multiple TouchAction objects using the add() method.
* When perform() method is called, all actions are sent to the driver.
*
* The driver performs the first step of each TouchAction object simultaneously as a multi-touch "execution group".
* Conceptually, the number of TouchAction objects added to the MultiTouchAction is equal to the number of "fingers" or
* other appendages or tools touching the screen at the same time as part of this multi-gesture.
* Then the driver performs the second step of each TouchAction object and another "execution group", and the third, and so on.
*
* Using a waitAction() action within a TouchAction takes up one of the slots in an "execution group", so these can be used to
* sync up complex actions.
*
* Calling perform() sends the action command to the Mobile Driver. Otherwise, more and more actions can be chained.
*/
public class MultiTouchAction {

private MobileDriver driver;
ImmutableList.Builder actions;

public MultiTouchAction(MobileDriver driver) {
this.driver = driver;
actions = ImmutableList.builder();
}

/**
* Add a TouchAction to this multi-touch gesture
* @param action TouchAction to add to this gesture
* @return This MultiTouchAction, for chaining
*/
public MultiTouchAction add(TouchAction action) {
actions.add(action);

return this;
}

/**
* Perform the multi-touch action on the mobile driver.
*/
public void perform() {
driver.performMultiTouchAction(this);
}

protected ImmutableMap getParameters() {
ImmutableList.Builder listOfActionChains = ImmutableList.builder();
ImmutableList<TouchAction> touchActions = actions.build();

for (TouchAction action : touchActions) {
listOfActionChains.add(action.getParameters().get("actions"));
}
return ImmutableMap.of("actions", listOfActionChains.build());
}
}
Loading