feat(android): add native controls and continuous multi-touch
This commit is contained in:
1 parent
3dd96078db
commit
4b12ae7728
70 files changed
+13105
-179
No files matched your search
@@ -63,6 +63,8 @@ click 的 times 支持 1–10 次连点,delay_ms 默认 100;连点仍占一
|
||||
|
||||
## MCP 能力
|
||||
|
||||
Android 原生控件读写、条件等待、限定容器滚动及同步多指缩放/旋转见 [原生动作说明](android-native-actions.md)。Flash、普通 Pro 和探索共用实现;工具完成与 APP 结果、最终业务验收分别记录。
|
||||
|
||||
仅 PC 测试与网页“仅 PC”勾选项使用同一任务目标。先用 `mobile_workspace(include_devices=false)` 查看实际电脑插件,再提交 `mobile_run_task(task_desc="在电脑浏览器检查指定页面", model="Flash", request_id="本次提交唯一编号", runtime_overrides={"execution_target":"computer"})`。无需手机或模拟器在线;仍需可用模型和已启用并授权的电脑观察插件。`model="Pro"` 与 `mobile_exploration` 的 `task_config.execution_target="computer"` 同样适用。每项批量用例的 `task_config` 使用相同字段;插件参数见 [插件任务参数](../skills/artemis-use/references/plugins.md)。
|
||||
|
||||
| 工具 | 功能 |
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# 安卓原生控件与多指动作
|
||||
|
||||
Flash、普通 Pro 和探索共用同一套动作参数、助手执行入口与设备锁。外层 `mobile_run_task` 等 MCP 提交接口保持不变;这些是任务内供模型组合的通用工具,不绑定某个 APP 或时间轴。
|
||||
|
||||
| 工具 | 用途 | 主要边界 |
|
||||
| --- | --- | --- |
|
||||
| `read_control` | 读取范围、当前值、支持动作、选区、状态及身份 token | 只读;精确选择器必须唯一 |
|
||||
| `control_action` | 原生点击、长按、聚焦、显示、设进度、滚动、编辑与 IME 提交 | 仅执行控件实际支持的动作;不回退坐标 |
|
||||
| `wait_until` | 等待出现、消失、可用、选中、精确文本/数值或持续稳定 | 只读;最多 30 秒;读取失败不能证明消失 |
|
||||
| `scroll_to` | 在指定唯一容器内滚动到目标 | 最多 20 次/30 秒;无进展、边界、失败或取消即停止 |
|
||||
| `pinch` | 同步双指缩放,并可旋转 | 两指间距与角度可配置;最长 10 秒 |
|
||||
| `touch_gesture` | 1~5 指同时按住、停留及多段连续移动 | 2~32 个关键帧;跨段保持同一次接触;最长 10 秒 |
|
||||
|
||||
## 控件选择与精确编辑
|
||||
|
||||
`selector` 中提供的属性全部按精确 AND 匹配:`resource_id`、`text`、`content_desc`、`class_name`、`package_name`、`bounds`。至少提供一项观察到的资源 ID、文本、描述或类名;包名和坐标不能单独充当语义目标。原样复制观察结果,不猜包名或省略 ID。未找到、多个匹配、不可读及不支持分别返回明确回执。
|
||||
|
||||
原生查询在当前窗口及同一前台 APP 不低于当前窗口的应用窗口中共同解析,跨窗口仍须唯一,不扩大到后台 APP。窗口枚举或所属窗口信息不完整时返回不可用,不能据此证明控件消失。原生写入前再次检查可见性及窗口层级:将目标裁切到所属窗口的可触区域,并扣除所有同屏更高窗口的可触区域,必须仍有可见区域;完全遮挡则返回 OCCLUDED。部分露出的编辑框可继续原生编辑,不会因 Android 选区工具栏或手柄的小幅覆盖就一律拒绝。Android 11/API 30 及以上使用系统提供的精确区域;更旧系统只能用矩形边界近似,非矩形窗口可能有偏差。透明或不可触摸的窗口也可能保守拒绝,应重新观察或关闭遮挡,不自动回退坐标。
|
||||
|
||||
先 `read_control`,再按实际 `actions` 操作。范围设值使用 `action="set_progress", value=...`,严格检查实际 min/max;自绘时间轴没有 range 时不能直接设置秒数。`expected_token` 可绑定之前的读数;目标文本、位置或身份变化时返回 `STALE`,由模型重新观察,避免使用旧目标。
|
||||
|
||||
文本动作包括 `set_text`、`set_selection`、`replace_selection`、`ime_enter`。选择区使用 Android UTF-16 索引,不能切开代理对;例如 `A😀B中文` 中 emoji 的范围是 `[1,3]`。`replace_selection` 保留其它文字并将光标移至插入末尾;空白与空字符串按原样传入。密码字段不公开原文,不支持基于隐藏原文替换选区。`ime_enter` 仅提交控件支持的 IME 动作,不代表业务表单已成功提交。
|
||||
|
||||
## 手势、组合与验证
|
||||
|
||||
坐标空间明确写作 `normalized`(0..1000,默认)或 `pixel`(原图像素),全程只换算一次。`pinch` 的归一化间距按屏幕短边换算,保留物理纵横比例;`angle_degrees` 是起始双指方向,`rotation_degrees` 是旋转量。`touch_gesture.frames` 每项含 `at_ms` 与按固定手指顺序排列的 `points`;从 0 开始,时间严格增加,手指数量保持不变。同坐标后续帧表示继续按住。越界、相交或合并的触点在输入前拒绝,不夹到屏幕边缘。
|
||||
|
||||
`action_sequence` 可组合 `pinch`、`touch_gesture`、`wait_until` 及 `control_action`。控制动作成员把参数放入 `request`:`{"action":"control_action","request":{"selector":{"content_desc":"观察到的名称"},"action":"click"}}`。数量沿用任务的 `pro.execution.max_burst_actions`,等待也算成员;组内条件等待最多 3 秒,文本最多 1024 字符。全部成员先校验参数与能力,后执行;部分失败停止余下成员,保留已经完成与结果不明的回执。不要整组重发。较大文本用单独 `control_action`;序列仍保留 16,384 字符参数流保护。
|
||||
|
||||
`wait_until` 遇到窗口过渡产生的 READ_FAILED/UNAVAILABLE 时,在原定时限内继续只读轮询,并重新计算稳定窗口;只有可靠的后续观测才能满足条件。持续不可读会超时并保留最后的原始错误,不会当作控件消失。歧义、无效状态及不支持等错误立即返回。
|
||||
|
||||
原生滚动接受动作后,仍等待容器内容及目标可见性/身份连续停稳 `settle_ms`,再确认目标或追加下一次滚动;动画中短暂进入视口不能证明完成。首次内容更新有至少 500ms 的观测余量,总等待仍受 `timeout_ms` 限制,结果不明的输入不重复派发。
|
||||
|
||||
执行回执区分 `not_started`、`observed`、`completed`、`partial`、`cancelled`、`unknown`,并保留节点前后状态、观测与中间滚动记录。派发完成只证明 Android 完成输入,不能证明 APP 收到了输入或业务通过;键盘、弹层、透明遮挡都可能截获触点。每组动作后重新观察实际控件值、刻度或 APP 状态。取消先到会记录 operation ID,阻止迟到的同一手势;输入仍不确定时保持通道占用,旧点击/滑动也不能抢占。原生写请求不会切换到其它后端重放。
|
||||
|
||||
原生能力由连接时的助手声明决定。新助手协议为 3,多段/多指手势需要 Android 8.0/API 26;旧助手或其它后端不支持时工具不会暴露,直接调用也明确拒绝。没有原生语义的控件继续使用观察后手势,不能冒充原生设值。
|
||||
|
||||
## 时间轴精度
|
||||
|
||||
双指距离变化不一定等于 APP 比例尺变化:先小幅缩放,再读取实际刻度,建立像素与时间的局部对应。平移后等待停稳,读取残差再修正;播放中应先暂停后验收。
|
||||
|
||||
如果常驻读数通过标签、节点角色或层级明确绑定到固定指针当前选时,且在暂停或独立停稳观测中持续显示、与刻度一致,可以直接作为秒级证据,无须再要求粗像素插值独立达到同样精度。事件卡片、录像水印和短暂拖动浮层不能代替当前选时。真实冲突、来源不明或缺少稳定证据时继续保留不确定结论;没有可信读数时仍用刻度插值并计入读图误差。
|
||||
|
||||
## 开发验证
|
||||
|
||||
默认单元测试不需要设备。真实设备回归使用隔离测试 Activity,先通过 ARTEMIS 探索,再显式执行 [测试夹具说明](../tests/native_actions_fixture/README.md)。回调模拟只能验证状态机,不能代替 APP 的真实 MotionEvent、控件值或屏幕证据。
|
||||
@@ -0,0 +1,67 @@
|
||||
# 安卓原生动作验证记录
|
||||
|
||||
对应 [实现计划](2026-11-02-native-actions.md) 与 [能力说明](../android-native-actions.md)。主程序基于 2.5.0 的 main 开发,未改变主程序版本或第三方依赖;助手独立升级为 1.3.0 / protocol 3 / versionCode 15。
|
||||
|
||||
## 已验证的基础能力
|
||||
|
||||
| 层次 | 实际验证 | 结果与边界 |
|
||||
| --- | --- | --- |
|
||||
| 默认确定性测试 | `uv run --no-sync pytest -q` | 3824 passed、2 skipped、8 deselected;299.20s;使用最终 Python 实现,未依赖设备或私有服务 |
|
||||
| 静态质量 | Ruff format/check、质量基线、核心 Pyright | 均通过;核心类型检查 0 错误、0 警告;未放宽质量基线 |
|
||||
| Java 状态机 | 编译生产 TouchGestures/GestureController,模拟 Android 回调 | 694 项通过;覆盖取消注册竞态、迟到回调、所有权及提前完成的静止段;不代替真实触摸交付 |
|
||||
| Java 窗口解析 | 编译生产 ControlWindowRoots/NativeControls,模拟窗口与节点 | 57 场景、361 项通过;涵盖真实选区工具栏形状、联合遮挡、区域空洞、旧 API、枚举失败、全局唯一与预算、取消及编辑后禁用 |
|
||||
| 独立原生 Activity | 生产 helper → driver → actuator,5 轮完整回归 | 321 项通过;包含范围设值、唯一/缺失/歧义选择、UTF-16 编辑/光标/IME、等待、双指缩放/旋转、连续停留,以及两轮容器内往返滚动 |
|
||||
| 真实设备取消 | 提前取消、重复取消、双指接触后取消 Python task、原始非法参数 | 30 项通过;仅一笔原手势及一笔取消;取得中断回执并释放,返回后 500ms 没有新增触点 |
|
||||
| 普通 Pro 只读 | `9430c6fd-966d-4f00-94c8-79b91be320ee` | passed;模型与最终 Checker 均收到真实 range、文本与选区回执 |
|
||||
| Flash 原生编辑 | `a3149abd-326b-40fd-99b9-816cf6a0299d` | passed;范围 42、emoji 选区替换、光标和一次 IME 提交由 APP 状态证明;保留一次错误包名查询的 NOT_FOUND |
|
||||
| 普通 Pro 连续触摸 | `0955486c-38b0-4eb9-9e41-bafaa4caaefe` | passed;三次真实首 MOVE−DOWN 为 280/266/266ms,UP−DOWN 为 760/752/757ms,工具 elapsed 为 763/755/771ms;每次一次 DOWN/UP、无 CANCEL、同一 pointer |
|
||||
|
||||
真实设备报告保存在本次本地 `artifacts/native-actions-fixture/` 下,未将 APK 夹具、设备输出或模型请求正文提交源码。主程序分发所需的既有助手 APK 与 manifest 同步更新;其 SHA256 为 `544c38d5c135518fe6796aa4e03f804e87342687125a383ff3244952c8160d1f`。
|
||||
|
||||
## 失败样本与修正
|
||||
|
||||
- 旧助手中,中间静止路径的 Android callback 会提前完成:750ms 计划曾实际约 509ms,不能按回调成功视为时长正确。现在按 uptime 保持该段的最早结束时间,再续接下一段;真实 APP 事件及三次正式 Pro 任务均验证了修正。
|
||||
- 原生滚动仅表示接受动画,200ms 后继续滚动曾产生错误的 NO_PROGRESS。现在等待容器和目标状态连续停稳;目标超出容器签名的有界采样时,也独立跟踪目标可见性及身份,避免短暂进入视口就成功。
|
||||
- 一次编辑框位置异步变化使新读 token 与派发时 token 不符,返回 STALE_TARGET / not_started,未输入。夹具改为在后续编辑前显式等待该控件稳定,没有放宽 token 或隐式重发,随后 5 轮全部通过。
|
||||
- 多窗口写入检查最初把任何高层窗口交叠都当作遮挡,真实选区工具栏及手柄只遮住编辑框边缘,也会错误阻止选区替换。保留 code14 失败证据;改为扣除高层覆盖后仍须存在可见区域,不按包名或工具栏类名豁免,多个窗口共同完全遮挡仍拒绝。
|
||||
- 窗口退出期间曾短暂返回 READ_FAILED,等待工具原先也立即退出。现在仅对 READ_FAILED/UNAVAILABLE 在原始截止时间内继续只读轮询,并重置稳定计时;失败不证明消失,歧义、非法参数等仍立即拒绝。新增确定性回归后,原生执行器 72 项通过;夹具编辑前后显式等待稳定,最终助手的 5 轮 321 项全部通过。
|
||||
- 取消脚本最初把 IME 内部 mIsInputViewShown 当作屏幕键盘可见,错误阻塞测试。现在使用系统唯一 mInputShown 状态并保留窗口覆盖检查;失败报告保留。
|
||||
- 早期夹具运行还保留了坐标空间拼写、UTF-8 解码、已有焦点不再支持 focus,以及键盘遮挡导致 APP 未收到触摸的样本。参数拒绝、Android 输入完成和 APP 接收分别判定,没有将这些运行记为完整通过。
|
||||
|
||||
## Checker 证据规则
|
||||
|
||||
原历史任务 `4794dc4b-8164-4996-b489-2a2899bb4078` 保持原来的 inconclusive,未改写历史。使用完整 final prompt、真实 `run_final_check`、当前配置的 `gpt-6.1-terra / medium` 与完整报告解析器,独立验证:
|
||||
|
||||
- 真实暂停状态中,绑定当前选时的常驻 17:41:59 读数、重复停稳观测及粗指针一致:两次均 passed。
|
||||
- 五个离线候选反例:事件卡片、水印、短暂浮层、读数/指针真实冲突、缺少稳定或暂停证据,均 inconclusive;理由明确引用各自缺口。
|
||||
- 资格集 7 次均单请求、无传输重试,保存实际请求/服务端 ID、原始完整输出及解析结果,system prompt 字节完全一致。
|
||||
|
||||
两次正例复用同一历史暂停状态,五类反例为离线候选,不能冒充设备定位成功率。审计不完整的首组记录及候选作用域不充分的反例也保留,共 19 次实际模型运行;仅上述 7 次计入资格集。
|
||||
|
||||
## 目标 APP 实测
|
||||
|
||||
探索 `a51b55d8-46da-436e-a5f1-7288e3b9a6a0` 已通过。当前摄像头监控回看时间轴没有暴露原生 range/actions,不能用 set_progress 直接设置秒数;播放按钮和常驻选时则有独立节点。
|
||||
|
||||
- 小跨度 120→160px:工具完成,但实际刻度未见可辨变化,未计作 APP 缩放成功。
|
||||
- 同一区域 100→400px:10 分钟刻度约 120–122→211px,实际约放大 1.73–1.76 倍;说明需要读取 APP 刻度,不能把手指跨度倍数直接当作比例尺。
|
||||
- 日期面板选择 17:00 可到目标小时,但会自动播放;通过实际播放按钮转为三角形后确认暂停。
|
||||
|
||||
Flash 的三次独立缩放与精确定位验收全部 passed:
|
||||
|
||||
| 独立任务 | 目标 | 暂停后稳定读数 | 误差 |
|
||||
| --- | --- | --- | --- |
|
||||
| `af8c38b7-6a6b-435a-86da-6820c3843242` | 17:42:00 | 17:41:57 | −3s |
|
||||
| `ca5a5cb9-351f-4639-940e-41880af95766` | 17:43:00 | 17:42:57 | −3s |
|
||||
| `7d809bff-a529-4174-8cc7-d5494cb2a267` | 17:42:00 | 17:42:01 | +1s |
|
||||
|
||||
每次均有真实刻度变化、常驻选时与固定指针一致、三角形暂停状态及至少 1 秒后的独立稳定观测。三次都在 ±5s 内,不代表所有设备或后续任意输入的保证。
|
||||
|
||||
收尾的无输入复验又发现多窗口问题:APP 的空壳窗口被 Android 标为 active,而完整播放器在同包上层窗口;观察接口看得见的日期与播放按钮,在只搜索 active 的原生接口中返回 NOT_FOUND。已修复为有界前台窗口解析、跨根全局唯一以及每次写入前的精确区域遮挡检查;枚举不完整明确失败,旧系统保留已说明的矩形近似。修复前后的只读证据均保留,最终 code15 的夹具与取消回归已通过。
|
||||
|
||||
最终普通 Flash 任务 `54115cf7-4332-41fe-8c25-3daef8a82a18` passed:修正到暂停 17:42:00 后,在同一 action_sequence 原生 click→等待 1000ms→原生 click,两次均完成,selected 依次 false→true→false。常驻选时推进至 17:42:01,约 6.9 秒后独立截图仍为同一读数与三角形暂停状态。收尾再次从原生接口读取日期、播放节点与前台包名,确认保持此状态,仅卸载本次独立夹具,未清空或停止用户 APP。
|
||||
|
||||
## 验证环境与范围
|
||||
|
||||
本机 Python 3.14.0;Android 模拟器上实际执行。助手以 JDK 21.0.8、Android SDK/build-tools 36 构建,Java 8 目标。未把本次单一设备结果外推到所有 Android/OEM;多段/多指手势最低 API 26,工具仍按助手实际能力声明启用。
|
||||
|
||||
未进行主程序发版或最终 ZIP 的空 npm 缓存安装构建;本次没有前端输入或第三方锁文件改动。正式发版仍须单独通过仓库发布门槛。
|
||||
@@ -0,0 +1,34 @@
|
||||
# 安卓通用原生控制能力
|
||||
|
||||
状态:已实现并完成本次验收。沿用 main,不创建开发分支;结果见 [验证记录](2026-11-02-native-actions-validation.md)。正式版本发布单独履行发布门槛。
|
||||
|
||||
## 完整范围
|
||||
|
||||
1. 同步双指缩放/旋转;单次本机调用完成,两指同时接触。
|
||||
2. 有界条件等待:出现/消失、可用/选中、文本/数值及持续稳定;观测失败不等于目标消失。
|
||||
3. 原生控件范围、当前值、支持动作的只读查询与严格设值。
|
||||
4. 唯一实时原生节点动作,保留过期目标、歧义、未支持与拒绝回执。
|
||||
5. 指定容器内有界滚动到目标,记录中间动作,停止于找到、边界、无进展、取消或超时。
|
||||
6. 保持按压的多段触摸轨迹,包括按住后移动;支持多个同时触点。
|
||||
7. 精细文本选择、替换选区、指定输入框设值及 IME 提交;Android UTF-16 索引严格校验。
|
||||
8. 修复最终验收证据分级:可信常驻当前选时读数与稳定/暂停及固定指针一致时提供秒级证据,保留真实冲突和非选中时间保护。
|
||||
|
||||
## 共享接口与边界
|
||||
|
||||
复用已有 Android 助手、driver/controller、action_specs、Flash/Pro 和资源锁。新增 pinch、touch_gesture、read_control、control_action、wait_until、scroll_to;pinch、touch_gesture、control_action 与 wait_until 按明确声明进入 action_sequence。只读工具与写动作分别声明,不开放第二套任务调度或任意系统命令。
|
||||
|
||||
- read_control/control_action 使用精确 AND selector,至少包含一项语义属性;实时查询唯一节点,返回范围、支持动作、选择区与身份 token。写入可绑定已读 token;拒绝缺失、歧义、过期、越界、非有限值和不支持动作,不隐式退回坐标或重发。
|
||||
- touch_gesture 以有界时间关键帧描述一个或多个同步手指,跨段保持同一次接触,整个操作有界;pinch 为该基础的可配置便捷入口。
|
||||
- 写请求不经观测 fallback 重放。取消期间已派发动作须有界收尾再释放资源;未知结果保留 possible side effect,不能报告成功。
|
||||
- 动作受理、派发完成、APP 值变化和业务验收分别表达;动作回执必须进入模型下一轮、步骤记录和报告。
|
||||
- 自绘控件没有原生 range 时明确不支持设值,继续使用双指放大、刻度校准、平移和残差修正。两指距离比例不直接当作 APP 比例尺变化。
|
||||
- 保留现有配置、队列、历史和插件授权;不停止用户任务。设备验证固定使用已确认且空闲的模拟器。
|
||||
|
||||
## 验证与交付门槛
|
||||
|
||||
- 确定性测试覆盖所有参数边界、身份/动作能力、无输入拒绝、部分回执、取消/超时、写入不重发、UTF-16、条件等待/滚动停止、Flash/Pro 共享入口及参数流保护。
|
||||
- 独立原生测试 Activity 验证真实 SeekBar、选择区、多指触点、连续按压、条件变化、列表滚动;使用设备锁,清理自身临时连接,不清空或重置用户 APP。
|
||||
- 目标 APP 先观察实际路径,重复验证时间轴缩放、定位与暂停;记录每次误差和成功率,失败样本保留,不以单次成功代替稳定性。
|
||||
- 指定历史任务的最终验收使用新规则做只读验证,正反样本覆盖选时读数、事件卡片、浮层冲突及证据不足;不改写历史为人工结论。
|
||||
- Python 默认全套、Ruff 两项、质量基线、核心 Pyright;若前端输入发生变化则完成其局部检查。助手重建 APK、版本/协议和 SHA256 必须一致。
|
||||
- 交付记录列出全部实际验证与未验证范围;正式发布另须履行最终 ZIP 的双 registry 空缓存构建、标签和公开附件核验。
|
||||
Reference in new issue
Block a user