appium-androidpython-appium

In Appium Python, how to make double-tap faster?


I am using Appium Python to send double-tap (also known as double-click). I tried the following 3 codes. They sometimes work but sometimes not.

action = TouchAction(driver)
action.tap(x=x, y=y).wait(10).tap(x=x, y=y).perform() # 1
action.tap(x=x, y=y).tap(x=x, y=y).perform()          # 2
action.tap(x=x, y=y, count=2).perform()               # 3

I looked into Appium server log.

When they didn't work, the two taps had too much time in between, even if I set 0 wait between the 2 taps. Below I saw 0.9 second between 2 taps.

    2023-01-01 00:45:15:379 [W3C (0e33c728)] Calling AppiumDriver.performTouch() with args: [[{"action":"tap","options":{"x":551.5,"y":1107,"count":1}},{"action":"tap","options":{"x":551.5,"y":1107,"count":1}}],"0e33c728-8dc6-49b5-82dd-567c1508a410"]
    2023-01-01 00:45:15:382 [WD Proxy] Proxying [POST /appium/tap] to [POST http://127.0.0.1:8200/wd/hub/session/448df8e5-309e-4448-bf69-dbfd36602b77/appium/tap] with body: {"x":551.5,"y":1107,"undefined":null}
    2023-01-01 00:45:16:256 [WD Proxy] Got response with status 200: {"sessionId":"448df8e5-309e-4448-bf69-dbfd36602b77","value":null}
    2023-01-01 00:45:16:259 [WD Proxy] Proxying [POST /appium/tap] to [POST http://127.0.0.1:8200/wd/hub/session/448df8e5-309e-4448-bf69-dbfd36602b77/appium/tap] with body: {"x":551.5,"y":1107,"undefined":null}
    2023-01-01 00:45:16:931 [WD Proxy] Got response with status 200: {"sessionId":"448df8e5-309e-4448-bf69-dbfd36602b77","value":null}

When they worked, I saw the interval was 0.28 second.

Part of the delay was from my computer. I am using Android Wireless Debugging. I cannot do much about this.

There are at least 2 other things that I'd like to explore:

  1. is there a way to extend Android device's double-tap timer? To allow 0.9 second between two taps.
  2. is there a way that Appium TouchAction can send the 2nd action without waiting for the response from the 1st action?

I welcome other suggestions.

Thank you


Solution

  • This is what worked for me. It is a hack, but hopefully Appium dev can integrate it into future release.

    in my program, call the new action name 'tap2'

    action = TouchAction(driver)
    action.tap2(x=x, y=y, wait=200).perform()
    

    define the tap2 in site-packages/appium/webdriver/common/touch_action.py

    def tap2(
            self,
            element: Optional['WebElement'] = None,
            x: Optional[int] = None,
            y: Optional[int] = None,
            wait: int = 200,
            count: int = 1,
        ) -> 'TouchAction':
            """Perform a tap action on the element
    
            Args:
                element: the element to tap
                x : x coordinate to tap, relative to the top left corner of the element.
                y : y coordinate. If y is used, x must also be set, and vice versa
    
            Returns:
                `TouchAction`: Self instance
            """
            opts = self._get_opts(element, x, y)
            opts['count'] = count
            self._add_action('tap2', opts)
    
            return self
    

    added in appdata/Roaming/npm/node_modules/appium/node_modules/appium-android-driver/build/lib/commands/touch.js

    case 'tap2':
      return await this.tap2(null, x, y, count);
    

    added in appdata/Roaming/npm/node_modules/appium/node_modules/appium-uiautomator2-driver/build/lib/commands/element.js

    commands.tap2 = async function (elementId = null, x = null, y = null, wait=200, count = 1) {
      let tianstack = new Error("tianstack");
      _logger.default.info(`tianstack: ${tianstack.stack}`);
    
      const areCoordinatesDefined = _appiumSupport.util.hasValue(x) && _appiumSupport.util.hasValue(y);
    
      if (!areCoordinatesDefined) {
        throw new Error(`both absolute coordinates should be defined`);
      }
    
      Promise.all([
        new Promise(resolve => setTimeout(resolve, wait))
          .then(() => this.uiautomator2.jwproxy.command(`/appium/tap`, 'POST', {
            x,
            y,
            [_appiumBaseDriver.W3C_ELEMENT_KEY]: elementId
          })),
        this.uiautomator2.jwproxy.command(`/appium/tap`, 'POST', {
          x,
          y,
          [_appiumBaseDriver.W3C_ELEMENT_KEY]: elementId
        })
      ]).catch((error) => {
        _logger.default.info / (`Failed during tap2: ${error}`)
      });;
    };
    

    This last step allowed me to fire the 1st action without waiting for the response coming back from the 2nd action. This way I can control the timing of the two taps without WIFI's interference.