kotones-auto-assistant/kotonebot/client/host/adb_common.py

92 lines
3.0 KiB
Python

from abc import ABC
from typing import Literal, TypeVar
from typing_extensions import assert_never
from adbutils import adb
from adbutils._device import AdbDevice
from kotonebot import logging
from kotonebot.client.device import AndroidDevice
from .protocol import Instance, AdbHostConfig, Device
logger = logging.getLogger(__name__)
AdbRecipes = Literal['adb', 'adb_raw', 'uiautomator2']
def connect_adb(
ip: str,
port: int,
connect: bool = True,
disconnect: bool = True,
timeout: float = 180,
device_serial: str | None = None
) -> AdbDevice:
"""
创建 ADB 连接。
"""
if disconnect:
logger.debug('adb disconnect %s:%d', ip, port)
adb.disconnect(f'{ip}:{port}')
if connect:
logger.debug('adb connect %s:%d', ip, port)
result = adb.connect(f'{ip}:{port}')
if 'cannot connect to' in result:
raise ValueError(result)
serial = device_serial or f'{ip}:{port}'
logger.debug('adb wait for %s', serial)
adb.wait_for(serial, timeout=timeout)
devices = adb.device_list()
logger.debug('adb device_list: %s', devices)
d = [d for d in devices if d.serial == serial]
if len(d) == 0:
raise ValueError(f"Device {serial} not found")
d = d[0]
return d
class CommonAdbCreateDeviceMixin(ABC):
"""
通用 ADB 创建设备的 Mixin。
该 Mixin 定义了创建 ADB 设备的通用接口。
"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
# 下面的属性只是为了让类型检查通过,无实际实现
self.adb_ip: str
self.adb_port: int
self.adb_name: str
def create_device(self, recipe: AdbRecipes, config: AdbHostConfig) -> Device:
"""
创建 ADB 设备。
"""
connection = connect_adb(
self.adb_ip,
self.adb_port,
connect=True,
disconnect=True,
timeout=config.timeout,
device_serial=self.adb_name
)
d = AndroidDevice(connection)
match recipe:
case 'adb':
from kotonebot.client.implements.adb import AdbImpl
impl = AdbImpl(connection)
d._screenshot = impl
d._touch = impl
d.commands = impl
case 'adb_raw':
from kotonebot.client.implements.adb_raw import AdbRawImpl
impl = AdbRawImpl(connection)
d._screenshot = impl
d._touch = impl
d.commands = impl
case 'uiautomator2':
from kotonebot.client.implements.uiautomator2 import UiAutomator2Impl
from kotonebot.client.implements.adb import AdbImpl
impl = UiAutomator2Impl(d)
d._screenshot = impl
d._touch = impl
d.commands = AdbImpl(connection)
case _:
assert_never(f'Unsupported ADB recipe: {recipe}')
return d