AutoGLMPhone07-源码-ADB手势

智谱AI-OpenAutoGLM-开源的手机智能体

针对智谱AI-AutoGLM-开源的手机智能体,整理代码拆解步骤,【手机手势】+【手机截屏】


1-思路整理

  • 1)先把手机和电脑的连接软件安装上【ADB(电脑安装)】+【ADBKeyboard(手机安装)】
  • 2)然后手机打开调试模式->这个电脑的应用就可以直接操作手机
  • 3)配置智谱AI-AutoGLM-开源的手机智能体的模型(模型地址/模型)
  • 4)运行智谱AI-AutoGLM-开源的手机智能体代码->直接操作手机

2-参考网址


3-动手实操

1-ADB操作-手机手势

ADB进行手机的手势操作->这一块的代码很独立,可以理解为独立的工具类

1-源码展示

python 复制代码
"""Android自动化设备控制工具。"""

import subprocess
import time

from phone_agent.config.apps import APP_PACKAGES
from phone_agent.config.timing import TIMING_CONFIG


def get_current_app(device_id: str | None = None) -> str:
    """
    获取当前聚焦的应用名称。

    Args:
        device_id: 可选的ADB设备ID,用于多设备环境。

    Returns:
        如果识别到应用则返回应用名称,否则返回"System Home"。
    """
    adb_prefix = _get_adb_prefix(device_id)

    result = subprocess.run(
        adb_prefix + ["shell", "dumpsys", "window"], capture_output=True, text=True
    )
    output = result.stdout

    # 解析窗口焦点信息
    for line in output.split("\n"):
        if "mCurrentFocus" in line or "mFocusedApp" in line:
            for app_name, package in APP_PACKAGES.items():
                if package in line:
                    return app_name

    return "System Home"


def tap(
        x: int, y: int, device_id: str | None = None, delay: float | None = None
) -> None:
    """
    在指定坐标位置点击。

    Args:
        x: X坐标。
        y: Y坐标。
        device_id: 可选的ADB设备ID。
        delay: 点击后延迟时间(秒)。如果为None,则使用配置的默认值。
    """
    if delay is None:
        delay = TIMING_CONFIG.device.default_tap_delay

    adb_prefix = _get_adb_prefix(device_id)

    subprocess.run(
        adb_prefix + ["shell", "input", "tap", str(x), str(y)], capture_output=True
    )
    time.sleep(delay)


def double_tap(
        x: int, y: int, device_id: str | None = None, delay: float | None = None
) -> None:
    """
    在指定坐标位置双击。

    Args:
        x: X坐标。
        y: Y坐标。
        device_id: 可选的ADB设备ID。
        delay: 双击后延迟时间(秒)。如果为None,则使用配置的默认值。
    """
    if delay is None:
        delay = TIMING_CONFIG.device.default_double_tap_delay

    adb_prefix = _get_adb_prefix(device_id)

    subprocess.run(
        adb_prefix + ["shell", "input", "tap", str(x), str(y)], capture_output=True
    )
    time.sleep(TIMING_CONFIG.device.double_tap_interval)
    subprocess.run(
        adb_prefix + ["shell", "input", "tap", str(x), str(y)], capture_output=True
    )
    time.sleep(delay)


def long_press(
        x: int,
        y: int,
        duration_ms: int = 3000,
        device_id: str | None = None,
        delay: float | None = None,
) -> None:
    """
    长按指定坐标位置。

    Args:
        x: X坐标。
        y: Y坐标。
        duration_ms: 按压持续时间(毫秒)。
        device_id: 可选的ADB设备ID。
        delay: 长按后延迟时间(秒)。如果为None,则使用配置的默认值。
    """
    if delay is None:
        delay = TIMING_CONFIG.device.default_long_press_delay

    adb_prefix = _get_adb_prefix(device_id)

    subprocess.run(
        adb_prefix
        + ["shell", "input", "swipe", str(x), str(y), str(x), str(y), str(duration_ms)],
        capture_output=True,
    )
    time.sleep(delay)


def swipe(
        start_x: int,
        start_y: int,
        end_x: int,
        end_y: int,
        duration_ms: int | None = None,
        device_id: str | None = None,
        delay: float | None = None,
) -> None:
    """
    从起始坐标滑动到结束坐标。

    Args:
        start_x: 起始X坐标。
        start_y: 起始Y坐标。
        end_x: 结束X坐标。
        end_y: 结束Y坐标。
        duration_ms: 滑动持续时间(毫秒),如果为None则自动计算。
        device_id: 可选的ADB设备ID。
        delay: 滑动后延迟时间(秒)。如果为None,则使用配置的默认值。
    """
    if delay is None:
        delay = TIMING_CONFIG.device.default_swipe_delay

    adb_prefix = _get_adb_prefix(device_id)

    if duration_ms is None:
        # 根据距离计算持续时间
        dist_sq = (start_x - end_x) ** 2 + (start_y - end_y) ** 2
        duration_ms = int(dist_sq / 1000)
        duration_ms = max(1000, min(duration_ms, 2000))  # 限制在1000-2000毫秒之间

    subprocess.run(
        adb_prefix
        + [
            "shell",
            "input",
            "swipe",
            str(start_x),
            str(start_y),
            str(end_x),
            str(end_y),
            str(duration_ms),
        ],
        capture_output=True,
    )
    time.sleep(delay)


def back(device_id: str | None = None, delay: float | None = None) -> None:
    """
    按下返回键。

    Args:
        device_id: 可选的ADB设备ID。
        delay: 按下返回键后的延迟时间(秒)。如果为None,则使用配置的默认值。
    """
    if delay is None:
        delay = TIMING_CONFIG.device.default_back_delay

    adb_prefix = _get_adb_prefix(device_id)

    subprocess.run(
        adb_prefix + ["shell", "input", "keyevent", "4"], capture_output=True
    )
    time.sleep(delay)


def home(device_id: str | None = None, delay: float | None = None) -> None:
    """
    按下主页键。

    Args:
        device_id: 可选的ADB设备ID。
        delay: 按下主页键后的延迟时间(秒)。如果为None,则使用配置的默认值。
    """
    if delay is None:
        delay = TIMING_CONFIG.device.default_home_delay

    adb_prefix = _get_adb_prefix(device_id)

    subprocess.run(
        adb_prefix + ["shell", "input", "keyevent", "KEYCODE_HOME"], capture_output=True
    )
    time.sleep(delay)


def launch_app(
        app_name: str, device_id: str | None = None, delay: float | None = None
) -> bool:
    """
    通过应用名称启动应用。

    Args:
        app_name: 应用名称(必须存在于APP_PACKAGES中)。
        device_id: 可选的ADB设备ID。
        delay: 启动后的延迟时间(秒)。如果为None,则使用配置的默认值。

    Returns:
        如果成功启动应用返回True,否则返回False(应用未找到)。
    """
    if delay is None:
        delay = TIMING_CONFIG.device.default_launch_delay

    if app_name not in APP_PACKAGES:
        return False

    adb_prefix = _get_adb_prefix(device_id)
    package = APP_PACKAGES[app_name]

    subprocess.run(
        adb_prefix
        + [
            "shell",
            "monkey",
            "-p",
            package,
            "-c",
            "android.intent.category.LAUNCHER",
            "1",
        ],
        capture_output=True,
    )
    time.sleep(delay)
    return True


def _get_adb_prefix(device_id: str | None) -> list:
    """获取带有可选设备标识符的ADB命令前缀。"""
    if device_id:
        return ["adb", "-s", device_id]
    return ["adb"]

2-测试代码

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
device_operation模块的真实使用案例测试
测试所有设备操作手势,包括启动QQ音乐应用
"""

import os
import sys
import time

from phone_agent.adb.device_operation_util.device_operation_util import launch_app, get_current_app, tap, double_tap, long_press, swipe, back, home

# 添加当前目录到Python路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

# 定义APP_PACKAGES字典的一部分,仅包含需要的部分
APP_PACKAGES = {
    "QQ音乐": "com.tencent.qqmusic",
}


def test_device_operations():
    """测试所有设备操作手势"""
    print("开始测试设备操作...")

    try:
        # 1. 测试启动QQ音乐
        print("\n1. 测试启动QQ音乐...")
        success = launch_app("QQ音乐")
        if success:
            print("成功启动QQ音乐")
        else:
            print("启动QQ音乐失败")

        # 等待应用启动
        time.sleep(3)

        # 2. 测试获取当前应用
        print("\n2. 测试获取当前应用...")
        current_app = get_current_app()
        print(f"当前应用: {current_app}")

        # 3. 测试点击操作 (假设在QQ音乐主界面某个位置点击)
        print("\n3. 测试点击操作...")
        # 在屏幕中心附近点击
        tap(500, 500)
        print("执行点击操作 (500, 500)")

        # 等待操作完成
        time.sleep(2)

        # 4. 测试双击操作
        print("\n4. 测试双击操作...")
        # 在屏幕中心附近双击
        double_tap(500, 500)
        print("执行双击操作 (500, 500)")

        # 等待操作完成
        time.sleep(2)

        # 5. 测试长按操作
        print("\n5. 测试长按操作...")
        # 在屏幕中心附近长按
        long_press(500, 500, duration_ms=2000)
        print("执行长按操作 (500, 500),持续2秒")

        # 等待操作完成
        time.sleep(2)

        # 6. 测试滑动操作
        print("\n6. 测试滑动操作...")
        # 从屏幕上方中间向下滑动
        swipe(500, 300, 500, 800, duration_ms=1000)
        print("执行滑动操作: 从(500, 300)到(500, 800),持续1秒")

        # 等待操作完成
        time.sleep(2)

        # 7. 测试返回键
        print("\n7. 测试返回键...")
        back()
        print("执行返回键操作")

        # 等待操作完成
        time.sleep(2)

        # 8. 测试主页键
        print("\n8. 测试主页键...")
        home()
        print("执行主页键操作")

        # 等待操作完成
        time.sleep(2)

        # 9. 再次测试启动QQ音乐
        print("\n9. 再次测试启动QQ音乐...")
        success = launch_app("QQ音乐")
        if success:
            print("成功启动QQ音乐")
        else:
            print("启动QQ音乐失败")

        # 等待应用启动
        time.sleep(3)

        print("\n所有设备操作测试完成!")

    except Exception as e:
        print(f"测试过程中出现错误: {e}")
        raise


if __name__ == "__main__":
    test_device_operations()
  • 打印结果
shell 复制代码
开始测试设备操作...

1. 测试启动QQ音乐...
成功启动QQ音乐

2. 测试获取当前应用...
当前应用: QQ音乐

3. 测试点击操作...
执行点击操作 (500, 500)

4. 测试双击操作...
执行双击操作 (500, 500)

5. 测试长按操作...
执行长按操作 (500, 500),持续2秒

6. 测试滑动操作...
执行滑动操作: 从(500, 300)到(500, 800),持续1秒

7. 测试返回键...
执行返回键操作

8. 测试主页键...
执行主页键操作

9. 再次测试启动QQ音乐...
成功启动QQ音乐

所有设备操作测试完成!

2-ADB操作-手机截屏

1-源码展示

一个独立的工具类方法,直接可以进行屏幕截屏

python 复制代码
"""Android设备屏幕截图工具。"""

import base64
import os
import subprocess
import tempfile
import uuid
from dataclasses import dataclass
from io import BytesIO

from PIL import Image


@dataclass
class Screenshot:
    """表示已捕获的截图。"""

    base64_data: str
    width: int
    height: int
    is_sensitive: bool = False


def get_screenshot(device_id: str | None = None, timeout: int = 10) -> Screenshot:
    """
    从连接的Android设备捕获屏幕截图。

    参数:
        device_id: 可选的ADB设备ID,用于多设备环境。
        timeout: 截图操作的超时时间(秒)。

    返回:
        包含base64数据和尺寸的Screenshot对象。

    注意:
        如果截图失败(例如在支付页面等敏感屏幕),将返回一张黑色占位图,并设置is_sensitive=True。
    """
    temp_path = os.path.join(tempfile.gettempdir(), f"screenshot_{uuid.uuid4()}.png")
    adb_prefix = _get_adb_prefix(device_id)

    try:
        # 执行截图命令
        result = subprocess.run(
            adb_prefix + ["shell", "screencap", "-p", "/sdcard/tmp.png"],
            capture_output=True,
            text=True,
            timeout=timeout,
        )

        # 检查截图失败情况(如敏感屏幕)
        output = result.stdout + result.stderr
        if "Status: -1" in output or "Failed" in output:
            return _create_fallback_screenshot(is_sensitive=True)

        # 将截图拉取到本地临时路径
        subprocess.run(
            adb_prefix + ["pull", "/sdcard/tmp.png", temp_path],
            capture_output=True,
            text=True,
            timeout=5,
        )

        if not os.path.exists(temp_path):
            return _create_fallback_screenshot(is_sensitive=False)

        # 读取并编码图像
        img = Image.open(temp_path)
        width, height = img.size

        buffered = BytesIO()
        img.save(buffered, format="PNG")
        base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8")

        # 清理临时文件
        os.remove(temp_path)

        return Screenshot(
            base64_data=base64_data, width=width, height=height, is_sensitive=False
        )

    except Exception as e:
        print(f"截图错误: {e}")
        return _create_fallback_screenshot(is_sensitive=False)


def _get_adb_prefix(device_id: str | None) -> list:
    """获取带有可选设备标识符的ADB命令前缀。"""
    if device_id:
        return ["adb", "-s", device_id]
    return ["adb"]


def _create_fallback_screenshot(is_sensitive: bool) -> Screenshot:
    """当截图失败时创建一张黑色占位图。"""
    default_width, default_height = 1080, 2400

    black_img = Image.new("RGB", (default_width, default_height), color="black")
    buffered = BytesIO()
    black_img.save(buffered, format="PNG")
    base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8")

    return Screenshot(
        base64_data=base64_data,
        width=default_width,
        height=default_height,
        is_sensitive=is_sensitive,
    )

2-测试代码

python 复制代码
from phone_agent.adb.screenshot_util.screenshot_util import get_screenshot

if __name__ == '__main__':
    screenshot = get_screenshot()
    print(f"截图宽度: {screenshot.width}")
    print(f"截图高度: {screenshot.height}")
    print(f"截图是否敏感: {screenshot.is_sensitive}")
  • 日志打印
shell 复制代码
截图宽度: 1176
截图高度: 2400
截图是否敏感: False

相关推荐
是垚不是土2 小时前
MySQL8.0数据库GTID主从同步方案
android·网络·数据库·安全·adb
java1234_小锋2 小时前
Transformer 大语言模型(LLM)基石 - Transformer架构详解 - 解码器(Decoder)详解以及算法实现
深度学习·语言模型·llm·transformer
骚戴3 小时前
在科研与项目开发中:如何高效调用大语言模型(LLM)API
人工智能·语言模型·自然语言处理·大模型·llm·api
亚里随笔3 小时前
简约而不简单:JustRL如何用最简RL方案实现1.5B模型突破性性能
人工智能·深度学习·机器学习·语言模型·llm·rl
CoderJia程序员甲14 小时前
GitHub 热榜项目 - 日榜(2025-12-21)
开源·大模型·llm·github·ai教程
智泊AI15 小时前
这真的是我看过最深刻的2025年AI大模型年度复盘
llm
明阳~21 小时前
LLM函数调用(Function Call):智能扩展AI能力
llm·prompt·agent·function call
大模型教程21 小时前
深入理解 Token:大语言模型的最小单位到底是什么?
程序员·llm·agent
大模型教程21 小时前
万字长文!大模型(LLM)推理优化技术总结(非常详细)
程序员·llm·agent