# 安卓原生控件与多指动作 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`,由模型重新观察,避免使用旧目标。 观察列表中的 `native_selector` 是可直接复制到读取、等待、状态确保及数值反馈中的原生选择器。它优先使用资源 ID、类名和包名,不把当前读数或描述当作身份;重复 ID 或没有 ID 时才附加原始像素边界。列表 `Bounds` 是用于手势的 0–1000 归一化坐标,而 `selector.bounds` 必须是 Android 原始屏幕像素,不能混用,也不能将有损取整的列表坐标反算回去。可点击区域经过裁剪或合并时仍保留原生节点的原始边界。选择器仍逐项精确 AND 匹配;布局改变、原生窗口中有其它同名节点时可能失效或歧义,需要重新观察。`NOT_FOUND` 只表示本次选择器未匹配,不能直接推断控件没有原生语义。等待的目标文本放 `expected_text`,不要塞入身份筛选。 文本动作包括 `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;旧助手或其它后端不支持时工具不会暴露,直接调用也明确拒绝。没有原生语义的控件继续使用观察后手势,不能冒充原生设值。 ## 时间轴精度 精确调整可绑定可选的 `feedback`:`selector` 指向原生可读取、唯一且持久的真实选中值,`value_format` 为 `range`(原生范围)、`number`(完整数字文本)或 `clock`(文本中唯一的 HH:MM:SS,换算为当天秒数),并提供同单位的 `target_value` 与 `tolerance`。不要选择录像卡片、视频水印或短暂拖动浮层,也不要在 selector 中包含会变化的读数文本。无法建立真实绑定时继续视觉观测,不能编造控件或把手势派发当作读值。 单独拖动把 feedback 放在 `drag` 参数上;“拖动后暂停”等组合把它放在 `action_sequence` 顶层,不放在 drag 成员上。输入前确认可读,整组成功后再等待读数连续稳定 `stable_ms`(默认 1000,范围 100–5000),总观察不超过 `timeout_ms`(默认 3000,上限 10000,必须大于稳定时长)。时钟稳定时长应覆盖其刷新周期。回执含 before_value、after_value、residual、within_tolerance 和连续无效次数;数值条件满足与最终业务验收分开。输入完成但读数失败或未达标仍保留完成回执,不会自动重发。 同一执行实例内,同读数、目标和策略连续两次没有减小实际误差,第三次拒绝输入,提示重新观察并放大、改变有效移动距离或使用原生设值。微改坐标不算换策略;新刻度比例、轴向或明显不同的距离可重新尝试。读数改变或任务实例更换后重新判断。此守卫依赖显式 feedback,不声称能够自动理解所有自绘控件。 所有手指路径可以通过 `region=[left,top,right,bottom]` 限定到已观察到的手势区域;坐标单位跟随 coordinate_space,右/下边界不包含在区域内。拖动、缩放或多指轨迹离开区域时整组在首个输入前拒绝;区域只验证几何边界,不证明窗口未被遮挡。calibration.tolerance 检查像素取整误差,回执给出每像素数值和允许像素误差;min_movement_px 只能填实测起效距离,过小则拒绝并要求放大。几何误差不包括读图、惯性与 APP 的内部吸附误差。 暂停或勾选等状态可用 `control_action.ensure_state`,支持 checked/unchecked、selected/unselected、text/content_desc 精确匹配及 value/tolerance。先 read_control 确认其语义,稳定 selector 不包含会改变的文字/描述。达到状态时跳过;未达到时仅操作一次,再只读等待 verify_timeout_ms(默认 1500,上限 3000)。状态缺失不当作 false,核验失败保留已完成输入,不继续盲点切换。不能假定 selected 就表示暂停。 例如,已观察确认资源 ID `player` 的描述为“播放”时表示暂停,可将以下成员放在拖动后: ```json {"action":"control_action","request":{"selector":{"resource_id":"player"},"action":"click","ensure_state":{"state":"content_desc","expected_text":"播放"}}} ``` 示例 ID 和状态含义必须换成实际观察结果。绑定时间反馈示例为 `{"selector":{"resource_id":"observed_current_time"},"value_format":"clock","target_value":63737,"tolerance":5}`,对应目标 17:42:17±5 秒;不用于日期验收或跨日时间差。 双指距离变化不一定等于 APP 比例尺变化:先小幅缩放,再读取实际刻度,建立像素与时间的局部对应。平移后等待停稳,读取残差再修正;播放中应先暂停后验收。 如果常驻读数通过标签、节点角色或层级明确绑定到固定指针当前选时,且在暂停或独立停稳观测中持续显示、与刻度一致,可以直接作为秒级证据,无须再要求粗像素插值独立达到同样精度。事件卡片、录像水印和短暂拖动浮层不能代替当前选时。真实冲突、来源不明或缺少稳定证据时继续保留不确定结论;没有可信读数时仍用刻度插值并计入读图误差。