autool/DroidBot/platforms/ios/ios_input_event.py
2026-06-17 19:44:18 +08:00

304 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
iOS Input Event Implementation
iOS-specific input event classes that implement AbstractInputEvent.
"""
from typing import Optional, Dict, Any
from ...core.abstract_input_event import (
AbstractInputEvent,
BaseTouchEvent,
BaseLongTouchEvent,
BaseSwipeEvent,
BaseScrollEvent,
BaseSetTextEvent,
BaseKeyEvent,
BaseKillAppEvent,
EventType
)
class IOSTouchEvent(BaseTouchEvent):
"""iOS 触摸/点击事件"""
def __init__(self, x: int, y: int, view: Dict[str, Any] = None):
"""
初始化触摸事件
:param x: 触摸 X 坐标
:param y: 触摸 Y 坐标
:param view: 关联的视图字典(可选)
"""
super().__init__(x=x, y=y, view=view)
def send(self, device) -> bool:
"""发送事件到设备"""
try:
device.view_touch(self.x, self.y)
return True
except Exception as e:
device.logger.warning(f"Failed to send touch event: {e}")
return False
def get_event_str(self, state=None) -> str:
return f"IOSTouchEvent(x={self.x}, y={self.y})"
class IOSLongTouchEvent(BaseLongTouchEvent):
"""iOS 长按事件"""
def __init__(self, x: int, y: int, duration: float = 2.0, view: Dict[str, Any] = None):
"""
初始化长按事件
:param x: 长按 X 坐标
:param y: 长按 Y 坐标
:param duration: 长按持续时间(秒)
:param view: 关联的视图字典(可选)
"""
super().__init__(x=x, y=y, duration=duration, view=view)
def send(self, device) -> bool:
"""发送事件到设备"""
try:
device.view_long_touch(self.x, self.y, self.duration)
return True
except Exception as e:
device.logger.warning(f"Failed to send long touch event: {e}")
return False
def get_event_str(self, state=None) -> str:
return f"IOSLongTouchEvent(x={self.x}, y={self.y}, duration={self.duration})"
class IOSSwipeEvent(BaseSwipeEvent):
"""iOS 滑动事件"""
def __init__(self, start_x: int, start_y: int, end_x: int, end_y: int,
duration: float = 0.5, view: Dict[str, Any] = None):
"""
初始化滑动事件
:param start_x: 起始 X 坐标
:param start_y: 起始 Y 坐标
:param end_x: 结束 X 坐标
:param end_y: 结束 Y 坐标
:param duration: 滑动持续时间(秒)
:param view: 关联的视图字典(可选)
"""
super().__init__(
start_x=start_x, start_y=start_y,
end_x=end_x, end_y=end_y,
duration=duration, view=view
)
def send(self, device) -> bool:
"""发送事件到设备"""
try:
device.view_drag(
(self.start_x, self.start_y),
(self.end_x, self.end_y),
self.duration
)
return True
except Exception as e:
device.logger.warning(f"Failed to send swipe event: {e}")
return False
def get_event_str(self, state=None) -> str:
return f"IOSSwipeEvent(({self.start_x},{self.start_y})->({self.end_x},{self.end_y}))"
class IOSScrollEvent(BaseScrollEvent):
"""iOS 滚动事件"""
def __init__(self, start_x: int, start_y: int, end_x: int, end_y: int,
direction: str = "down", view: Dict[str, Any] = None):
"""
初始化滚动事件
:param start_x: 起始 X 坐标
:param start_y: 起始 Y 坐标
:param end_x: 结束 X 坐标
:param end_y: 结束 Y 坐标
:param direction: 滚动方向 (up/down/left/right)
:param view: 关联的视图字典(可选)
"""
super().__init__(direction=direction, view=view)
self.start_x = start_x
self.start_y = start_y
self.end_x = end_x
self.end_y = end_y
def send(self, device) -> bool:
"""发送事件到设备"""
try:
device.view_drag(
(self.start_x, self.start_y),
(self.end_x, self.end_y),
0.3 # 滚动通常比较快
)
return True
except Exception as e:
device.logger.warning(f"Failed to send scroll event: {e}")
return False
def get_event_str(self, state=None) -> str:
return f"IOSScrollEvent(direction={self.direction})"
class IOSSetTextEvent(BaseSetTextEvent):
"""iOS 文本输入事件"""
def __init__(self, text: str, view: Dict[str, Any] = None):
"""
初始化文本输入事件
:param text: 要输入的文本
:param view: 关联的视图字典(可选)
"""
super().__init__(text=text, view=view)
def send(self, device) -> bool:
"""发送事件到设备"""
try:
# 如果有关联视图,先点击激活
if self.view:
bounds = self.view.get("bounds", [[0, 0], [0, 0]])
center_x = (bounds[0][0] + bounds[1][0]) // 2
center_y = (bounds[0][1] + bounds[1][1]) // 2
device.view_touch(center_x, center_y)
import time
time.sleep(0.5)
device.view_set_text(self.text)
return True
except Exception as e:
device.logger.warning(f"Failed to send set text event: {e}")
return False
def get_event_str(self, state=None) -> str:
text_preview = self.text[:20] + "..." if len(self.text) > 20 else self.text
return f"IOSSetTextEvent(text='{text_preview}')"
class IOSKeyEvent(BaseKeyEvent):
"""iOS 按键事件"""
# iOS 支持的按键
SUPPORTED_KEYS = {
"HOME": "home",
"BACK": "home", # iOS 无返回键,用 Home 代替
"VOLUME_UP": "volumeUp",
"VOLUME_DOWN": "volumeDown",
}
def __init__(self, key_name: str = None, key_code: str = None):
"""
初始化按键事件
:param key_name: 按键名称 (兼容参数,与基类一致)
:param key_code: 按键代码 (HOME, VOLUME_UP, VOLUME_DOWN)
"""
# 兼容两种参数名
key = key_name or key_code or "HOME"
super().__init__(key_name=key)
self.key_code = key # 保留 key_code 供 send 方法使用
def send(self, device) -> bool:
"""发送事件到设备"""
try:
device.key_press(self.key_name)
return True
except Exception as e:
device.logger.warning(f"Failed to send key event: {e}")
return False
def get_event_str(self, state=None) -> str:
return f"IOSKeyEvent(key={self.key_name})"
class IOSKillAppEvent(BaseKillAppEvent):
"""iOS 终止应用事件"""
def __init__(self, app: str = None, bundle_id: str = None):
"""
初始化终止应用事件
:param app: 要终止的应用(兼容参数,与基类一致)
:param bundle_id: 要终止的应用 Bundle IDNone 表示当前应用)
"""
# 兼容两种参数名
app_id = app or bundle_id
super().__init__(app=app_id)
self.bundle_id = app_id # 保留 bundle_id 供 send 方法使用
def send(self, device) -> bool:
"""发送事件到设备"""
try:
# 等待 WDA 就绪
if hasattr(device, '_wait_wda_ready'):
device._wait_wda_ready()
bundle_id = self.bundle_id or device.bundle_id
if bundle_id and device._wda_client:
device._wda_client.app_terminate(bundle_id)
return True
return False
except Exception as e:
device.logger.warning(f"Failed to kill app: {e}")
return False
def get_event_str(self, state=None) -> str:
return f"IOSKillAppEvent(bundle_id={self.bundle_id})"
class IOSIntentEvent(AbstractInputEvent):
"""
iOS Intent 事件(适配 Android Intent 概念)
iOS 没有真正的 Intent此类用于兼容 input_policy 中的应用启动逻辑。
"""
def __init__(self, intent=None, bundle_id: str = None):
"""
初始化 Intent 事件
:param intent: 兼容参数iOS 中忽略)
:param bundle_id: 要启动的应用 Bundle ID
"""
super().__init__(event_type=EventType.INTENT)
self.intent = intent
self.bundle_id = bundle_id
def send(self, device) -> bool:
"""发送事件到设备"""
try:
# 等待 WDA 就绪
if hasattr(device, '_wait_wda_ready'):
device._wait_wda_ready()
# 从 intent 或 bundle_id 确定目标应用
target_bundle_id = self.bundle_id or device.bundle_id
if target_bundle_id and device._wda_client:
# 使用 WDA 启动应用
device._wda_client.app_launch(target_bundle_id)
import time
time.sleep(1)
return True
return False
except Exception as e:
device.logger.warning(f"Failed to launch app via intent: {e}")
return False
def to_dict(self) -> Dict[str, Any]:
return {
"event_type": self.event_type.value,
"bundle_id": self.bundle_id,
"intent": str(self.intent) if self.intent else None,
}
def get_event_str(self, state=None) -> str:
return f"IOSIntentEvent(bundle_id={self.bundle_id})"