mirror of
https://github.com/appium/appium.git
synced 2026-02-13 21:39:49 -06:00
Merge pull request #1945 from bootstraponline/master
Android longClick x, y, and duration support
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -34,8 +34,7 @@ sample-code/apps/ApiDemos
|
||||
*~
|
||||
org.eclipse.ltk.core.refactoring.prefs
|
||||
/selendroid
|
||||
.appiumconfig
|
||||
.appiumconfig.json
|
||||
.appiumconfig*
|
||||
build/
|
||||
.idea
|
||||
lib/devices/ios/uiauto/lib/status.js
|
||||
|
||||
@@ -113,8 +113,13 @@ androidController.click = function (elementId, cb) {
|
||||
this.proxy(["element:click", {elementId: elementId}], cb);
|
||||
};
|
||||
|
||||
androidController.touchLongClick = function (elementId, cb) {
|
||||
this.proxy(["element:touchLongClick", {elementId: elementId}], cb);
|
||||
androidController.touchLongClick = function (elementId, x, y, duration, cb) {
|
||||
var opts = {};
|
||||
if (elementId) opts.elementId = elementId;
|
||||
if (x) opts.x = x;
|
||||
if (y) opts.y = y;
|
||||
if (duration) opts.duration = duration;
|
||||
this.proxy(["element:touchLongClick", opts], cb);
|
||||
};
|
||||
|
||||
androidController.fireEvent = function (evt, elementId, value, cb) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import io.appium.android.bootstrap.exceptions.ElementNotInHashException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
@@ -38,7 +40,7 @@ public class TouchLongClick extends CommandHandler {
|
||||
* UiAutomator has a broken longClick. getAutomatorBridge is private so we
|
||||
* access the bridge via reflection to use the touchDown / touchUp methods.
|
||||
*/
|
||||
private boolean correctLongClick(final int x, final int y) {
|
||||
private boolean correctLongClick(final int x, final int y, final int duration) {
|
||||
try {
|
||||
/*
|
||||
* bridge.getClass() returns ShellUiAutomatorBridge on API 18/19 so use
|
||||
@@ -61,7 +63,7 @@ public class TouchLongClick extends CommandHandler {
|
||||
touchUp.setAccessible(true);
|
||||
|
||||
if ((Boolean) touchDown.invoke(controller, x, y)) {
|
||||
SystemClock.sleep(2000);
|
||||
SystemClock.sleep(duration);
|
||||
if ((Boolean) touchUp.invoke(controller, x, y)) {
|
||||
return true;
|
||||
}
|
||||
@@ -85,25 +87,68 @@ public class TouchLongClick extends CommandHandler {
|
||||
@Override
|
||||
public AndroidCommandResult execute(final AndroidCommand command)
|
||||
throws JSONException {
|
||||
if (!command.isElementCommand()) {
|
||||
return getErrorResult("Unable to long click without an element.");
|
||||
}
|
||||
|
||||
try {
|
||||
final AndroidElement el = command.getElement();
|
||||
final Hashtable<String, Object> params = command.params();
|
||||
AndroidElement el = null;
|
||||
int clickX = -1;
|
||||
int clickY = -1;
|
||||
|
||||
final Rect bounds = el.getVisibleBounds();
|
||||
final int x = bounds.centerX();
|
||||
final int y = bounds.centerY();
|
||||
boolean isElement = false;
|
||||
// isElementCommand doesn't check to see if we actually have an element
|
||||
// so getElement is used instead.
|
||||
try {
|
||||
if (command.getElement() != null) {
|
||||
isElement = true;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
}
|
||||
|
||||
if (correctLongClick(x, y)) {
|
||||
if (isElement) {
|
||||
// extract x and y from the element.
|
||||
el = command.getElement();
|
||||
|
||||
final Rect bounds = el.getVisibleBounds();
|
||||
clickX = bounds.centerX();
|
||||
clickY = bounds.centerY();
|
||||
} else { // no element so extract x and y from params
|
||||
final Object paramX = params.get("x");
|
||||
final Object paramY = params.get("y");
|
||||
double targetX = 0.5;
|
||||
double targetY = 0.5;
|
||||
if (paramX != null) {
|
||||
targetX = Double.parseDouble(paramX.toString());
|
||||
}
|
||||
|
||||
if (paramY != null) {
|
||||
targetY = Double.parseDouble(paramY.toString());
|
||||
}
|
||||
|
||||
final ArrayList<Integer> posVals = absPosFromCoords(new Double[] {
|
||||
targetX, targetY });
|
||||
clickX = posVals.get(0);
|
||||
clickY = posVals.get(1);
|
||||
}
|
||||
|
||||
final Object paramDuration = params.get("duration");
|
||||
int duration = 2000; // two seconds
|
||||
if (paramDuration != null) {
|
||||
duration = Integer.parseInt(paramDuration.toString());
|
||||
}
|
||||
|
||||
Logger.debug("longClick using element? " + isElement + " x: " + clickX
|
||||
+ ", y: " + clickY + ", duration: " + duration);
|
||||
if (correctLongClick(clickX, clickY, duration)) {
|
||||
return getSuccessResult(true);
|
||||
}
|
||||
|
||||
Logger.debug("Falling back to broken longClick");
|
||||
// if correctLongClick failed and we have an element
|
||||
// then uiautomator's longClick is used as a fallback.
|
||||
if (isElement) {
|
||||
Logger.debug("Falling back to broken longClick");
|
||||
|
||||
final boolean res = el.longClick();
|
||||
return getSuccessResult(res);
|
||||
final boolean res = el.longClick();
|
||||
return getSuccessResult(res);
|
||||
}
|
||||
} catch (final UiObjectNotFoundException e) {
|
||||
return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage());
|
||||
} catch (final ElementNotInHashException e) {
|
||||
@@ -111,5 +156,6 @@ public class TouchLongClick extends CommandHandler {
|
||||
} catch (final Exception e) {
|
||||
return getErrorResult(e.getMessage());
|
||||
}
|
||||
return getErrorResult("Failed to long click");
|
||||
}
|
||||
}
|
||||
@@ -256,8 +256,14 @@ exports.doClick = function (req, res) {
|
||||
|
||||
exports.touchLongClick = function (req, res) {
|
||||
var element = req.body.element;
|
||||
if (checkMissingParams(res, {element: element}, true)) {
|
||||
req.device.touchLongClick(element, getResponseHandler(req, res));
|
||||
var x = req.body.x;
|
||||
var y = req.body.y;
|
||||
var duration = req.body.duration;
|
||||
|
||||
if (element && checkMissingParams(res, {element: element}, true)) {
|
||||
req.device.touchLongClick(element, x, y, duration, getResponseHandler(req, res));
|
||||
} else if (checkMissingParams(res, {x: x, y: y}, true)) {
|
||||
req.device.touchLongClick(element, x, y, duration, getResponseHandler(req, res));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ describe("apidemo - gestures -", function () {
|
||||
.nodeify(done);
|
||||
});
|
||||
//todo: not working in nexus 7
|
||||
it(' should click via x/y pct', function (done) {
|
||||
it('should click via x/y pct', function (done) {
|
||||
// this test depends on having a certain size screen, obviously
|
||||
// I use a nexus something or other phone style thingo
|
||||
driver
|
||||
@@ -232,4 +232,73 @@ describe("apidemo - gestures -", function () {
|
||||
}).nodeify(done);
|
||||
});
|
||||
|
||||
});
|
||||
it('should long click via element value', function (done) {
|
||||
var element;
|
||||
|
||||
driver
|
||||
.elementsByTagName("text").then(function (els) { element = els[1]; })
|
||||
.then(function () { driver.execute("mobile: longClick", [{element: element.value}]); })
|
||||
.sleep(3000)
|
||||
.elementsByTagName("text").then(function (els) { return els[1]; }).text()
|
||||
.then(function (text) {
|
||||
["Accessibility Node Provider"].should.include(text);
|
||||
}).nodeify(done);
|
||||
});
|
||||
|
||||
it('should long click via element value with custom duration', function (done) {
|
||||
var element;
|
||||
|
||||
driver
|
||||
.elementsByTagName("text").then(function (els) { element = els[1]; })
|
||||
.then(function () { driver.execute("mobile: longClick", [{element: element.value, duration: 1000}]); })
|
||||
.sleep(3000)
|
||||
.elementsByTagName("text").then(function (els) { return els[1]; }).text()
|
||||
.then(function (text) {
|
||||
["Accessibility Node Provider"].should.include(text);
|
||||
}).nodeify(done);
|
||||
});
|
||||
|
||||
it('should long click via pixel value', function (done) {
|
||||
var element, location, elSize;
|
||||
|
||||
driver
|
||||
.elementsByTagName("text").then(function (els) { element = els[1]; })
|
||||
.then(function () { return element.getLocation(); })
|
||||
.then(function (loc) { location = loc; })
|
||||
.then(function () { return element.getSize(); })
|
||||
.then(function (size) { elSize = size; })
|
||||
.then(function () {
|
||||
var centerX = location.x + (elSize.width / 2);
|
||||
var centerY = location.y + (elSize.height / 2);
|
||||
driver.execute("mobile: longClick", [{x: centerX, y: centerY}]);
|
||||
})
|
||||
.sleep(3000)
|
||||
.elementsByTagName("text").then(function (els) { return els[1]; }).text()
|
||||
.then(function (text) {
|
||||
["Accessibility Node Provider"].should.include(text);
|
||||
}).nodeify(done);
|
||||
});
|
||||
|
||||
it('should long click via relative value', function (done) {
|
||||
var element, location, elSize, windowSize;
|
||||
|
||||
driver
|
||||
.elementsByTagName("text").then(function (els) { element = els[1]; })
|
||||
.then(function () { return element.getLocation(); })
|
||||
.then(function (loc) { location = loc; })
|
||||
.then(function () { return element.getSize(); })
|
||||
.then(function (size) { elSize = size; })
|
||||
.then(function () { return driver.getWindowSize(); })
|
||||
.then(function (size) { windowSize = size; })
|
||||
.then(function () {
|
||||
var relX = (location.x + (elSize.width / 2)) / windowSize.width;
|
||||
var relY = (location.y + (elSize.height / 2)) / windowSize.height;
|
||||
driver.execute("mobile: longClick", [{x: relX, y: relY}]);
|
||||
})
|
||||
.sleep(3000)
|
||||
.elementsByTagName("text").then(function (els) { return els[1]; }).text()
|
||||
.then(function (text) {
|
||||
["Accessibility Node Provider"].should.include(text);
|
||||
}).nodeify(done);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user