AppAgent 源码 (AndroidController 类 )

1. AndroidController 类

AndroidController 类,用于通过 ADB(Android Debug Bridge)命令控制连接的 Android 设备。它提供了一系列方法来实现常见的 Android 设备操作,例如获取屏幕截图、获取 XML 布局文件、模拟点击、输入文本、滑动和长按操作。

以下是详细解释:

  1. 初始化 (__init__)

    • 输入参数
      • device: 表示设备的标识符(通过 adb devices 命令可以获取)。
    • 初始化内容
      • self.device: 保存设备 ID。
      • self.screenshot_dir: 保存截图的默认路径(从全局 configs 中获取)。
      • self.xml_dir: 保存 XML 文件的默认路径(从全局 configs 中获取)。
      • self.width, self.height: 通过调用 get_device_size() 获取设备屏幕宽高。
      • self.backslash: 为了处理路径中的反斜杠。
  2. 获取设备屏幕尺寸 (get_device_size)

    • 功能
      • 执行 adb shell wm size 命令以获取设备屏幕的分辨率。
      • 如果获取成功,解析返回值(例如 "Physical size: 1080x1920")并提取宽高。
      • 如果失败,返回 (0, 0)
  3. 获取屏幕截图 (get_screenshot)

    • 功能
      • 将设备屏幕截图保存到设备的指定目录,并将其拉取到本地。
    • 步骤
      • 构造两个 ADB 命令:
        • screencap -p: 在设备上保存截图。
        • pull: 将截图从设备目录拉取到本地目录。
      • 执行命令并检查结果。
      • 如果成功,返回本地保存的文件路径;如果失败,返回错误信息。
  4. 获取 XML 布局文件 (get_xml)

    • 功能
      • 获取设备当前的 UI 布局并保存为 XML 文件,然后拉取到本地。
    • 步骤
      • 构造两个 ADB 命令:
        • uiautomator dump: 生成 XML 文件。
        • pull: 将 XML 文件从设备拉取到本地。
      • 执行命令并检查结果。
      • 如果成功,返回本地保存的文件路径;如果失败,返回错误信息。
  5. 返回操作 (back)

    • 功能
      • 模拟按下返回键。
    • 实现
      • 执行 adb shell input keyevent KEYCODE_BACK 命令。
  6. 点击操作 (tap)

    • 功能
      • 在屏幕的指定位置模拟点击。
    • 实现
      • 执行 adb shell input tap x y,其中 x, y 是点击位置的坐标。
  7. 文本输入 (text)

    • 功能
      • 模拟文本输入操作。
    • 处理特殊字符
      • 替换空格为 %s
      • 删除单引号('),防止命令解析错误。
    • 实现
      • 执行 adb shell input text input_str
  8. 长按操作 (long_press)

    • 功能
      • 模拟长按屏幕某个位置。
    • 实现
      • 执行 adb shell input swipe x y x y duration,通过滑动命令模拟长按,duration 表示持续时间。
  9. 滑动操作 (swipe)

    • 功能
      • 模拟从某个起点朝某个方向滑动。
    • 步骤
      • 根据屏幕宽度计算滑动距离(短、中、长)。
      • 根据方向设置偏移量:
        • 上:(0, -2 * unit_dist)
        • 下:(0, 2 * unit_dist)
        • 左:(-unit_dist, 0)
        • 右:(unit_dist, 0)
      • 构造并执行 adb shell input swipe 命令。
  10. 精确滑动 (swipe_precise)

    • 功能
      • 模拟从指定起点滑动到指定终点。
    • 实现
      • 接收起点和终点的坐标 (start_x, start_y)(end_x, end_y)
      • 构造并执行 adb shell input swipe 命令,指定滑动路径和持续时间。

2. AndroidController 类源码

python 复制代码
class AndroidController:
    def __init__(self, device):
        self.device = device
        self.screenshot_dir = configs["ANDROID_SCREENSHOT_DIR"]
        self.xml_dir = configs["ANDROID_XML_DIR"]
        self.width, self.height = self.get_device_size()
        self.backslash = "\\"

    def get_device_size(self):
        adb_command = f"adb -s {self.device} shell wm size"
        result = execute_adb(adb_command)
        if result != "ERROR":
            return map(int, result.split(": ")[1].split("x"))
        return 0, 0

    def get_screenshot(self, prefix, save_dir):
        cap_command = (
            f"adb -s {self.device} shell screencap -p "
            f"{os.path.join(self.screenshot_dir, prefix + '.jpg').replace(self.backslash, '/')}"
        )
        pull_command = (
            f"adb -s {self.device} pull "
            f"{os.path.join(self.screenshot_dir, prefix + '.jpg').replace(self.backslash, '/')} "
            f"{os.path.join(save_dir, prefix + '.jpg')}"
        )
        result = execute_adb(cap_command)
        if result != "ERROR":
            result = execute_adb(pull_command)
            if result != "ERROR":
                return os.path.join(save_dir, prefix + ".jpg")
            return result
        return result

    def get_xml(self, prefix, save_dir):
        dump_command = (
            f"adb -s {self.device} shell uiautomator dump "
            f"{os.path.join(self.xml_dir, prefix + '.xml').replace(self.backslash, '/')}"
        )
        pull_command = (
            f"adb -s {self.device} pull "
            f"{os.path.join(self.xml_dir, prefix + '.xml').replace(self.backslash, '/')} "
            f"{os.path.join(save_dir, prefix + '.xml')}"
        )
        result = execute_adb(dump_command)
        if result != "ERROR":
            result = execute_adb(pull_command)
            if result != "ERROR":
                return os.path.join(save_dir, prefix + ".xml")
            return result
        return result

    def back(self):
        adb_command = f"adb -s {self.device} shell input keyevent KEYCODE_BACK"
        ret = execute_adb(adb_command)
        return ret

    def tap(self, x, y):
        adb_command = f"adb -s {self.device} shell input tap {x} {y}"
        ret = execute_adb(adb_command)
        return ret

    def text(self, input_str):
        input_str = input_str.replace(" ", "%s")
        input_str = input_str.replace("'", "")
        adb_command = f"adb -s {self.device} shell input text {input_str}"
        ret = execute_adb(adb_command)
        return ret

    def long_press(self, x, y, duration=1000):
        adb_command = (
            f"adb -s {self.device} shell input swipe {x} {y} {x} {y} {duration}"
        )
        ret = execute_adb(adb_command)
        return ret

    def swipe(self, x, y, direction, dist="medium", quick=False):
        unit_dist = int(self.width / 10)
        if dist == "long":
            unit_dist *= 3
        elif dist == "medium":
            unit_dist *= 2

        if direction == "up":
            offset = 0, -2 * unit_dist
        elif direction == "down":
            offset = 0, 2 * unit_dist
        elif direction == "left":
            offset = -1 * unit_dist, 0
        elif direction == "right":
            offset = unit_dist, 0
        else:
            return "ERROR"

        duration = 100 if quick else 400
        adb_command = f"adb -s {self.device} shell input swipe {x} {y} {x+offset[0]} {y+offset[1]} {duration}"
        ret = execute_adb(adb_command)
        return ret

    def swipe_precise(self, start, end, duration=400):
        start_x, start_y = start
        end_x, end_y = end
        adb_command = f"adb -s {self.device} shell input swipe {start_x} {start_x} {end_x} {end_y} {duration}"
        ret = execute_adb(adb_command)
        return ret

3. 例子演示

python 复制代码
from and_controller import AndroidController
import os

device_id = "your connected device id"

# 初始化 AndroidController
controller = AndroidController(device=device_id)

# 打印设备屏幕分辨率
width, height = controller.get_device_size()
print(f"Device screen resolution: {width}x{height}")
<class 'os._Environ'>
<class 'dict'>
Warning! No module named 'sounddevice'
Warning! No module named 'matplotlib'
Warning! No module named 'keras'
Device screen resolution: 1220x2712
python 复制代码
import time
import datetime
app = "gaode"
root_dir = "./"
work_dir = os.path.join(root_dir, "apps")
if not os.path.exists(work_dir):
    os.mkdir(work_dir)
work_dir = os.path.join(work_dir, app)
if not os.path.exists(work_dir):
    os.mkdir(work_dir)
demo_dir = os.path.join(work_dir, "demos")
if not os.path.exists(demo_dir):
    os.mkdir(demo_dir)
demo_timestamp = int(time.time())
task_name = datetime.datetime.fromtimestamp(demo_timestamp).strftime("self_explore_%Y-%m-%d_%H-%M-%S")
task_dir = os.path.join(demo_dir, task_name)
os.mkdir(task_dir)


# 截取屏幕截图并保存
screenshot_path = controller.get_screenshot("test_screenshot", task_dir)
if screenshot_path != "ERROR":
    print(f"Screenshot saved at: {screenshot_path}")
else:
    print("Failed to capture screenshot.")
Screenshot saved at: ./apps\gaode\demos\self_explore_2024-12-26_01-21-10\test_screenshot.jpg
python 复制代码
# 获取并保存 XML 文件
xml_path = controller.get_xml("test_layout", task_dir)
if xml_path != "ERROR":
    print(f"XML layout saved at: {xml_path}")
else:
    print("Failed to capture XML layout.")
    
XML layout saved at: ./apps\gaode\demos\self_explore_2024-12-26_01-21-10\test_layout.xml

项目结构:

python 复制代码
# 模拟返回按键
result = controller.back()
print(f"Back key result: {result}")    
Back key result: 

如果成功,result结果为空

python 复制代码
# 模拟点击屏幕坐标 (100, 200)
result = controller.tap(100, 200)
python 复制代码
# 模拟输入文本
result = controller.text("Hello World")
python 复制代码
# 模拟长按屏幕坐标 (150, 300) 持续 2 秒
result = controller.long_press(150, 300, duration=2000)
python 复制代码
# 模拟从 (100, 500) 向上滑动
result = controller.swipe(100, 500, "up")
python 复制代码
# 模拟从 (100, 200) 滑动到 (300, 400)
result = controller.swipe_precise((100, 200), (300, 400), duration=1000)
相关推荐
油泼辣子多加27 分钟前
2024年12月27日Github流行趋势
人工智能·github
测试者家园39 分钟前
ChatGPT助力数据可视化与数据分析效率的提升(二)
软件测试·人工智能·信息可视化·chatgpt·数据分析·用chatgpt做软件测试·测试图书
岁月如歌,青春不败1 小时前
R语言森林生态系统结构、功能与稳定性分析与可视化
开发语言·人工智能·python·深度学习·r语言
久笙&1 小时前
高效设计AI Prompt:10大框架详细对比与应用
人工智能·prompt
yuanlulu2 小时前
mindie推理大语言模型问题及解决方法汇总
人工智能·华为·自然语言处理·nlp·大语言模型·昇腾
学术会议2 小时前
【智慧光学与高效信号处理】2025年信号处理与光学工程国际会议 (SPOE 2024)
大数据·人工智能·物联网·安全·信号处理
爱研究的小牛2 小时前
Midjourney技术浅析(一)
人工智能·深度学习·aigc·midjourney
qq_273900232 小时前
PyTorch Lightning Callback介绍
人工智能·pytorch·python
mingo_敏2 小时前
深度学习中的并行策略概述:4 Tensor Parallelism
人工智能·深度学习
敲代码敲到头发茂密3 小时前
基于 LangChain 实现数据库问答机器人
数据库·人工智能·语言模型·langchain·机器人