/*
   * 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, final int duration) {
    try {
      /*
       * bridge.getClass() returns ShellUiAutomatorBridge on API 18/19 so use
       * the super class.
       */

      final UiDevice device = UiDevice.getInstance();
      final Object bridge = enableField(device.getClass(), "mUiAutomationBridge").get(device);
      final Object controller =
          enableField(bridge.getClass().getSuperclass(), "mInteractionController").get(bridge);
      final Class<?> controllerClass = controller.getClass();

      Logger.debug("Finding methods on class: " + controllerClass);
      final Method touchDown = controllerClass.getDeclaredMethod("touchDown", int.class, int.class);
      touchDown.setAccessible(true);
      final Method touchUp = controllerClass.getDeclaredMethod("touchUp", int.class, int.class);
      touchUp.setAccessible(true);

      if ((Boolean) touchDown.invoke(controller, x, y)) {
        SystemClock.sleep(duration);
        if ((Boolean) touchUp.invoke(controller, x, y)) {
          return true;
        }
      }
      return false;

    } catch (final Exception e) {
      Logger.debug("Problem invoking correct long click: " + e);
      return false;
    }
  }
 private static Field enableField(final Class<?> clazz, final String field)
     throws SecurityException, NoSuchFieldException {
   Logger.debug("Updating class \"" + clazz + "\" to enable field \"" + field + "\"");
   final Field fieldObject = clazz.getDeclaredField(field);
   fieldObject.setAccessible(true);
   return fieldObject;
 }
Exemple #3
0
 protected void printEventDebugLine(final String methodName, final Integer... duration) {
   String extra = "";
   if (duration.length > 0) {
     extra = ", duration: " + duration[0];
   }
   Logger.debug(
       "Performing "
           + methodName
           + " using element? "
           + isElement
           + " x: "
           + clickX
           + ", y: "
           + clickY
           + extra);
 }
  /*
   * @param command The {@link AndroidCommand} used for this handler.
   *
   * @return {@link AndroidCommandResult}
   *
   * @throws JSONException
   *
   * @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android.
   * bootstrap.AndroidCommand)
   */
  @Override
  public AndroidCommandResult execute(final AndroidCommand command) throws JSONException {
    final Hashtable<String, Object> params = command.params();

    AndroidElement el;
    final String direction = params.get("direction").toString();
    final Integer percent = (Integer) params.get("percent");
    final Integer steps = (Integer) params.get("steps");
    try {
      el = command.getElement();
      if (el == null) {
        return getErrorResult(
            "Could not find an element with elementId: " + params.get("elementId"));
      }
    } catch (final Exception e) { // JSONException, NullPointerException, etc.
      return getErrorResult("Unknown error:" + e.getMessage());
    }

    Logger.debug(
        "Pinching "
            + direction
            + " "
            + percent.toString()
            + "%"
            + " with steps: "
            + steps.toString());
    boolean res;
    if (direction.equals("in")) {
      try {
        res = el.pinchIn(percent, steps);
      } catch (final UiObjectNotFoundException e) {
        return getErrorResult("Selector could not be matched to any UI element displayed");
      }
    } else {
      try {
        res = el.pinchOut(percent, steps);
      } catch (final UiObjectNotFoundException e) {
        return getErrorResult("Selector could not be matched to any UI element displayed");
      }
    }

    if (res) {
      return getSuccessResult(res);
    } else {
      return getErrorResult("Pinch did not complete successfully");
    }
  }
Exemple #5
0
  public boolean check() {
    if (start + delta < System.currentTimeMillis()) {
      // Send only one alert message...
      if (isDialogPresent() && (!alerted)) {
        Logger.info("Emitting system alert message");
        alerted = true;
      }

      // if the dialog went away, make sure we can send an alert again
      if (!isDialogPresent() && alerted) {
        alerted = false;
      }

      start = System.currentTimeMillis();
    }
    return false;
  }
Exemple #6
0
  @Override
  protected boolean executeTouchEvent() throws UiObjectNotFoundException {
    final Object paramDuration = params.get("duration");
    int duration = 2000; // two seconds
    if (paramDuration != null) {
      duration = Integer.parseInt(paramDuration.toString());
    }

    printEventDebugLine("TouchLongClick", duration);
    if (correctLongClick(clickX, clickY, duration)) {
      return true;
    }
    // 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");

      return el.longClick();
    }
    return false;
  }
Exemple #7
0
  /*
   * UiAutomator has a broken longClick, so we'll try to implement it using the
   * touchDown / touchUp events.
   */
  protected static boolean correctLongClick(final int x, final int y, final int duration) {
    try {
      /*
       * bridge.getClass() returns ShellUiAutomatorBridge on API 18/19 so use
       * the super class.
       */

      final ReflectionUtils utils = new ReflectionUtils();
      final Method touchDown = utils.getControllerMethod("touchDown", int.class, int.class);
      final Method touchUp = utils.getControllerMethod("touchUp", int.class, int.class);

      if ((Boolean) touchDown.invoke(utils.getController(), x, y)) {
        SystemClock.sleep(duration);
        if ((Boolean) touchUp.invoke(utils.getController(), x, y)) {
          return true;
        }
      }
      return false;

    } catch (final Exception e) {
      Logger.debug("Problem invoking correct long click: " + e);
      return false;
    }
  }
  /**
   * @param command The {@link AndroidCommand}
   * @return {@link AndroidCommandResult}
   * @throws JSONException
   * @see
   *     io.appium.android.bootstrap.CommandHandler#execute(io.appium.android.bootstrap.AndroidCommand)
   */
  @Override
  public AndroidCommandResult execute(final AndroidCommand command) throws JSONException {
    try {
      final Hashtable<String, Object> params = command.params();
      AndroidElement el = null;
      int clickX = -1;
      int clickY = -1;

      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 (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);
      }

      // 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);
      }
    } catch (final UiObjectNotFoundException e) {
      return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage());
    } catch (final ElementNotInHashException e) {
      return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage());
    } catch (final Exception e) {
      return getErrorResult(e.getMessage());
    }
    return getErrorResult("Failed to long click");
  }
Exemple #9
0
  /*
   * @param command The {@link AndroidCommand} used for this handler.
   *
   * @return {@link AndroidCommandResult}
   *
   * @throws JSONException
   *
   * @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android.
   * bootstrap.AndroidCommand)
   */
  @Override
  public AndroidCommandResult execute(final AndroidCommand command) throws JSONException {
    final Hashtable<String, Object> params = command.params();

    // only makes sense on a device
    final Strategy strategy = Strategy.fromString((String) params.get("strategy"));
    final String text = (String) params.get("selector");
    final String contextId = (String) params.get("context");

    Logger.debug(
        "Finding " + text + " using " + strategy.toString() + " with the contextId: " + contextId);

    final Boolean multiple = (Boolean) params.get("multiple");
    final boolean isXpath = strategy.equals("xpath");

    if (isXpath) {
      final JSONArray xpathPath = (JSONArray) params.get("path");
      final String xpathAttr = (String) params.get("attr");
      final String xpathConstraint = (String) params.get("constraint");
      final Boolean xpathSubstr = (Boolean) params.get("substr");

      try {
        if (multiple) {
          final UiSelector sel =
              getSelectorForXpath(xpathPath, xpathAttr, xpathConstraint, xpathSubstr);
          return getSuccessResult(fetchElements(sel, contextId));
        } else {
          final UiSelector sel =
              getSelectorForXpath(xpathPath, xpathAttr, xpathConstraint, xpathSubstr);
          return getSuccessResult(fetchElement(sel, contextId));
        }
      } catch (final AndroidCommandException e) {
        return getErrorResult(e.getMessage());
      } catch (final ElementNotFoundException e) {
        return getErrorResult(e.getMessage());
      } catch (final ElementNotInHashException e) {
        return getErrorResult(e.getMessage());
      } catch (final UiObjectNotFoundException e) {
        return getErrorResult(e.getMessage());
      }
    } else {
      try {
        final UiSelector sel = getSelector(strategy, text, multiple);
        if (multiple) {
          return getSuccessResult(fetchElements(sel, contextId));
        } else {
          return getSuccessResult(fetchElement(sel, contextId));
        }
      } catch (final InvalidStrategyException e) {
        return getErrorResult(e.getMessage());
      } catch (final ElementNotFoundException e) {
        return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage());
      } catch (final AndroidCommandException e) {
        return getErrorResult(e.getMessage());
      } catch (final ElementNotInHashException e) {
        return getErrorResult(e.getMessage());
      } catch (final UiObjectNotFoundException e) {
        return getErrorResult(e.getMessage());
      }
    }
  }
Exemple #10
0
  /**
   * Create and return a UiSelector based on Xpath attributes.
   *
   * @param path The Xpath path.
   * @param attr The attribute.
   * @param constraint Any constraint.
   * @param substr Any substr.
   * @return UiSelector
   * @throws AndroidCommandException
   */
  private UiSelector getSelectorForXpath(
      final JSONArray path, final String attr, String constraint, final boolean substr)
      throws AndroidCommandException {
    UiSelector s = new UiSelector();
    JSONObject pathObj;
    String nodeType;
    String searchType;
    final String substrStr = substr ? "true" : "false";
    Logger.info(
        "Building xpath selector from attr "
            + attr
            + " and constraint "
            + constraint
            + " and substr "
            + substrStr);
    String selOut = "s";

    // $driver.find_element :xpath, %(//*[contains(@text, 'agree')])
    // info: [ANDROID] [info] Building xpath selector from attr text and
    // constraint agree and substr true
    // info: [ANDROID] [info] s.className('*').textContains('agree')
    try {
      nodeType = path.getJSONObject(0).getString("node");
    } catch (final JSONException e) {
      throw new AndroidCommandException("Error parsing xpath path obj from JSON");
    }

    if (attr.toLowerCase().contentEquals("text")
        && !constraint.isEmpty()
        && substr == true
        && nodeType.contentEquals("*") == true) {
      selOut += ".textContains('" + constraint + "')";
      s = s.textContains(constraint);
      Logger.info(selOut);
      return s;
    }

    // //*[contains(@tag, "button")]
    if (attr.toLowerCase().contentEquals("tag")
        && !constraint.isEmpty()
        && substr == true
        && nodeType.contentEquals("*") == true) {
      // (?i) = case insensitive match. Esape everything that isn't an
      // alpha num.
      // use .* to match on contains.
      constraint = "(?i)^.*" + constraint.replaceAll("([^\\p{Alnum}])", "\\\\$1") + ".*$";
      selOut += ".classNameMatches('" + constraint + "')";
      s = s.classNameMatches(constraint);
      Logger.info(selOut);
      return s;
    }

    for (int i = 0; i < path.length(); i++) {
      try {
        pathObj = path.getJSONObject(i);
        nodeType = pathObj.getString("node");
        searchType = pathObj.getString("search");
      } catch (final JSONException e) {
        throw new AndroidCommandException("Error parsing xpath path obj from JSON");
      }
      nodeType = AndroidElementClassMap.match(nodeType);
      if (searchType.equals("child")) {
        s = s.childSelector(s);
        selOut += ".childSelector(s)";
      } else {
        s = s.className(nodeType);
        selOut += ".className('" + nodeType + "')";
      }
    }
    if (attr.equals("desc") || attr.equals("name")) {
      selOut += ".description";
      if (substr) {
        selOut += "Contains";
        s = s.descriptionContains(constraint);
      } else {
        s = s.description(constraint);
      }
      selOut += "('" + constraint + "')";
    } else if (attr.equals("text") || attr.equals("value")) {
      selOut += ".text";
      if (substr) {
        selOut += "Contains";
        s = s.textContains(constraint);
      } else {
        s = s.text(constraint);
      }
      selOut += "('" + constraint + "')";
    }
    Logger.info(selOut);
    return s;
  }