# assists-bridge **Repository Path**: johnluicn/assists-bridge ## Basic Information - **Project Name**: assists-bridge - **Description**: No description available - **Primary Language**: Android - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-27 - **Last Updated**: 2026-05-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # assists-bridge - Android 远程控制桥接 基于 Assists 无障碍框架的 Android 设备远程控制方案。 ## 架构 ``` ┌─────────────┐ WebSocket连接 ┌─────────────┐ Assists框架 ┌─────────────┐ │ 控制端 │ ═══════════════> │ Android │ ═══════════> │ 目标应用 │ │ Python客户端│ 发送命令 │ APK(WS服务端)│ 执行操作 │ (控制目标) │ └─────────────┘ └─────────────┘ └─────────────┘ ``` ## 项目结构 ``` assists-bridge/ ├── build.gradle ├── settings.gradle ├── app/ # Android 应用 │ ├── build.gradle │ └── src/main/ │ ├── AndroidManifest.xml │ ├── java/com/assists/bridge/ │ │ ├── AssistsBridgeApp.kt # 应用入口 │ │ ├── MainActivity.kt # 主界面 │ │ ├── ScreenshotManager.kt # 截屏管理器 │ │ ├── WebSocketService.kt # WebSocket 服务端 │ │ └── CommandHandler.kt # 命令处理器 │ │ ├── WakeManager.kt # 屏幕唤醒管理器 │ │ ├── BridgeNotificationListener.kt # 通知监听器 │ │ └── UiAutomationHandler.kt # UiAutomation 高级操作 │ └── res/ ├── .miscellaneous/ # 杂项工具 │ ├── scripts/ # Python 辅助脚本示例 │ └── test/ # 测试脚本 ├── .deploy/ # 部署工具 └── .notes/ # 开发笔记 ``` ## 构建 ```bash # Android Studio 打开项目 # Build → Build APK(s) # 或命令行 ./gradlew assembleDebug ``` ## 部署 1. 安装 APK 到手机:`adb install app/build/outputs/apk/debug/assists-bridge-1.3.0.apk` 2. 开启无障碍服务(设置中找到「AssistsBridge」) 3. 启动 WebSocket 服务(点击「启动服务」按钮) 4. 控制端连接 WebSocket:`ws://手机IP:8765` ## JSON 桥接接口 ### 请求格式 所有命令通过 WebSocket 发送 JSON 格式: ```json { "id": 1, "action": "命令名称", "params": { ... } } ``` | 字段 | 类型 | 说明 | |------|------|------| | `id` | int | 请求 ID(用于匹配响应) | | `action` | string | 命令名称 | | `params` | object | 命令参数(可选) | ### 响应格式 ```json { "type": "response", "id": 1, "success": true, "result": { ... } } ``` 失败响应: ```json { "type": "response", "id": 1, "success": false, "error": "错误信息" } ``` ## 命令列表 ### 基础命令 #### `ping` - 测试连接 ```json {"id": 1, "action": "ping"} // 响应: {"type": "response", "id": 1, "success": true, "result": {"pong": true}} ``` #### `screenSize` - 获取屏幕尺寸 ```json {"id": 2, "action": "screenSize"} // 响应: {"type": "response", "id": 2, "success": true, "result": {"width": 1080, "height": 1792}} ``` #### `checkPermissions` - 检查权限状态 ```json {"id": 3, "action": "checkPermissions"} // 响应: 包含 permissions 和 features 状态 ``` ### 节点操作命令 #### `dump` - 获取所有节点 ```json {"id": 4, "action": "dump", "params": {"limit": 50}} ``` 响应包含节点列表,每个节点有: - `nodeId`: 节点 ID - `text`: 文本内容 - `viewId`: resource-id - `bounds`: 边界坐标 "[left,top][right,bottom]" - `isClickable`: 是否可点击 | 参数 | 类型 | 说明 | |------|------|------| | `limit` | int | 限制返回节点数(可选) | #### `findById` / `findByText` - 查找节点 ```json {"id": 5, "action": "findById", "params": {"id": "com.example:id/btn"}} {"id": 6, "action": "findByText", "params": {"text": "确定"}} ``` #### `getTexts` - 获取所有文本 ```json {"id": 7, "action": "getTexts"} // 响应: {"texts": ["文本1", "文本2", ...]} ``` #### `contains` - 检查是否包含文本 ```json {"id": 8, "action": "contains", "params": {"text": "确定"}} // 响应: {"found": true} ``` #### `getPackage` - 获取当前应用包名 ```json {"id": 9, "action": "getPackage"} // 响应: {"packageName": "com.example.app"} ``` ### 点击/输入命令 #### `click` - 点击操作 支持三种方式: **坐标点击**: ```json {"id": 10, "action": "click", "params": {"x": 540, "y": 1000}} ``` **ID 点击**: ```json {"id": 11, "action": "click", "params": {"id": "com.example:id/btn"}} ``` **文本点击**: ```json {"id": 12, "action": "click", "params": {"text": "确定"}} ``` #### `input` - 输入文本 ```json {"id": 13, "action": "input", "params": {"id": "com.example:id/input", "text": "测试文本"}} ``` #### `clickByBounds` - 通过边界点击节点 ```json {"id": 14, "action": "clickByBounds", "params": {"bounds": "[100,200][200,250]"}} ``` 或单独指定坐标: ```json {"id": 15, "action": "clickByBounds", "params": {"left": 100, "top": 200, "right": 200, "bottom": 250}} ``` 响应包含: - `clicked`: 是否成功点击 - `bounds`: 点击的节点边界 - `nodeInfo`: 节点信息(text, viewId 等) #### `focusByBounds` - 通过边界获取焦点 ```json {"id": 16, "action": "focusByBounds", "params": {"bounds": "[100,200][200,250]"}} ``` #### `setTextByBounds` - 通过边界设置文本 (API 21+) ```json {"id": 17, "action": "setTextByBounds", "params": {"bounds": "[100,200][200,250]", "text": "新文本"}} ``` #### `pasteByBounds` - 通过边界粘贴剪贴板内容 ```json {"id": 18, "action": "pasteByBounds", "params": {"bounds": "[100,200][200,250]"}} ``` ### 导航命令 #### `back` / `home` - 导航操作 ```json {"id": 19, "action": "back"} {"id": 20, "action": "home"} ``` #### `launch` - 启动应用 ```json {"id": 21, "action": "launch", "params": {"package": "com.example.app"}} // 指定 Activity: {"package": "com.example.app", "activity": "com.example.app/.MainActivity"} ``` ### 滑动命令 #### `swipe` - 屏幕滑动 支持多种方式: **方向滑动**: ```json {"id": 22, "action": "swipe", "params": {"direction": "up", "distance": 500}} ``` | 方向 | 说明 | |------|------| | `up` | 向上滑动(手指向上划) | | `down` | 向下滑动(手指向下划) | | `left` | 向左滑动(手指向左划) | | `right` | 向右滑动(手指向右划) | **坐标滑动**: ```json {"id": 23, "action": "swipe", "params": {"startX": 540, "startY": 1000, "endX": 540, "endY": 300}} ``` **起点终点滑动**: ```json {"id": 24, "action": "swipe", "params": {"from": {"x": 540, "y": 1000}, "to": {"x": 540, "y": 300}, "duration": 300}} ``` | 参数 | 说明 | |------|------| | `distance` | 滑动距离(像素),默认屏幕高度的 50% | | `duration` | 滑动持续时间(毫秒),默认 300 | #### `shellTap` - Shell 命令点击 使用 `input tap` shell 命令执行点击,比 dispatchGesture 更底层。 ```json {"id": 25, "action": "shellTap", "params": {"x": 540, "y": 1000}} ``` 响应包含: - `success`: 是否成功 - `exitCode`: Shell 命令退出码 - `error`: 错误信息 **与手势点击的区别**: | 方式 | API | 特点 | |------|------|------| | `click(x,y)` | dispatchGesture() | 无障碍服务手势 | | `shellTap(x,y)` | Runtime.exec("input tap") | Shell 命令,更底层 | ### 屏幕状态命令 #### `wakeScreen` - 唤醒屏幕 ```json {"id": 26, "action": "wakeScreen", "params": {"timeout": 30000, "bright": true}} ``` | 参数 | 说明 | |------|------| | `timeout` | 唤醒持续时间(毫秒),默认 30000 | | `bright` | 是否保持屏幕亮度,默认 true | #### `sleepScreen` - 让屏幕休眠 ```json {"id": 27, "action": "sleepScreen"} ``` 释放 WakeLock,屏幕将在系统超时后休眠。 #### `lockScreen` - 锁屏 ```json {"id": 28, "action": "lockScreen"} ``` 需要 DeviceAdmin 权限。 #### `unlockScreen` - 解锁屏幕 ```json {"id": 29, "action": "unlockScreen"} ``` 只能解除简单锁屏(滑动解锁),PIN/密码仍需手动输入。 #### `screenState` - 获取屏幕状态 ```json {"id": 30, "action": "screenState"} ``` 响应包含: - `screenOn`: 屏幕是否亮着 - `locked`: 是否锁屏 - `securelyLocked`: 是否安全锁屏(需要 PIN/密码) - `hasWakeLock`: 是否持有 WakeLock ### 截屏命令 #### `screenShot` - 截屏 ```json {"id": 31, "action": "screenShot", "params": {"quality": 80, "format": "jpeg"}} ``` | 参数 | 说明 | |------|------| | `quality` | 图片质量(默认 80) | | `format` | 图片格式(默认 jpeg) | 响应: - 已授权:返回 `data`(base64 编码图片) - 未授权:返回 `needAuth: true` - 未启用:返回 `disabled: true` ### 短信/通话命令 #### `getSms` - 读取短信 ```json {"id": 32, "action": "getSms", "params": {"limit": 50}} ``` | 参数 | 说明 | |------|------| | `limit` | 限制返回数量(默认 50) | 需要启用短信读取功能 + READ_SMS 权限。 #### `getCallLog` - 读取通话记录 ```json {"id": 33, "action": "getCallLog", "params": {"limit": 50}} ``` 需要启用通话记录功能 + READ_CALL_LOG 权限。 ### 通知命令 #### `getNotifications` - 获取通知列表 ```json {"id": 34, "action": "getNotifications"} ``` 需要启用通知监听功能。 #### `openNotification` - 打开通知 ```json {"id": 35, "action": "openNotification", "params": {"package": "com.example.app", "id": 123}} ``` | 参数 | 说明 | |------|------| | `package` | 应用包名 | | `id` | 通知 ID | | `tag` | 通知标签(可选) | #### `cancelNotification` - 取消通知 ```json {"id": 36, "action": "cancelNotification", "params": {"package": "com.example.app", "id": 123}} ``` #### `clearNotifications` - 清除应用的所有通知 ```json {"id": 37, "action": "clearNotifications", "params": {"package": "com.example.app"}} ``` ### 剪贴板命令 #### `getClipboard` - 获取剪贴板内容 ```json {"id": 38, "action": "getClipboard"} ``` 响应包含: - `success`: 是否成功 - `hasText`: 是否有文本内容 - `text`: 剪贴板文本 #### `setClipboard` - 设置剪贴板内容 ```json {"id": 39, "action": "setClipboard", "params": {"text": "要复制的内容"}} ``` | 参数 | 说明 | |------|------| | `text` | 要设置的文本内容(必填) | | `label` | 剪贴板标签(可选) | #### `clearClipboard` - 清空剪贴板 ```json {"id": 40, "action": "clearClipboard"} ``` ### 功能管理命令 #### `listFeatures` - 查询可选功能列表 ```json {"id": 41, "action": "listFeatures"} ``` 返回所有可选功能的状态。 #### `enableFeature` - 启用可选功能 ```json {"id": 42, "action": "enableFeature", "params": {"feature": "SCREENSHOT"}} ``` 可选功能: - `SCREENSHOT`: 截屏功能 - `SMS_READ`: 短信读取 - `CALL_LOG`: 通话记录 - `NOTIFICATION_LISTENER`: 通知监听 #### `disableFeature` - 禁用可选功能 ```json {"id": 43, "action": "disableFeature", "params": {"feature": "SCREENSHOT"}} ``` ### 通知订阅推送 客户端可订阅特定类型的通知,服务端主动推送。 #### `subscribeNotifications` - 订阅通知 ```json {"id": 44, "action": "subscribeNotifications", "params": {"types": ["SMS", "CALL"], "mode": "push"}} ``` 订阅类型: - `SMS`: 短信通知 - `CALL`: 来电通知 - `SYSTEM`: 系统通知 - `APP`: 指定应用通知(需提供 `packageName`) - `ALL`: 全部通知 推送消息格式: ```json { "type": "notification", "subscriptionType": "SMS", "data": { "package": "com.android.mms", "title": "10010", "text": "您的余额已不足...", "timestamp": 1716012345000 } } ``` ## 命令总览 | 类别 | 命令数 | 底层实现 | |------|--------|----------| | 基础命令 | 4 | - | | 节点操作 | 6 | AssistsCore API | | 点击/输入 | 8 | AssistsCore / AccessibilityNodeInfo | | 导航 | 3 | AssistsCore API | | 滑动 | 2 | dispatchGesture / Shell | | 屏幕状态 | 5 | PowerManager / WakeLock | | 截屏 | 1 | MediaProjection | | 短信/通话 | 2 | ContentProvider | | 通知 | 4 | NotificationListenerService | | 剪贴板 | 3 | ClipboardManager | | 功能管理 | 3 | FeatureSettings | ## Python 客户端示例 ```python import asyncio import json import base64 import websockets async def main(): uri = "ws://192.168.1.100:8765" async with websockets.connect(uri) as ws: # 测试连接 await ws.send(json.dumps({"id": 1, "action": "ping"})) response = await ws.recv() print(f"响应: {response}") # 获取节点 await ws.send(json.dumps({"id": 2, "action": "dump", "params": {"limit": 50}})) response = await ws.recv() result = json.loads(response)["result"] print(f"节点数: {result['count']}") # 截屏 await ws.send(json.dumps({"id": 3, "action": "screenShot"})) response = await ws.recv() result = json.loads(response)["result"] if result.get("success"): img_data = base64.b64decode(result["data"]) with open("screenshot.jpg", "wb") as f: f.write(img_data) elif result.get("needAuth"): print("请授权截屏权限") # 点击坐标 await ws.send(json.dumps({"id": 4, "action": "click", "params": {"x": 540, "y": 1000}})) response = await ws.recv() print(f"点击结果: {response}") # 返回 await ws.send(json.dumps({"id": 5, "action": "back"})) response = await ws.recv() asyncio.run(main()) ``` ## 依赖 - **Assists v3.5.0** - 无障碍服务框架 - **Java-WebSocket 1.5.4** - WebSocket 服务端 - **Gson** - JSON 解析 ## 版本历史 | 版本 | 日期 | 变更 | |------|------|------| | v1.3.0 | 2026-05 | 新增剪贴板操作命令(getClipboard, setClipboard, clearClipboard) | | v1.2.0 | 2026-05 | 新增通知操作命令(openNotification, cancelNotification, clearNotifications) | | v1.1.0 | 2026-05 | 新增屏幕状态命令、bounds 操作命令、通知订阅推送 | | v1.0.0 | 2026-05 | 基础版本:节点操作、点击、滑动、截屏、短信/通话 | --- *代码量:约 1700 行 Kotlin*