package io.appium.android.bootstrap.handler; import android.view.MotionEvent.PointerCoords; import com.android.uiautomator.common.ReflectionUtils; import io.appium.android.bootstrap.*; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.lang.reflect.Method; import static io.appium.android.bootstrap.utils.API.API_18; public class MultiPointerGesture extends CommandHandler { private double computeLongestTime(final JSONArray actions) throws JSONException { double max = 0.0; for (int i = 0; i < actions.length(); i++) { final JSONArray gestures = actions.getJSONArray(i); final double endTime = gestures.getJSONObject(gestures.length() - 1) .getDouble("time"); if (endTime > max) { max = endTime; } } return max; } private PointerCoords createPointerCoords(final JSONObject obj) throws JSONException { final JSONObject o = obj.getJSONObject("touch"); final int x = o.getInt("x"); final int y = o.getInt("y"); final PointerCoords p = new PointerCoords(); p.size = 1; p.pressure = 1; p.x = x; p.y = y; return p; } @Override public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { try { final PointerCoords[][] pcs = parsePointerCoords(command); if (command.isElementCommand()) { final AndroidElement el = command.getElement(); if (el.performMultiPointerGesture(pcs)) { return getSuccessResult("OK"); } else { return getErrorResult("Unable to perform multi pointer gesture"); } } else { if (API_18) { final ReflectionUtils utils = new ReflectionUtils(); final Method pmpg = utils.getControllerMethod("performMultiPointerGesture", PointerCoords[][].class); final Boolean rt = (Boolean) pmpg.invoke(utils.getController(), (Object) pcs); if (rt) { return getSuccessResult("OK"); } else { return getErrorResult("Unable to perform multi pointer gesture"); } } else { Logger.error("Device does not support API < 18!"); return new AndroidCommandResult(WDStatus.UNKNOWN_ERROR, "Cannot perform multi pointer gesture on device below API level 18"); } } } catch (final Exception e) { Logger.debug("Exception: " + e); e.printStackTrace(); return new AndroidCommandResult(WDStatus.UNKNOWN_ERROR, e.getMessage()); } } private PointerCoords[] gesturesToPointerCoords(final double maxTime, final JSONArray gestures) throws JSONException { // gestures, e.g.: // [ // {"touch":{"y":529.5,"x":120},"time":0.2}, // {"touch":{"y":529.5,"x":130},"time":0.4}, // {"touch":{"y":454.5,"x":140},"time":0.6}, // {"touch":{"y":304.5,"x":150},"time":0.8} // ] // From the docs: // "Steps are injected about 5 milliseconds apart, so 100 steps may take // around 0.5 seconds to complete." final int steps = (int) (maxTime * 200) + 2; final PointerCoords[] pc = new PointerCoords[steps]; int i = 1; JSONObject current = gestures.getJSONObject(0); double currentTime = current.getDouble("time"); double runningTime = 0.0; final int gesturesLength = gestures.length(); for (int j = 0; j < steps; j++) { if (runningTime > currentTime && i < gesturesLength) { current = gestures.getJSONObject(i++); currentTime = current.getDouble("time"); } pc[j] = createPointerCoords(current); runningTime += 0.005; } return pc; } private PointerCoords[][] parsePointerCoords(final AndroidCommand command) throws JSONException { final JSONArray actions = (org.json.JSONArray) command.params().get( "actions"); final double time = computeLongestTime(actions); final PointerCoords[][] pcs = new PointerCoords[actions.length()][]; for (int i = 0; i < actions.length(); i++) { final JSONArray gestures = actions.getJSONArray(i); pcs[i] = gesturesToPointerCoords(time, gestures); } return pcs; } }
多点触控根据你传递过来的参数决定,如果参数是一个控件元素,那么就要调用performMultiPointerGesture方法,如果参数是一系列的点,那么就要调用反射。那么具体来看看2个方法细节。
performMultiPointerGesture
public boolean performMultiPointerGesture(PointerCoords[] ...touches) { try { if (API_18) { // The compile-time SDK expects the wrong arguments, but the runtime // version in the emulator is correct. So we cannot do: // `return el.performMultiPointerGesture(touches);` // Instead we need to use Reflection to do it all at runtime. Method method = this.el.getClass().getMethod("performMultiPointerGesture", PointerCoords[][].class); Boolean rt = (Boolean)method.invoke(this.el, (Object)touches); return rt; } else { Logger.error("Device does not support API < 18!"); return false; } } catch (final Exception e) { Logger.error("Exception: " + e + " (" + e.getMessage() + ")"); return false; } }
UiObject中有直接可以调用的performMultiPointerGesture方法,为什么还要用反射呢。上面的方法里的注释是这样解释的:编译的时候sdk会认为参数是错误的,但是运行时却认为是正确的,所以只有在运行时调用才能保证正确性。反射调用的就是运行时的环境,所以它使用了反射调用了performMultiPointerGesture。
在api18以上的版本中才有传点组的方法可调用,所以先判断sdk的版本。如果api在18以上,那么就要调用InteractionController..performMultiPointerGesture的方法来执行