bootstrapのMultipointerGesture

6037 ワード

MultiPointerGesture

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を使用します.

ポイントグループ


api 18以上のバージョンでは、ポインティンググループのメソッドが呼び出されるので、sdkのバージョンを先に判断します.apiが18以上の場合はInteractionControllerを呼び出す.performMultipointerGestureの方法で実行