inputMethod 输入法控制
inputMethod 输入法控制
inputMethod 是 ScriptX 新增的输入法控制模块。它不是“往任意进程里偷偷塞文本”的那种黑盒接口,而是围绕 ScriptX 自带输入法服务来工作的一整套能力:
- 先把 ScriptX 输入法启用并切成当前输入法
- 再等待输入法服务和当前编辑框就绪
- 然后做文本提交、替换、删改、选区控制、编辑器动作
这套能力的好处是行为比较稳定,尤其适合:
- 自己写主应用脚本或无宿主脚本时做文本注入
- 需要精细控制光标和选区
- 想知道“输入框现在到底拿没拿到焦点、包名是谁、选区在哪”
先记住这 12 条
- 全局对象名是
inputMethod,兼容别名是$inputMethod。 - 这套 API 只在 ScriptX 主应用运行时或无宿主运行时可用,普通 App Hook 场景里不能直接拿它操作别的进程输入框。
enable()、activate()、restore()、disable()都依赖 Root,因为底层会执行ime enable/ime set/ime disable。openSettings()和showPicker()不需要 Root,但仍然要求当前运行环境支持inputMethod。- 真正做文本操作前,最稳的顺序通常是:
enable()->activate()->waitForReady()。 status()不是简单布尔值,它会把启用状态、选中状态、当前编辑框快照、上一个输入法 id 一起带出来。disable()有一个硬限制:只要 ScriptX 输入法还是当前选中的输入法,就不能直接禁用。restore()只会恢复“最近一次activate()前保存下来的旧输入法”;如果从没保存过,会直接报错。text()/commitText()走的是 AndroidcommitText语义;replaceText()/setText()才是整段替换。moveCursor(direction, length)的双参数写法里,direction只能填-1或1。editorAction()支持固定字符串动作名,也支持直接传非负整数 action id。inputMethod.id是当前 ScriptX 输入法服务的组件 id,排查系统输入法切换问题时很好用。
inputMethod.isSupported()
判断当前运行时是否允许使用 inputMethod 模块。
返回值
boolean
什么时候会返回 true
- 当前脚本运行在 ScriptX 主应用
- 或者运行在无宿主运行环境
什么时候会返回 false
- 普通 App Hook 脚本
- 当前没有可用的 ScriptX 主应用上下文
示例
if (!inputMethod.isSupported()) {
throw new Error("当前环境不支持 inputMethod");
}
inputMethod.status() / inputMethod.getState()
返回当前输入法模块的完整状态快照。
const state = inputMethod.status();
log(JSON.stringify(state, null, 2));
返回值
object
返回对象字段
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | ScriptX 输入法组件 id |
runtimeSupported | boolean | 当前运行环境是否支持 inputMethod |
enabled | boolean | ScriptX 输入法是否已在系统输入法列表中启用 |
selected | boolean | 当前系统默认输入法是不是 ScriptX 输入法 |
ready | boolean | 当前输入法服务是否准备好处理编辑框 |
serviceRunning | boolean | ScriptX 输入法服务是否正在运行 |
selectedInputMethodId | string | 当前系统选中的输入法 id |
previousInputMethodId | string | null | 最近一次激活前保存的旧输入法 id |
rootMode | boolean | 当前 ScriptX Shell 是否处于 Root 模式 |
editor | object | 当前编辑框快照;只有服务可访问编辑器时才会出现 |
editorError | string | 拉取编辑框快照失败时的错误信息 |
editor 子字段
| 字段 | 类型 | 说明 |
|---|---|---|
ready | boolean | 当前编辑框是否可操作 |
packageName | string | null | 当前编辑框所属应用包名 |
fieldId | string | null | 当前控件字段标识 |
inputType | number | Android inputType 原始值 |
imeOptions | number | Android imeOptions 原始值 |
actionId | number | 当前编辑器 action id |
actionLabel | string | null | 自定义 action 文案 |
selectionStart | number | 当前选区起点 |
selectionEnd | number | 当前选区终点 |
textLength | number | 当前编辑框文本长度 |
怎么理解 ready
status().ready 是最值得先看的那个字段:
false:输入法服务还没真的接到当前输入框true:可以开始做提交文本、改选区、删文本这些动作
示例:打印一份排错快照
const state = inputMethod.status();
log(`enabled=${state.enabled}`);
log(`selected=${state.selected}`);
log(`ready=${state.ready}`);
log(`serviceRunning=${state.serviceRunning}`);
log(`selectedInputMethodId=${state.selectedInputMethodId}`);
log(`previousInputMethodId=${state.previousInputMethodId}`);
if (state.editor) {
log(JSON.stringify(state.editor, null, 2));
}
inputMethod.isEnabled()
判断 ScriptX 输入法是否已经在系统输入法列表里启用。
返回值
boolean
典型用途
- 区分“还没启用”还是“已经启用但没切换过去”
- 在首次引导脚本里决定要不要先
enable()
示例
if (!inputMethod.isEnabled()) {
inputMethod.enable();
}
inputMethod.isSelected() / inputMethod.isActive()
判断当前系统默认输入法是否就是 ScriptX 输入法。
返回值
boolean
和 isEnabled() 的区别
| API | 含义 |
|---|---|
isEnabled() | 只是系统输入法列表里允许使用 |
isSelected() | 当前已经真的切到了 ScriptX 输入法 |
示例
if (!inputMethod.isSelected()) {
inputMethod.activate();
}
inputMethod.isReady()
判断当前输入法服务和当前编辑框是否已经准备好接收输入操作。
返回值
boolean
什么时候适合用它
- 做文本提交前快速判断
- 不想拿整份
status(),只想要一个布尔值
示例
if (!inputMethod.isReady()) {
inputMethod.waitForReady(5000);
}
inputMethod.openSettings()
直接打开系统输入法设置页。
返回值
boolean
成功时返回 true。
什么时候适合用它
- 首次引导用户去启用 ScriptX 输入法
- 让用户自己检查当前系统输入法状态
示例
toast("请先在系统设置里启用 ScriptX 输入法");
inputMethod.openSettings();
inputMethod.showPicker()
弹出系统输入法选择器。
返回值
boolean
它适合什么场景
- ScriptX 输入法已经启用,但你不想强制 Root 切换
- 想让用户自己手动选输入法
示例
if (inputMethod.isEnabled() && !inputMethod.isSelected()) {
inputMethod.showPicker();
}
inputMethod.enable()
启用 ScriptX 输入法。
返回值
boolean
成功时返回 true。
前提
- 当前环境支持
inputMethod - Root 可用
真实行为
- 底层会执行
ime enable <componentId> - 只负责启用,不负责切换成当前输入法
示例
if (!inputMethod.isEnabled()) {
inputMethod.enable();
}
inputMethod.activate() / inputMethod.activateInputMethod()
启用并切换到 ScriptX 输入法。
const state = inputMethod.activate();
log(JSON.stringify(state, null, 2));
返回值
object
返回值就是最新的 status() 结果。
它做了什么
- 检查当前输入法是谁
- 如果当前输入法不是 ScriptX 输入法,就先把旧输入法 id 保存起来
- 执行
ime enable和ime set - 返回最新状态快照
什么时候会保存旧输入法
只有当前输入法:
- 非空
- 且不是 ScriptX 输入法本身
才会存进 previousInputMethodId,供后面的 restore() 使用。
示例:标准激活流程
inputMethod.enable();
const state = inputMethod.activate();
log(`selected=${state.selected}`);
inputMethod.waitForReady(timeoutMs?)
等待输入法服务和当前编辑器进入可用状态。
const ok = inputMethod.waitForReady(8000);
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
timeoutMs | number | 0..120000 的有限数字 | 5000 | 最长等待时长,单位毫秒 |
返回值
boolean
| 返回值 | 含义 |
|---|---|
true | 在超时前准备好了 |
false | 到了超时时间仍未就绪 |
边界行为
- 小于
0会按0处理 - 大于
120000会按120000处理 - 不传时默认是
5000ms
示例
inputMethod.enable();
inputMethod.activate();
if (!inputMethod.waitForReady(8000)) {
throw new Error("输入法服务没有在 8 秒内准备好");
}
inputMethod.restore(disableAfterRestore?) / inputMethod.restoreOriginalInputMethod(disableAfterRestore?)
恢复到上一次激活前保存下来的旧输入法。
inputMethod.restore();
inputMethod.restore(true);
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
disableAfterRestore | boolean | true / false | false | 恢复旧输入法后,是否顺手把 ScriptX 输入法也禁用掉 |
返回值
object
返回最新 status() 快照。
前提
必须先有一次成功的 activate() 保存过旧输入法,否则会直接报错。
什么时候会报错
- 从没保存过旧输入法
- 保存下来的旧输入法 id 是空值
- 保存下来的旧输入法 id 又指回 ScriptX 自己
- 保存值已经不是合法输入法组件 id
示例:只恢复,不禁用
inputMethod.restore(false);
示例:恢复后把 ScriptX 输入法也关掉
inputMethod.restore(true);
inputMethod.disable()
禁用 ScriptX 输入法。
返回值
boolean
一个很关键的限制
如果当前系统选中的输入法还是 ScriptX 输入法,这个方法不会偷偷帮你切走,而是直接报错:
Restore or select another input method before disabling ScriptX input method
正确顺序
通常应该先:
restore()恢复旧输入法- 再
disable()
示例
inputMethod.restore();
inputMethod.disable();
inputMethod.text(text, newCursorPosition?) / inputMethod.inputText(...) / inputMethod.commitText(...)
向当前编辑框提交一段文本。
inputMethod.text("hello");
inputMethod.commitText("world", 1);
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
text | 任意 | 任意 JS 值 | 必填 | 会先转成字符串;null / undefined 会转成空字符串 |
newCursorPosition | number | Java int 范围内的有限数字 | 1 | 直接按 Android commitText 语义传给输入法服务 |
返回值
boolean
它和 replaceText() 的区别
| API | 含义 |
|---|---|
text() / commitText() | 做一次提交,保留输入法提交语义 |
replaceText() | 直接把整段文本替换成新文本 |
示例:最常见输入
inputMethod.text("你好,ScriptX");
示例:提交后让光标继续停在末尾
inputMethod.commitText(" 追加内容", 1);
inputMethod.replaceText(text) / inputMethod.setText(text)
直接把当前编辑框文本替换成指定内容。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
text | 任意 | 会先转成字符串;空值会变成空字符串 |
返回值
boolean
什么时候更适合用它
- 你想把输入框重置成一段确定文本
- 不想让原文本保留任何旧内容
示例
inputMethod.replaceText("https://example.com");
inputMethod.setText("");
inputMethod.getText()
读取当前编辑框完整文本。
返回值
string
示例
const text = inputMethod.getText();
log(text);
inputMethod.getSelection()
读取当前选区。
返回值
{ start, end } | null
返回结构
| 字段 | 类型 | 说明 |
|---|---|---|
start | number | 选区起点 |
end | number | 选区终点 |
什么时候会是 null
- 当前没有可读取的编辑框
- 输入法服务拿不到选区信息
示例
const selection = inputMethod.getSelection();
if (selection) {
log(`start=${selection.start}, end=${selection.end}`);
}
inputMethod.selectedText() / inputMethod.getSelectedText()
读取当前被选中的那段文本。
返回值
string
示例
const selected = inputMethod.getSelectedText();
log(`selected=${selected}`);
inputMethod.setSelection(start, end?) / inputMethod.setSelect(start, end?)
设置当前选区范围。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
start | number | Java int 范围内有限数字 | 必填 | 选区起点 |
end | number | Java int 范围内有限数字 | start | 选区终点 |
返回值
boolean
示例:选中前 5 个字符
inputMethod.setSelection(0, 5);
示例:把光标折叠到第 10 位
inputMethod.setSelection(10);
inputMethod.setCursor(position) / inputMethod.setSelectPosition(position)
把光标折叠到指定位置。
参数
| 参数 | 类型 | 可填值 | 说明 |
|---|---|---|---|
position | number | Java int 范围内有限数字 | 光标位置 |
返回值
boolean
它和 setSelection() 的关系
它本质上等价于:
inputMethod.setSelection(position, position);
示例
inputMethod.setCursor(3);
inputMethod.moveCursor(offset) / inputMethod.moveCursor(direction, length)
相对移动光标。
支持的两种写法
1. 单参数偏移写法
inputMethod.moveCursor(-1);
inputMethod.moveCursor(5);
这时参数含义就是“直接偏移多少”。
2. 方向 + 长度写法
inputMethod.moveCursor(-1, 3);
inputMethod.moveCursor(1, 2);
这时:
direction = -1表示向前direction = 1表示向后
参数
| 写法 | 参数 | 可填值 | 说明 |
|---|---|---|---|
| 单参数 | offset | 任意 Java int 范围内有限数字 | 直接偏移量 |
| 双参数 | direction | 只能是 -1 或 1 | 方向 |
| 双参数 | length | >= 0 的整数 | 移动长度 |
返回值
boolean
会报错的情况
- 双参数写法里
direction不是-1或1 length是负数
示例:向前移动 1 位
inputMethod.moveCursor(-1);
示例:向后移动 4 位
inputMethod.moveCursor(1, 4);
inputMethod.deleteSelection() / inputMethod.delSelect()
删除当前选中的文本。
返回值
boolean
示例
inputMethod.setSelection(0, 5);
inputMethod.deleteSelection();
inputMethod.clear()
清空当前编辑框文本。
返回值
boolean
示例
inputMethod.clear();
inputMethod.deleteSurroundingText(beforeLength, afterLength?)
按“光标前多少个字符、光标后多少个字符”的方式删除文本。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
beforeLength | number | >= 0 的整数 | 必填 | 删除光标前多少个字符 |
afterLength | number | >= 0 的整数 | 0 | 删除光标后多少个字符 |
返回值
boolean
示例:删前 1 个,删后 2 个
inputMethod.deleteSurroundingText(1, 2);
inputMethod.deleteText(direction, length)
按方向删除文本。
参数
| 参数 | 类型 | 可填值 | 说明 |
|---|---|---|---|
direction | number | 只能是 -1 或 1 | -1 表示删前面,1 表示删后面 |
length | number | >= 0 的整数 | 删除长度 |
返回值
boolean
它内部等价于什么
| 写法 | 等价逻辑 |
|---|---|
deleteText(-1, n) | deleteSurroundingText(n, 0) |
deleteText(1, n) | deleteSurroundingText(0, n) |
示例
inputMethod.deleteText(-1, 3);
inputMethod.deleteText(1, 2);
inputMethod.backspace(length?)
删除光标前面的字符。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
length | number | >= 0 的整数 | 1 | 删除前方字符数量 |
返回值
boolean
示例
inputMethod.backspace();
inputMethod.backspace(5);
inputMethod.deleteForward(length?)
删除光标后面的字符。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
length | number | >= 0 的整数 | 1 | 删除后方字符数量 |
返回值
boolean
示例
inputMethod.deleteForward();
inputMethod.deleteForward(2);
inputMethod.keyCode(keyCode, metaState?)
向当前编辑器发送一个按键事件。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
keyCode | number | Java int 范围内有限数字 | 必填 | Android KeyEvent.KEYCODE_* 数值 |
metaState | number | Java int 范围内有限数字 | 0 | 组合键状态,如 Shift、Ctrl |
返回值
boolean
示例:发一个回车键
inputMethod.keyCode(66);
示例:发一个带 Shift 的按键
const META_SHIFT_ON = 1;
inputMethod.keyCode(29, META_SHIFT_ON);
inputMethod.enter()
发送回车键。
返回值
boolean
它等价于什么
inputMethod.keyCode(66, 0);
示例
inputMethod.enter();
inputMethod.editorAction(action)
执行当前编辑器 action。
inputMethod.editorAction("search");
inputMethod.editorAction("done");
inputMethod.editorAction(3);
参数
action 支持两类写法。
1. 固定字符串
| 可填值 | 含义 |
|---|---|
"" | IME_ACTION_UNSPECIFIED |
"unspecified" | IME_ACTION_UNSPECIFIED |
"none" | IME_ACTION_NONE |
"go" | IME_ACTION_GO |
"search" | IME_ACTION_SEARCH |
"send" | IME_ACTION_SEND |
"next" | IME_ACTION_NEXT |
"done" | IME_ACTION_DONE |
"previous" | IME_ACTION_PREVIOUS |
"prev" | IME_ACTION_PREVIOUS |
2. 非负整数
可以直接传 Android action id,但必须是 >= 0 的整数。
返回值
boolean
示例:执行搜索动作
inputMethod.editorAction("search");
示例:执行完成动作
inputMethod.editorAction("done");
inputMethod.hide()
请求隐藏当前输入法窗口。
返回值
boolean
什么时候适合用它
- 文本输入完毕后收起输入法
- 自动化流程里避免键盘遮挡界面
示例
inputMethod.hide();
inputMethod.id
当前 ScriptX 输入法服务组件 id。
类型
string
典型值
类似这样:
com.daowuya.scriptx/com.daowuya.scriptx.core.input.ime.ScriptXInputMethodService
用途
- 和
status().selectedInputMethodId对比 - 手动排查系统输入法切换状态
示例
log(inputMethod.id);
inputMethod.api
inputMethod 自己的别名引用。
类型
object
什么时候会用到
- 统一按
xxx.api风格做动态模块分发 - 想把模块对象显式传给别的辅助函数
示例
const api = inputMethod.api;
log(api === inputMethod);
一段完整的新手示例
下面这段流程把“启用 -> 切换 -> 等待就绪 -> 替换文本 -> 调整选区 -> 执行动作 -> 恢复旧输入法”串起来了。
if (!inputMethod.isSupported()) {
throw new Error("当前环境不支持 inputMethod");
}
if (!inputMethod.isEnabled()) {
inputMethod.enable();
}
inputMethod.activate();
if (!inputMethod.waitForReady(8000)) {
throw new Error("输入法没有准备好");
}
inputMethod.replaceText("ScriptX 输入法测试");
inputMethod.setSelection(0, 7);
log(`selected=${inputMethod.getSelectedText()}`);
inputMethod.setCursor(inputMethod.getText().length);
inputMethod.text(" - 已追加");
inputMethod.editorAction("done");
inputMethod.hide();
inputMethod.restore(false);
