Appiumでマルチタッチを行う

Appiumで開発者向けオプションが有効なスマートフォンを操作する。
スマートフォンを扱うならば複数の指による操作を行いたい。
そういうときにMulti Touch Performを使うようだが、このクラスは終息するのでW3C Actionsを使う指示がある。
各言語の例はマルチタッチのものだが、Pythonは違う。
Pythonは村八分された…w

まずピンチを実現したい。
ピンチのコードについてGenerating Touch Gestures to Zoom In and Out on Google Mapsを参考にPythonへ移植する。
Seleniumに含まれるモジュールを見てPythonでマルチタッチを行う方法を探り、なんとかピンチを実現する。
import math
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput
from selenium.webdriver.common.actions.pointer_actions import PointerActions
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy as By

STEPS = 16
WAIT = .001

def zoom(p, r0, r1, angle, duration):
	angle = math.pi / 2 - ( 2 * math.pi * angle / 360 )
	options = {}
	chain = ActionChains(driver)
	chain.w3c_actions = ActionBuilder(driver, **options)
	zoom1(chain.w3c_actions, p, r0, r1, angle, duration)
	zoom1(chain.w3c_actions, p, r0, r1, angle + math.pi, duration)
	return chain

def zoom1(ab, p, r0, r1, angle, duration):
	p0 = ( p[0] + r0 * math.cos(angle), p[1] - r0 * math.sin(angle) )
	p1 = ( p[0] + r1 * math.cos(angle), p[1] - r1 * math.sin(angle) )
	pointer_input = ab.add_pointer_input(interaction.POINTER_TOUCH, '{}-{}'.format(interaction.POINTER_TOUCH, len(ab.devices)))
	pointer_action = PointerActions(pointer_input, duration=duration)
	pointer_action.move_to_location(*p0)
	pointer_action.pointer_down()
	pointer_action.pause(WAIT)
	for i in range(1, STEPS):
		pointer_action.move_to_location(p0[0] + ( p1[0] - p0[0] ) * i / STEPS, p0[1] + ( p1[1] - p0[1] ) * i / STEPS)
		pointer_action.pause(WAIT)
	pointer_action.move_to_location(*p1)
	pointer_action.pause(WAIT)
	pointer_action.release()

def zoom_in(p, d, d0=100, duration=200):
	return zoom(p, d0, d0 + d, 45, duration)

def zoom_out(p, d, d0=100, duration=200):
	return zoom(p, d0 + d, d0, 45, duration)

def get_position(element, pos='c'):
	pos = pos.lower()
	rect = element.rect
	(x, y) = (rect['x'], rect['y'])
	if 'r' in pos: x += rect['width']
	elif not 'l' in pos: x += .5 * rect['width']
	if 'b' in pos or 'd' in pos: y += rect['height']
	elif not 't' in pos and not 'u' in pos: y += .5 * rect['height']
	return (x, y)

caps = {}
caps["platformName"] = "android"
caps["ensureWebviewsHavePages"] = True
driver = webdriver.Remote("//localhost:5554/wd/hub", caps)

element = driver.find_element(getattr(By, 'ID'), 'jp.co.yahoo.android.weather.type1:id/snackbar_area')
center = get_position(element)

zoom_out(center, 500).perform()
zoom_in(center, 500).perform()
zoom_out(center, 500).perform()
zoom_in(center, 500).perform()

driver.quit()
Yahoo!天気を実機で検証していたが、次の2つが重要だった:
  • 遷移中の状態をアクションとして実行する
  • それらの間に適切なウエイトを入れる
動きは少しトロくて滑らかでないw
アプリケーションによって挙動が違う…?
2022/09/29 21:39
タグ