keys 按键模拟
keys 按键模拟
keys 是 ScriptX 里负责“按键事件派发、系统全局动作触发、文本输入、KeyCode 解析”的模块对象。
它和坐标点击不一样,这一组更像是:
- 打返回、回桌面、开通知栏
- 直接发
KEYCODE_* - 用系统
input text注入文本 - 把按键事件明确打到某块
vdisplay虚拟屏
先记住这 14 条
keys.home()这一类小写方法,默认优先尝试无障碍全局动作。keys.Home()这一类大写方法,是直接发input keyevent KEYCODE_*的快捷入口。keys.powerDialog()是“打开电源菜单”;keys.Power()是“发电源键”,这两个不是一回事。keys.notifications()、keys.quickSettings()、keys.recents()主要依赖无障碍全局动作,不保证所有设备都能成功。keys.splitScreen()只有 Android 7.0 及以上才可能成功。keys.Text("")会直接返回true,不会报错。keys.KeyCode(value)是“解析后立刻派发”;keys.keyCode(value)只是“把你传的值解析成整数 KeyCode”。keys.keyCode("home")、keys.keyCode("KEYCODE_HOME")、keys.keyCode("3")都能用。- 解析失败时,
keys.keyCode(...)返回-1;keys.KeyCode(...)返回false。 - 这一页讲的是模块
keys,但其中大多数方法也同时被挂成了全局快捷函数。 back()/home()、keys.back()/keys.home()、大部分大写按键函数、Text(...)、KeyCode(...)现在都支持可选的displayId/vdisplaysession,也能直接吃 session id 字符串和一些 target 风格对象。- 你传了目标显示器以后,
keys.back(display)/keys.home(display)就不再走无障碍全局动作,而会改成对那块显示器直接发KEYCODE_BACK/KEYCODE_HOME。 - 没传目标显示器时,如果你之前已经
auto.setWindowFilter(screen)锁过自动化显示作用域,这一组 API 也可能自动继承那块displayId。 - 虚拟屏目标参数传错时,当前会直接抛:
displayId must be a number or vdisplay session
这条报错文案是历史名字。
虽然提示里只写了 “number or vdisplay session”,但当前实现实际上还接受:
"vdisplay-1"这种 session id 字符串{ screen: screen }/{ target: screen }/{ displayId: screen }这类对象写法
模块方法和全局快捷别名一览
全局动作优先这一组
| 模块写法 | 直接全局写法 | 作用 |
|---|---|---|
keys.back() | back() | 返回 |
keys.home() | home() | 回桌面 |
keys.powerDialog() | powerDialog() | 打开电源菜单 |
keys.notifications() | notifications() | 打开通知栏 |
keys.quickSettings() | quickSettings() | 打开快捷设置 |
keys.recents() | recents() | 打开最近任务 |
keys.splitScreen() | splitScreen() | 切换分屏 |
直接发 KeyEvent 这一组
| 模块写法 | 直接全局写法 | 实际键值 |
|---|---|---|
keys.Home() | Home() | KEYCODE_HOME |
keys.Back() | Back() | KEYCODE_BACK |
keys.Power() | Power() | KEYCODE_POWER |
keys.Menu() | Menu() | KEYCODE_MENU |
keys.VolumeUp() | VolumeUp() | KEYCODE_VOLUME_UP |
keys.VolumeDown() | VolumeDown() | KEYCODE_VOLUME_DOWN |
keys.Camera() | Camera() | KEYCODE_CAMERA |
keys.Up() | Up() | KEYCODE_DPAD_UP |
keys.Down() | Down() | KEYCODE_DPAD_DOWN |
keys.Left() | Left() | KEYCODE_DPAD_LEFT |
keys.Right() | Right() | KEYCODE_DPAD_RIGHT |
keys.OK() | OK() | KEYCODE_DPAD_CENTER |
keys.Text(text) | Text(text) | input text ... |
keys.KeyCode(value) | KeyCode(value) | 解析后发 input keyevent |
虚拟屏目标参数规则
这次更新里最容易漏看的就是这部分。
哪些接口支持虚拟屏目标参数
当前明确支持可选虚拟屏目标参数的有:
keys.back(displayId?)keys.home(displayId?)keys.Home(displayId?)keys.Back(displayId?)keys.Power(displayId?)keys.Menu(displayId?)keys.VolumeUp(displayId?)keys.VolumeDown(displayId?)keys.Camera(displayId?)keys.Up(displayId?)keys.Down(displayId?)keys.Left(displayId?)keys.Right(displayId?)keys.OK(displayId?)keys.Text(text, displayId?)keys.KeyCode(value, displayId?)
下面这些当前没有虚拟屏目标参数:
keys.powerDialog()keys.notifications()keys.quickSettings()keys.recents()keys.splitScreen()keys.keyCode(value)
可以怎么传
| 写法 | 例子 | 是否推荐 |
|---|---|---|
数字 displayId | keys.Home(7) | 推荐 |
vdisplay session 对象 | keys.Back(screen) | 最推荐 |
| session id 字符串 | keys.Text("hello", "vdisplay-1") | 推荐 |
| target 风格对象 | keys.KeyCode("HOME", { screen: screen }) | 可用 |
| 省略参数 | keys.Home() | 推荐 |
另外,如果你手里本来就是一个 session 风格对象,顶层带着这些字段时,当前实现也能直接认:
__vdisplaySessionIdsessionIdiddisplayId
后面各小节的参数表里如果为了排版简洁只写 displayId,都统一按这一节的完整规则来理解。
省略参数时会怎样
- 没有自动化显示作用域时:按默认行为执行
- 已经有自动化显示作用域时:会优先继承那个
displayId
典型写法:
const screen = vdisplay.create();
auto.setWindowFilter(screen);
keys.Home(); // 这里可能已经是对 screen.displayId 发 HOME
keys.back(displayId?)
返回上一级。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
displayId | number | VDisplaySession | string | object | 可选;目标显示器 |
返回值
boolean
真实行为
不传 displayId
当前实现会先尝试:
AccessibilityService.GLOBAL_ACTION_BACK- 如果这一步没成功,再退到
input keyevent KEYCODE_BACK
所以它通常比直接 keys.Back() 更像“系统级返回”。
传了 displayId / session
这时它不会再尝试无障碍全局返回。
因为全局动作本身没有“指定某块 display”的概念,所以当前实现会直接改成对目标显示器派发 KEYCODE_BACK。
示例
if (!keys.back()) {
toastLog("返回动作失败");
}
keys.back(screen);
keys.home(displayId?)
回到桌面。
参数
和 keys.back(displayId?) 一样。
返回值
boolean
真实行为
不传 displayId
先尝试 GLOBAL_ACTION_HOME,失败后再退到 KEYCODE_HOME。
传了 displayId / session
改成直接对目标显示器派发 KEYCODE_HOME。
示例
keys.home();
keys.home(screen);
keys.powerDialog()
尝试打开系统电源菜单。
参数
无。
返回值
boolean
真实行为
- 优先走
GLOBAL_ACTION_POWER_DIALOG - 当前这一个方法没有额外的 KeyEvent 回退键值
- 也不接
displayId,因为它本来就是系统级动作,不是某块显示器里的普通按键事件
示例
const ok = keys.powerDialog();
log(`power dialog = ${ok}`);
keys.notifications()
打开通知栏。
参数
无。
返回值
boolean
真实行为
先走 GLOBAL_ACTION_NOTIFICATIONS,失败时再尝试发 KEYCODE_NOTIFICATION。
示例
notifications();
keys.quickSettings()
打开快捷设置面板。
参数
无。
返回值
boolean
说明
- 主要依赖
GLOBAL_ACTION_QUICK_SETTINGS - 当前没有单独的
displayId参数
示例
keys.quickSettings();
keys.recents()
打开最近任务。
参数
无。
返回值
boolean
说明
- 主要依赖
GLOBAL_ACTION_RECENTS - 当前没有
displayId参数
示例
keys.recents();
keys.splitScreen()
尝试切换系统分屏。
参数
无。
返回值
boolean
平台限制
| 条件 | 行为 |
|---|---|
| Android 7.0 以下 | 直接返回 false |
| Android 7.0 及以上 | 尝试 GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN |
示例
const ok = keys.splitScreen();
log(`split screen = ${ok}`);
keys.Text(text, displayId?)
输入文本。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
text | string | 要输入的文本 |
displayId | number | VDisplaySession | string | object | 可选;目标显示器 |
返回值
boolean
真实行为
当前实现走的是:
input text '...'
并且会先做两件编码处理:
- 百分号
%会转成%25 - 空格会转成
%s
所以你平时直接写普通字符串就行,不需要自己手动把空格改成 %s。
特别说明
| 情况 | 结果 |
|---|---|
text == "" | 直接返回 true |
| shell / display input 成功 | 返回 true |
| 执行失败 | 返回 false,同时可能记录 [keys] 或虚拟屏输入相关日志 |
示例
keys.Text("hello world");
keys.Text("100% done", screen);
keys.KeyCode(value, displayId?)
把你给的值先解析成整数 KeyCode,再立刻派发出去。
参数
| 参数 | 类型 | 可填值 |
|---|---|---|
value | number | string | 数字、数字字符串、短键名、完整 KEYCODE_* 名 |
displayId | number | VDisplaySession | string | object | 可选;目标显示器 |
返回值
boolean
什么时候返回 false
- 你传的值根本解析不出 KeyCode
- shell 或 display input 派发失败
示例
keys.KeyCode("home");
keys.KeyCode("KEYCODE_BACK");
keys.KeyCode(24); // 音量+
keys.KeyCode("menu", screen);
keys.keyCode(value)
把一个名字或数字解析成 KeyCode 整数,但不发送事件。
参数
| 参数 | 类型 | 可填值 |
|---|---|---|
value | number | string | 3、"3"、"home"、"KEYCODE_HOME" 这类 |
返回值
number
可能的返回结果
| 情况 | 返回值 |
|---|---|
| 成功解析 | 对应的整数 KeyCode |
| 空字符串 / 未知名字 | -1 |
它到底认哪些名字
当前解析规则是:
- 如果你传的是数字,直接取整数
- 如果你传的是字符串数字,比如
"4",先转数字 - 如果你传的是
"home",会去匹配KEYCODE_HOME - 如果你传的是
"KEYCODE_HOME",也能直接匹配
示例
log(keys.keyCode("home")); // 3
log(keys.keyCode("KEYCODE_BACK")); // 4
log(keys.keyCode("24")); // 24
log(keys.keyCode("__bad__")); // -1
keys.Home(displayId?)
直接派发 KEYCODE_HOME。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
displayId | number | VDisplaySession | string | object | 可选;目标显示器 |
返回值
boolean
示例
Home();
keys.Home(screen);
keys.Back(displayId?)
直接派发 KEYCODE_BACK。
参数
和 keys.Home(displayId?) 一样。
和 keys.back(displayId?) 的区别
keys.back(...):默认更偏系统返回动作keys.Back(...):始终是直接发按键事件
示例
keys.Back();
keys.Back(screen);
keys.Power(displayId?)
直接派发 KEYCODE_POWER。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
displayId | number | VDisplaySession | string | object | 可选;目标显示器 |
说明
它不是“打开电源菜单”,那是 keys.powerDialog() 的语义。
示例
keys.Power();
keys.Power(screen);
keys.Menu(displayId?)
直接派发 KEYCODE_MENU。
参数
同上。
示例
Menu();
keys.Menu(screen);
keys.VolumeUp(displayId?)
直接派发 KEYCODE_VOLUME_UP。
参数
同上。
示例
keys.VolumeUp();
keys.VolumeUp(screen);
keys.VolumeDown(displayId?)
直接派发 KEYCODE_VOLUME_DOWN。
参数
同上。
示例
VolumeDown();
keys.VolumeDown(screen);
keys.Camera(displayId?)
直接派发 KEYCODE_CAMERA。
参数
同上。
示例
keys.Camera();
keys.Camera(screen);
keys.Up(displayId?)
直接派发 KEYCODE_DPAD_UP。
参数
同上。
示例
keys.Up();
keys.Up(screen);
keys.Down(displayId?)
直接派发 KEYCODE_DPAD_DOWN。
参数
同上。
示例
Down();
keys.Down(screen);
keys.Left(displayId?)
直接派发 KEYCODE_DPAD_LEFT。
参数
同上。
示例
Left();
keys.Left(screen);
keys.Right(displayId?)
直接派发 KEYCODE_DPAD_RIGHT。
参数
同上。
示例
Right();
keys.Right(screen);
keys.OK(displayId?)
直接派发 KEYCODE_DPAD_CENTER。
参数
同上。
示例
keys.OK();
keys.OK(screen);
keys.KEYCODE_*
keys 对象上还会自动挂出 Android KeyEvent 里的所有静态 KEYCODE_* 常量。
也就是说,像下面这些值都能直接读:
log(keys.KEYCODE_HOME);
log(keys.KEYCODE_BACK);
log(keys.KEYCODE_VOLUME_UP);
适合怎么配合使用
const code = keys.KEYCODE_BACK;
keys.KeyCode(code);
if (keys.keyCode("home") == keys.KEYCODE_HOME) {
log("解析成功");
}
一段够用的新手示例
auto.waitFor();
if (currentPackage() != "com.tencent.mm") {
home();
sleep(500);
}
Text("hello scriptx");
sleep(300);
Back();
sleep(300);
keys.notifications();
一段虚拟屏示例
const screen = vdisplay.create({ width: 720, height: 1280 });
keys.Home(screen);
sleep(500);
keys.Text("search demo", screen);
sleep(300);
keys.Back(screen);
最容易搞混的 4 组写法
back() 和 Back()
back()更偏“系统返回动作”Back()更偏“直接发返回键”
keys.back(display) 和 keys.Back(display)
keys.back(display)名字虽然是“小写系统动作版”,但传了目标显示器以后,它也会直接退成对该显示器派发KEYCODE_BACKkeys.Back(display)则从头到尾都只是直接发按键事件
powerDialog() 和 Power()
powerDialog()想要的是“打开电源菜单”Power()发的是物理电源键值
KeyCode(...) 和 keyCode(...)
KeyCode(...)会发送keyCode(...)只解析,不发送
