You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
8.7 KiB

"""
滑动手势服务模块
集成adb_swipe_norm.py的功能
"""
import re
from typing import Optional, Tuple, Dict, Any
from app.core.device.manager import device_manager, DeviceSource
from app.core.device.dispatcher import device_dispatcher
from app.services.auto_discovery_adb_service import AutoDiscoveryAdbService
from app.utils.structured_log import get_structured_logger
logger = get_structured_logger(__name__)
class SwipeService:
"""滑动手势服务类 - 遵循设备服务统一模式"""
def __init__(self):
self.auto_discovery_adb_service = AutoDiscoveryAdbService()
def clamp01(self, v: float) -> float:
"""将值限制在0-1范围内"""
return max(0.0, min(1.0, v))
async def get_device_source(self, device_id: str) -> DeviceSource:
"""获取设备来源"""
return await device_manager.get_device_source(device_id)
async def get_screen_size(self, device_id: str) -> Tuple[int, int]:
"""获取屏幕尺寸"""
try:
# 使用auto_discovery_adb_service获取屏幕尺寸
result = await self.auto_discovery_adb_service.execute_shell_command(device_id, "wm size")
if result.success:
output = result.output
m = re.search(r"(\d+)\s*x\s*(\d+)", output)
if m:
w, h = int(m.group(1)), int(m.group(2))
return w, h
# 备用方案:dumpsys display
result = await self.auto_discovery_adb_service.execute_shell_command(device_id, "dumpsys display")
if result.success:
output = result.output
m = re.search(r"(\d+)\s*x\s*(\d+)", output)
if m:
w, h = int(m.group(1)), int(m.group(2))
return w, h
raise RuntimeError("failed to detect screen size via 'wm size' or 'dumpsys display'.")
except Exception as e:
logger.error(f"获取屏幕尺寸失败: {device_id}", error=str(e))
raise
async def perform_swipe(self,
device_id: str,
x: float = 0.5,
y: float = 0.2,
direction: str = "down",
distance: float = 0.6,
duration: int = 300) -> Dict[str, Any]:
"""
执行标准化坐标滑动手势(统一入口)
Args:
device_id: 设备ID
x: 标准化起始x坐标 (0-1)
y: 标准化起始y坐标 (0-1)
direction: 滑动方向 ("up", "down", "left", "right")
distance: 滑动距离占屏幕比例 (0-1)
duration: 滑动持续时间(毫秒)
Returns:
包含执行结果的字典
"""
try:
device_source = await self.get_device_source(device_id)
logger.info(f"执行滑动手势: {device_id} -> {direction}",
device_source=device_source.value)
if device_source == DeviceSource.REGISTERED:
return await self._execute_registered_device_swipe(device_id, x, y, direction, distance, duration)
else:
return await self._execute_auto_discovered_device_swipe(device_id, x, y, direction, distance, duration)
except Exception as e:
logger.error(f"执行滑动手势失败: {device_id} -> {direction}", error=str(e))
return {
"success": False,
"error": str(e),
"device_id": device_id
}
async def _execute_registered_device_swipe(self, device_id: str, x: float, y: float,
direction: str, distance: float, duration: int) -> Dict[str, Any]:
"""执行注册设备的滑动手势"""
# 通过设备分发器执行操作
return await device_dispatcher.execute_operation(
device_id=device_id,
protocol_type="adb", # 滑动手势只支持ADB设备
operation="perform_swipe",
x=x,
y=y,
direction=direction,
distance=distance,
duration=duration
)
async def _execute_auto_discovered_device_swipe(self, device_id: str, x: float, y: float,
direction: str, distance: float, duration: int) -> Dict[str, Any]:
"""执行自动发现设备的滑动手势"""
try:
# 验证和限制参数
sx_n = self.clamp01(x)
sy_n = self.clamp01(y)
dist = max(0.0, min(1.0, distance))
# 获取屏幕尺寸
width, height = await self.get_screen_size(device_id)
# 转换标准化起始点到像素
sx = int(round(sx_n * width))
sy = int(round(sy_n * height))
# 根据方向和距离计算结束点
ex, ey = sx, sy
if direction == "down":
ey = sy + int(round(height * dist))
elif direction == "up":
ey = sy - int(round(height * dist))
elif direction == "right":
ex = sx + int(round(width * dist))
elif direction == "left":
ex = sx - int(round(width * dist))
else:
raise ValueError(f"无效的滑动方向: {direction}")
# 限制结束点在屏幕边界内
ex = max(0, min(width - 1, ex))
ey = max(0, min(height - 1, ey))
# 构建滑动命令
swipe_command = f"input swipe {sx} {sy} {ex} {ey} {duration}"
# 使用auto_discovery_adb_service执行命令
result = await self.auto_discovery_adb_service.execute_shell_command(device_id, swipe_command)
if not result.success:
error_msg = result.error or "Unknown error"
logger.error(f"ADB滑动命令执行失败: {error_msg}")
return {
"success": False,
"error": f"ADB error: {error_msg}",
"device_id": device_id,
"command": swipe_command
}
result_msg = f"swipe {direction} from ({sx},{sy}) to ({ex},{ey}) in {duration} ms on {width}x{height}"
logger.info(f"滑动成功: {result_msg}")
return {
"success": True,
"message": result_msg,
"device_id": device_id,
"start_point": {"x": sx, "y": sy},
"end_point": {"x": ex, "y": ey},
"screen_size": {"width": width, "height": height},
"normalized_start": {"x": sx_n, "y": sy_n},
"direction": direction,
"distance": dist,
"duration": duration
}
except Exception as e:
logger.error(f"执行自动发现设备滑动手势失败: {e}")
return {
"success": False,
"error": str(e),
"device_id": device_id
}
async def swipe_up(self, device_id: str, distance: float = 0.6, duration: int = 300) -> Dict[str, Any]:
"""向上滑动"""
return await self.perform_swipe(device_id, direction="up", distance=distance, duration=duration)
async def swipe_down(self, device_id: str, distance: float = 0.6, duration: int = 300) -> Dict[str, Any]:
"""向下滑动"""
return await self.perform_swipe(device_id, direction="down", distance=distance, duration=duration)
async def swipe_left(self, device_id: str, distance: float = 0.6, duration: int = 300) -> Dict[str, Any]:
"""向左滑动"""
return await self.perform_swipe(device_id, direction="left", distance=distance, duration=duration)
async def swipe_right(self, device_id: str, distance: float = 0.6, duration: int = 300) -> Dict[str, Any]:
"""向右滑动"""
return await self.perform_swipe(device_id, direction="right", distance=distance, duration=duration)
async def swipe_from_point(self,
device_id: str,
x: float,
y: float,
direction: str,
distance: float = 0.6,
duration: int = 300) -> Dict[str, Any]:
"""从指定点开始滑动"""
return await self.perform_swipe(device_id, x=x, y=y, direction=direction, distance=distance, duration=duration)