Gboard 增强

Gboard 增强

简体中文

Android Xposed Java License

把 Gboard 的语言 / 布局切换交还给框架,让 BetterZUIKey 的输入法快捷键对 Gboard 同样可用

兼容 Gboard 17.2.2 ~ 18.3.1

君ノ声ガ 聞コエルヨ。

你的声音,我能听到呀。

声明:本仓库主要部分均为 AIGC,可能有缺陷,欢迎审查和 PR。

这是一个 BetterZUIKey 的扩展组件,建议与本体搭配使用以达到最佳效果。

缺少本体不影响除了“只响应系统框架语言切换消息(严格模式)”之外的其他功能。

应用图标基于搜狗输入法自带图标二次创作;流萤像素画来源未知,如有侵权请联系删除


开发动机

Gboard 确实是一款优秀的输入法,但在使用过程中,仍有这样的问题:

其一:喧宾夺主

Gboard 把「切语言」这件事攥在自己手里——地球键、Shift 单击中/英、以及它自己的语言列表。这些动作要么在它进程内部闭环,要么由系统代它执行,框架看不见也管不了。

于是 BetterZUIKey 里「切换到下一个输入法语言」的逻辑与它根本对不上:BZK 发送的是 subtype 切换,而 Gboard 自身还有一套并行的切换逻辑,两边互相冲突。

其二:粗枝大叶

  • 物理键盘的中文态下把 {}、[]、+-、*# 等一律按全角输出,需要半角的场合只能切英文模式
    • 从逆向结果看:Gboard 原生库里有一张覆盖整个 ASCII 可打印区的 1:1 全角映射表,所以「逐个补进白名单」这条路永远补不完
  • 软键盘与物理键盘的引号 / 括号都没有自动补全
  • 中文态拼音栏还有字时按 Enter,浏览器地址栏 / 聊天消息栏会被一并提交,极易造成误触
    • Shift+Enter 可以缓解,但不能解决根本问题

本模块注入 Gboard 进程,拦截它自己的语言切换、接管中文态的标点与提交管线、补齐自动补全——以此让它与框架(以及 BZK)协同工作。

功能特性

  • 严格模式:拦截 Gboard 自己的语言 / 布局切换,语言只由框架 / BetterZUIKey 决定
    • 与 BetterZUIKey 联动:未检测到 BZK 时严格模式开关禁用;需要在 BZK 的「输入法增强 → 输入法适配管理」里把 Gboard 加进「使用系统框架」(BZK v1.7.0 以上),由 BZK 完全接管
  • 标点管线:把「中英字符」与「全半角」拆开,分别受不同开关控制
  • 引号 / 括号自动补全:软键盘与物理键盘两个独立开关,共用同一份可编辑配对表(默认 18 对)
    • 有选区时包裹选区(abc → (abc))而不是替换它
    • 光标后侧已有闭字符时只把光标移过去,不会重复插入;手动移动光标后也可以恢复正常闭合
  • 中文态 Enter 不提交:拼音照常上屏,但不再把输入框误提交
  • 配置热生效:广播推送 + 有界补播链,修改配置无需重启输入法
  • 状态位镜像:三个状态位(全角 / 中英标点 / 物理补全)的当前档位回传到设置页显示,可以长按条目应急切换
  • 替换语音输入:用自定义引擎接管 Gboard 的语音键,长按卡片进入配置页
    • 云端脚本引擎:内置讯飞 / 腾讯云 / 豆包(新版 / 旧版),也可以导入自定义脚本
    • 离线模型:SenseVoice / Paraformer / 两档流式 Zipformer,完全本机识别(与云端配置互斥)

功能一览

功能 解释 默认值
只响应系统框架语言切换消息 严格模式:拦截 Gboard 的原生切换,语言只认框架信号 关 ¹
完整的 …… 和 —— 输入 — / … 时输出两个而不是一个 开
智能中文标点 更合理的中文标点映射(+ - # [ ] 等按半角处理) 开
全角模式 允许在全角 / 半角之间切换(提交的字母数字也一起转,如 ABC123;拼音组合态只转符号) 切换快捷键:Shift+Space 开(状态默认半角)
中英文标点 允许中文态在中英标点之间切换 切换快捷键:Ctrl+. 开(状态默认中文标点)
智能编号 数字后面的 。 ) 自动用半角,方便输入 1. 2) 开
引号 / 括号自动补全(软键盘) 软键盘打引号、括号时自动补另一半并把光标移进中间 开
物理键盘自动补全 同上,但只认硬件按键 切换快捷键:Ctrl+Shift+9 关(状态默认开)
中文态 Enter 不提交 中文态按 Enter 时不再把整个输入框提交 关
替换语音输入 用自定义引擎(云端脚本或离线模型)接管 Gboard 的语音键,长按卡片进入配置页 关

¹ 未检测到 BetterZUIKey 时此项禁用(严格模式要靠 BZK 接管语言切换),强行开启会导致没有可用的切换快捷键

只有三个状态位(全角 / 中英标点 / 物理补全)带快捷键;其余都是纯开关,修改后即时生效。

离线语音

「语音输入」卡片里可以选离线模型:完全在本机识别,不联网也能正常使用;与上述云端配置文件 (讯飞等)互斥,同一时刻只有一个生效。模型在设置页下载,四档体积差异较大,可按需选择:

档位 体积 语言 标点 说明
SenseVoice-Small 约 228 MiB 中/粤/英/日/韩 自带 准确率较高,自带标点与数字规整
Paraformer-Small 约 78 MiB 中英 需补全 体积与速度的折中
Zipformer-中文 约 24 MiB 仅中文 需补全 流式:边说边出字,体积最小
Zipformer-中英 约 189 MiB 中英 需补全 流式:边说边出字,中英混说

设置页卡片上的准确率(97% / 96%)来自官方在公开朗读测试集(AISHELL-1)上的去标点字错率换算, 实际效果视口音与噪声而定;两档流式模型官方未公布数字。

  • 流式档位可以边说边出字;若要提高非流式模型的实时性,可以打开「启用切分」,使用 silero_vad 按句切分。 流式模型对口语的非流利现象较为敏感,转录效果可能下降。
  • 「补全标点」给不带标点的模型(Paraformer / Zipformer)插入中英标点,需要额外一个约 72 MiB 的 标点模型。
  • 权重与推理组件的许可以及 SenseVoice / Paraformer 的署名见 THIRD_PARTY_NOTICES.md 以及设置页「关于 → 开源许可」。
  • 权重文件的副本也发布在 个人网站,为保证国内用户的下载体验,优先从此下载,失败后回退 GitHub / Hugging Face

云端语音引擎

除了离线模型,也可以让云端引擎接管 Gboard 的语音键。需要先到各家控制台开通服务,再长按 「替换语音输入」卡片进入子页,单击对应配置填写参数,保存后该配置自动启用。

配置 填写内容 开通位置
讯飞语音听写 APPID / APISecret / APIKey 语音听写(流式版)
腾讯云实时语音识别 AppID / SecretId / SecretKey / 引擎(默认 16k_zh) 实时语音识别(需开通)
账号 APPID(13 位数字)
API 密钥
豆包流式语音识别(新版) API Key / 模型版本 服务管理(开通「流式语音识别 2.0」)
API Key 管理
豆包流式语音识别(旧版) App ID / Access Token / 模型版本 应用管理(旧版控制台)
Access Token 在该应用详情页

「模型版本」请选择控制台里已开通的那一个; 若选错,会在上屏时提示「这个模型版本没有开通」,届时请更换正确版本。

豆包的两条配置还有三个开关:二次识别修正 / 规范为书面格式(ITN) / 删除不流畅内容,默认均关。

每个引擎只允许连接其自身的服务域名,音频不会发往其他地址。

工作原理

本模块在 Gboard 进程内完成六件事:拦截语言切换 / 路由物理按键 / 改写提交内容 / 配对与补全 / 回传状态位 / 替换语音输入。

逆向细节、实测数据与已知陷阱见 PRINCIPLE.md。

模块 App(MainActivity)
    ↕ 显式广播(ConfigSender · 变更即刻发 + 有界补播链 · 接收端 SenderCheck 验来源)
    ↕ 状态位反向广播(模块 → App,用于设置页显示「当前状态」)
    ↕ 离线权重交付(ModelProvider · App 下载后以 FD 交给输入法进程)
Gboard 进程(BridgeHook)
    ├── SwitchGuard     严格模式①:IMM / InputMethodService / IMMS 代理 / 特权代理 四层挂点
    ├── KeyGuard        严格模式②:拦注入的 KEYCODE_LANGUAGE_SWITCH(204)
    ├── KeyRouter       物理按键总路由:Shift+Space / Ctrl+. / Ctrl+Shift+9 + Shift 单击守卫
    │                 └ EnterFix   中文态 Enter:改写成 Shift+Enter,再吞掉注入事件
    ├── SymbolNormHook  RemoteInputConnection.commitText / setComposingText(框架类)
    │                 └ SymbolNorm  语义层 → 宽度层
    │                 └ AutoPair    补闭字符 / 包选区 / 跳过已存在的闭字符
    ├── Banner          热键反馈(不用 Toast)
    └── 替换语音输入(接管 Gboard 的识别器:云端脚本或离线模型)
        ├── RecognizerProxy  动态代理识别器接口(按方法「形状」分派)
        ├── VoiceEngineHost  会话生命周期 + 结果上屏(partial 走结果通道 / final 走 IC)
        │     ├── AudioSource   麦克风采集(16k / 单声道 / PCM16,40ms 一帧)
        │     ├── ScriptEngine  Rhino 脚本宿主 → WsClient(云端)
        │     ├── LocalAsr      sherpa-onnx:整段 / VAD 切分 / 流式(离线)
        │     └── GboardSink    把文字翻译成 Gboard 的结果对象
        └── NativeLibs / OfflineModels / OfflineSelfTest(抽 .so / 缓存 / 加载自检)

设计取舍

配置通道与权重传递上,本模块做了几个不太直观的决定,完整推导见 PRINCIPLE.md §9:

  • 用显式广播,而不是 ContentProvider —— Gboard 的 targetSdk=36,受 Android 11+ 包可见性限制,无法看到本模块的包;而可见性只约束发起方,反过来由本模块(能看见 Gboard)向它发送显式广播即可送达。接收端在 Gboard 进程内用输入法服务自身的 Context 运行时注册,不要求宿主 APK 声明任何内容。
  • 配置要落盘到目标进程 —— 广播是「一次性」的:Gboard 一重启就回到编译期默认值,开关会静默恢复默认值。所以模块收到广播时一并将配置落盘,启动时先读回;另加一条有界补播链,覆盖「修改设置时 Gboard 通常未在运行」的场景。
  • 状态位走反向广播 —— 三个状态位存在于 Gboard 进程的 prefs 里,App 无法读取,设置页也就无法显示「当前是哪一档」。所以热键切换的那一刻由模块发一条显式指定包名的广播回来。
  • 离线权重不进 APK、也不放共享目录 —— 权重(24 MiB ~ 228 MiB)打进 APK 会导致每次更新都要重新下载,所以由设置页下载到 App 自己的 files/;而 Gboard 无法读取其他 App 的文件,跨进程只能交付 FD。链路是:App 下载(前台服务 + 通知栏进度)→ 交给输入法进程 → 校验体积与 sha256 后落进自己的缓存,只保留当前选中的那一档。

改代码前先读这个

已知陷阱都记在对应类的注释里;下面几条反直觉且会静默失效,改动前建议先读一遍 (推导与实测数据见 PRINCIPLE.md):

  • 挂 onKeyDown 必须挂「实例类链」 —— Gboard 覆盖了它,只挂 InputMethodService 只能看到 onKeyUp(实测:没有任何一条 onKeyDown 日志)。同理,挂在框架类上的钩子有一部分从来不响(覆盖版不调 super ⇒ 框架实现永不执行),这是跨版本必修项,见 §7。
  • 符号归一要挂 RemoteInputConnection 的全部重载 —— 框架类、不被混淆,但 API 33+ 的 3 参 TextAttribute 版本才是 Gboard 实际走的那条,只挂两参会漏掉大部分符号(§3)。
  • 半角化必须用区间规则(FF01–FF5E → ASCII),逐个补表补不完 —— Gboard 原生 .so 里是一张覆盖整个 ASCII 可打印区的 1:1 全角表,+= 就是这样被漏掉的(§4)。
  • 同一个方法最多挂 64 次(libxposed 上限)⇒ 所有 hook 都要带「装过就跳过」的旗标,否则每次会话重装会把日志刷满(§10)。

在改动严格模式或语言门控之前,强烈建议先通读 PRINCIPLE.md §2 / §3 —— 那两节的结论经过三轮互相推翻才收敛,重走一遍成本很高。

模块安装

  1. 前置条件:已安装 LSPosed + Gboard (严格模式另需 BetterZUIKey v1.7.0 以上,未安装时该开关禁用)
项 值
包名 com.google.android.inputmethod.latin
兼容版本 Gboard 17.2.2 ~ 18.3.1
ABI arm64

模块以 17.2.2 为开发基线;18.1.3 完成静态核验;18.3.1 实装 A/B 测试。 除了 DexKit 的结构化查找之外,功能挂的都是框架类 / 框架方法名,不硬编码混淆名,所以换版本时更可能是「功能降级」而不是崩溃。

  1. 在 Releases 下载 APK 并安装
  2. LSPosed Manager 里启用模块 —— 作用域由模块静态声明(只有 Gboard),无需也无法手动勾选
  3. 杀掉 Gboard 进程,让 hook 生效
  4. 打开模块 App,主页可见各开关;顶部「自启动权限 → 去获取」可跳到系统设置,放行后配置同步更稳定
  5. 若要严格模式(需 BZK v1.7.0 以上):在 BZK 的「输入法增强 → 输入法适配管理」的「使用系统框架」一段里勾上 Gboard(1.7.0 起为内置项,默认关),再打开本模块的严格模式开关

开发构建

git clone git@github.com:CommandPrompt-Wang/BetterZUIKey-GboardExt.git
cd BetterZUIKey-GboardExt
./gradlew :app:assembleDebug
# APK: app/build/outputs/apk/debug/BetterZUIKey-GboardExt-v<versionName>.apk

需要 JDK 17 + Android SDK 37(compileSdk 37 / minSdk 27 / targetSdk 36),以及 libxposed API 101。

  • 请自备 app-sign.keystore 和 keystore.properties(与 BZK 同一套签名,都不入库)
  • 运行期按结构(DexKit)而非写死的混淆名去找 Gboard 内部类

日志

adb shell logcat -s GboardExt
norm hooked 2 method(s) on RemoteInputConnection
keys: installed on <impl class> (2)
strict switch guard installed: 74 hook(s), STRICT=false
key guard: service side installed
config restored: strict=false, longMarks=true, num=true, enter=false, ...
config broadcast: strict=false, longMarks=true, num=true, enter=false, ...
hotkey Shift+Space -> fullwidth=true
pair: ( -> () [hw]
enter: down gate enabled=false chinese=true composing=true meta=0x0 pkg=...
enter: rewrite down -> Shift+Enter pkg=...
strict: blocked injected LANGUAGE_SWITCH (total 3)

各诊断旗标(DEV_TRACE / DEV_*)默认关,排查时按需在对应类里打开。改动 EnterFix / AutoPair 之前建议先看 enter: down gate ... 那一行——「第一次不生效」这类问题通常能直接从该行定位。

⚠️ 免责声明

这是一个 LSPosed 模块,直接 hook 输入法的按键、提交与语言切换链路。使用前请:

  • 理解每个开关的含义再操作
  • 不当配置可能导致切不到某个语言、标点 / 配对行为异常,或严格模式下无法切换语言(此时先关掉严格模式)
  • 只针对 com.google.android.inputmethod.latin 的 Gboard 17.2.2 ~ 18.3.1 / arm64 实测;其它版本可以尝试,但不保证效果

开发者不承担因使用本模块造成的输入异常、数据丢失或设备故障的任何责任。

项目结构

app/src/main/java/moe/lovefirefly/bzk/gboardext/
├── BridgeHook.java          # Xposed 入口:按包名过滤 + 后台线程装各 hook
├── SwitchGuard.java         # 严格模式①:四层挂点拦「当前输入法内部」的 subtype 切换
├── KeyGuard.java            # 严格模式②:拦注入的 KEYCODE_LANGUAGE_SWITCH(204)
├── KeyRouter.java           # 物理按键总路由:三个热键 + Shift 单击守卫(挂实例类链)
├── ServiceProbe.java        # 定位 IME 实现类 / 中文态判据 / DexKit 结构化查找 / 横幅视图挂载
├── SymbolNormHook.java      # 提交管线挂点:RemoteInputConnection(全部重载)+ 智能编号
├── SymbolNorm.java          # 符号归一:语义层(该输出什么字符)+ 宽度层(宽窄,区间规则)
├── EnterFix.java            # 中文态 Enter:down/up 成对改写为 Shift+Enter + 吞掉注入事件
├── AutoPair.java            # 配对:补闭字符 / 包裹选区 / 跳过已存在闭字符(光标兜底)
├── GboardPair.java          # 配对表:开→闭 Map + 转义 + 自检(奇偶 / 重复)
├── GboardState.java         # 三个状态位,存在目标进程 prefs 里 + 镜像回 App
├── GboardConfig.java        # 配置结构 / 默认值 / dump + parseDump
├── BroadcastConfig.java     # 配置通道:运行时注册的显式广播接收器 + 落盘到目标进程
├── SenderCheck.java         # 广播来源校验:签名级权限 + 发送方核对(阻止第三方伪造配置)
├── ConfigSender.java        # App 侧唯一发送入口(设置页 / 补播 / 开机共用)
├── ConfigRetry.java         # 有界补播链(AlarmManager.set,不需要精确闹钟权限)
├── ConfigRetryReceiver.java # 补播链的一环 + 开机 / 升级入口
├── ConfigWatch.java         # provider 轮询(在 Gboard 上不可行,默认关,留作兜底)
├── ConfigProvider.java      # provider 通道(同上,仅兜底)
├── TraceProbe.java          # 全量诊断探针:一次按键即可看出真实路径(默认关)
├── Banner.java              # 输入法窗口上的一行提示(Toast 会被通知设置拦截)
├── MainActivity.java        # 首页:所有开关 + 语音入口 + 「当前状态」显示
├── LicensesActivity.java    # 开源许可页(随 APK 分发的组件 + 用户下载模型的署名)
│
│ # 替换语音输入(云端脚本 / 离线模型)——宿主侧与 App 侧
├── VoiceEngineHost.java     # 宿主:会话生命周期、klu 代理、结果上屏(partial 走结果通道 / final 走 IC)
├── RecognizerProxy.java     # 动态代理实现 Gboard 的识别器接口(按方法的「形状」分派,不依赖混淆名)
├── AudioSource.java         # 麦克风采集:16k / 单声道 / PCM16,40ms 一帧 + RMS 音量回调
├── ScriptEngine.java        # Rhino 脚本宿主 + ctx.* 原语(ws / b64 / hmac / localAsr*)
├── WsClient.java            # 极简 WebSocket 客户端(RFC 6455 子集),只服务于引擎脚本
├── GboardSink.java          # 把识别文字翻译成 Gboard 的结果对象(反射 + 形状查找,对不上就降级透传)
├── VoiceProbe.java          # 会话准入探测(Gboard 调用序列与参数)
├── VoiceProfiles.java       # 引擎配置:内置 index.json 播种 + 用户导入
├── VoiceEngineActivity.java # 设置页:配置文件列表 + 离线模型卡片 + 「启用切分」「补全标点」
├── VoiceEngineAddActivity.java # 「添加配置文件」页(粘贴 / 导入脚本)
├── VoiceModels.java         # 离线模型清单:体积 / sha256 / 下载源 / 状态(与配置文件互斥)
├── ModelDownloadService.java# 下载:前台服务 + 通知栏进度 + 断点续传 + 哈希校验
├── ModelProvider.java       # 跨进程交权重:以 FD 交给输入法进程
├── OfflineModels.java       # 输入法侧缓存 bzk-models/<档位>/(只留当前选中的一档)
├── StorageProbe.java        # 存储探针:证明 Gboard 无法读取其他 App 的文件 ⇒ 只能通过 provider 交付 FD
├── OfflineSelfTest.java     # 离线引擎自检:抽出 .so → System.load → 询问版本号(失败仅记日志)
├── LocalAsr.java            # sherpa-onnx:整段解码 / VAD 切分 / 流式 transducer / 标点
└── NativeLibs.java          # 从模块 APK 抽 .so 并按依赖顺序 System.load

app/src/main/java/com/k2fsa/sherpa/onnx/    # sherpa-onnx 官方 Java API(随源码一起编译,宿主只用到其中一小部分)
app/src/main/assets/engines/               # 内置引擎脚本 + index.json:mock · iflytek · tencent · volc(新版)· volc-legacy(旧版)· sensevoice · paraformer · zipformer-{zh,bi}
app/src/main/assets/models/silero_vad.onnx # 「启用切分」用的 VAD 模型(随 APK 分发)
app/src/main/jniLibs/arm64-v8a/            # libonnxruntime.so + libsherpa-onnx-jni.so(离线语音用,随 APK 分发)

third_party/                 # 第三方组件与模型许可(Gradle 直接作为 assets 源目录打进 APK)

📄 许可证

GPL-3.0 © 2025–2026 CommandPrompt-Wang

本模块分发或使用了若干第三方组件与模型(sherpa-onnx · ONNX Runtime · silero-vad · Rhino 等, 以及 SenseVoice / Paraformer 等权重),各自的许可与署名见 THIRD_PARTY_NOTICES.md(许可全文在 third_party/licenses/, 随 APK 一起分发,设置页「关于 → 开源许可」里也能看)。

Releases

9/24/2026, 3:07:38 PM

Gboard 增强 v1.1.0

摘要

本次更新接管 Gboard 的语音键:可选用离线模型或云端引擎(讯飞 / 腾讯云 / 豆包)。离线模型完全在本机识别。另外,全角模式覆盖字母数字,修复了按下 Shift 使候选栏数字消失的问题与意外切语言的问题,设置页新增悬浮刷新键,并加入「开源许可」页。

完整变更: CommandPrompt-Wang/BetterZUIKey-GboardExt@1-1.0.0...2-1.1.0

相关 issue: #1 可否劫持谷歌语音输入?

新功能

语音输入:离线模型

完全在本机识别,无需联网。在设置页的「替换语音输入」里下载模型,与云端配置文件互斥,同一时刻只有一个生效。

档位 体积 语言 标点 说明
SenseVoice-Small 约 228 MiB 中/粤/英/日/韩 自带 准确率较高,自带标点与数字规整
Paraformer-Small 约 78 MiB 中英 需补全 体积与速度的折中
Zipformer-中文 约 24 MiB 仅中文 需补全 流式:边说边出字,体积最小
Zipformer-中英 约 189 MiB 中英 需补全 流式:边说边出字,中英混说
  • 启用切分:用 silero_vad 按句切分,非流式模型也能边说边出字,实时性更好
  • 补全标点:为不带标点的模型插入中英标点,需额外下载约 72 MiB 的标点模型
  • 模型不进 APK:由设置页下载,通知栏显示进度;优先从个人网站镜像下载,失败回退 GitHub / Hugging Face
  • 卡片上的准确率(97% / 96%)来自官方在公开朗读测试集上的去标点字错率换算,实际效果视口音与噪声而定;两档流式模型官方未公布数字

语音输入:云端引擎

先到各家控制台开通服务,再进「替换语音输入」子页填写参数,保存后自动启用:

配置 填写内容
讯飞语音听写 APPID / APISecret / APIKey
腾讯云实时语音识别 AppID / SecretId / SecretKey / 引擎(默认 16k_zh)
豆包流式语音识别(新版) API Key / 模型版本
豆包流式语音识别(旧版) App ID / Access Token / 模型版本
  • 「模型版本」是下拉项,只能从已开通的版本里选;选错会在上屏时提示「这个模型版本没有开通」
  • 豆包另有三个开关,默认关:二次识别修正 / 规范为书面格式 / 删除不流畅内容
  • 每个引擎只允许连接它自己的服务域名,音频不会发往别处
  • 引擎出错时把服务端返回的原文直接上屏,便于定位问题
  • 内置引擎脚本随模块更新自动升级,手动改过的不覆盖;已填写的参数不受影响

其它

  • 全角模式覆盖字母数字:全角态下 ABC、123 一并转换,此前只转符号。以此与其他输入法插件的行为对齐。
  • 开源许可页:设置页「关于 → 开源许可」,列出随 APK 分发的第三方组件与离线模型的许可和署名
  • 设置页:右下角新增悬浮刷新键,进入页面时自动刷新一次;当前输入法不是 Gboard 时提示「输入法未启用」

修复

  • Shift 卡住 / 按住 Shift 输入大写字母时意外切语言:远程桌面类应用同样受影响。收紧了原先过宽的拦截条件,被模块消费的组合键改为「按下时消费、抬起时放行」
  • 流式识别结果延迟上屏,并与已上屏文字重叠:收窄音频队列,修正停止时的收尾,说话结束即出字
  • 流式识别丢开头:预热模型期间先做一段预滚缓冲,第一句不再丢失
  • 标点模型:与语音模型一起预热,避免重复加载与阻塞音频;并发拉取的竞态一并修复
  • 云端引擎:收尾丢字、空结果被当作失败、改配置不生效,三处一并修正
  • 连接失败信息更完整:把网关返回的原因写入日志,握手时带上 User-Agent

已知限制

  • 离线模型下载不支持续传:镜像站不响应 Range,中断后重新下载
  • 「中文态 Enter 不提交」只对物理键盘生效,软键盘的 Enter 不走按键事件
  • 流式模型可能对口语的非流利现象较为敏感,可能出现转录效果下降的问题;可改用非流式档位并打开「启用切分」

其他

  • 语音相关功能全部默认关,不配置时行为与 v1.0.0 一致
  • 仅针对 com.google.android.inputmethod.latin 的 Gboard,其他输入法无效
  • 兼容 Gboard 17.2.2 ~ 18.3.1 / arm64;以 17.2.2 为开发基线,18.1.3 完成静态核验,18.3.1 实装 A/B 测试
  • 逆向细节、实测数据与已知陷阱整理在仓库 PRINCIPLE.md
  • 本仓库主要部分为 AIGC,可能有缺陷,欢迎审查和 PR

⚠ 安装与升级注意

  • 覆盖安装即可,已有配置不受影响;语音功能需要时再进设置页开启
    • 如果发现缺少配置,可以点击「恢复内置配置」尝试覆盖
  • 离线模型体积 24 ~ 228 MiB,建议在 Wi-Fi 下下载;模型存放在模块自己的目录,只保留当前选中的那一档
  • 需要 Android 8.1+ / LSPosed;严格模式另需 BetterZUIKey v1.7.0 以上
  • 本模块直接 hook 输入法的按键、提交与语言切换链路。不当配置可能导致切不到某个语言、标点 / 配对行为异常,或严格模式下无法切换语言(此时先关掉严格模式)

Assets

1

9/19/2026, 1:47:04 PM

Gboard 增强 v1.0.0

摘要

首个公开版本。本模块通过 LSPosed 注入 Gboard 进程,拦掉它自己的语言 / 布局切换,把「切语言」这件事交还给系统输入法框架 —— 让 BetterZUIKey 的「切换到下一个输入法语言」对 Gboard 也能用;并在提交链路上补齐标点归一、引号括号配对,以及中文态 Enter 不误提交。

完整变更: 本仓库首个版本,见 https://github.com/CommandPrompt-Wang/BetterZUIKey-GboardExt/commits/main

新功能

  • 框架驱动的语言切换(严格模式)(「只响应系统框架语言切换消息」,默认关):拦掉 Gboard 自己切语言 / 切布局,语言只由框架 / BetterZUIKey 决定。四层挂点(客户端 InputMethodManager / 输入法侧 InputMethodService / IMMS 的 Binder 代理 / IInputMethodPrivilegedOperations 代理),外加拦掉「地球键 → 注入 KEYCODE_LANGUAGE_SWITCH(204) → 系统替它切」这条路。需要 BetterZUIKey v1.7.0 以上。只拦「当前输入法内部」的 subtype 切换——目标是别的输入法时一律放行(那是换输入法,不是换语言);装不上、认不出的一律放行并打日志。

    • 1.6.x 亦可联动部分功能,但无法使用轮转顺序自定义功能,系统框架只能切换最近使用的 2 门语言。
  • 标点管线:把「该出什么字符(语义层)」与「宽窄(形式层)」拆开,二者可独立切换。

    功能 说明 快捷键 默认
    完整的 …… 和 —— 输入 — / … 时输出两个而不是一个 — 开
    智能中文标点 更合理的中文标点映射(+ - # [ ] 等按半角处理) — 开
    全角模式 允许在全角 / 半角之间切换 Shift+Space 开(状态默认半角)
    中英文标点 允许中文态在中英标点之间切换 Ctrl+. 开(状态默认中文标点)
    智能编号 数字后面的 。 ) 自动用半角,方便输入 1. 2) — 开
    • 半角 = 把 FF01–FF5E 按区间规则拉回 ASCII(只排除中文标点 !?;:,());全角 = 只把符号转全角、不动字母数字(否则中文态打字会变成全角拼音)
    • 之所以用区间而不是逐个补表:Gboard 原生的 .so 里躺着一张覆盖整个 ASCII 可打印区的 1:1 全角表,+= 这类就是补漏漏出来的,补表是打地鼠
  • 引号 / 括号自动补全:软键盘与物理键盘两个独立开关,共用同一份可编辑的配对表(默认 18 对,支持分组换行;留空 = 不配对)。

    • 软键盘(默认开):打引号、括号时自动补另一半并把光标移进中间
    • 物理键盘(默认关,状态默认开;快捷键 Ctrl+Shift+9):同上,但只认硬件按键
    • 有选区时包裹选区(abc → (abc))而不是替换它
    • 光标后侧已有闭字符时只把光标移过去,不再多吐一个;手动移动光标后可以恢复正常闭合
  • 中文态 Enter 不误提交(默认关):在「按回车就提交」的输入框(浏览器地址 / 搜索框、聊天消息栏)里,拼音栏还有字时按 Enter 会把原始拼音上屏、但整个输入框不再被提交。做法是把普通 Enter 整对(down + 配对的 up)改写为带 Shift 交给 Gboard 走它自己的 Shift+Enter 路径,再把这次「给拼音上屏用的」注入事件吞掉;拼音仍由 Gboard 自己提交,引擎内部状态天然同步。

  • 配置热生效:广播推送 + 有界补播链(0s / 10s / 30s / 1min / 3min / 10min / 30min),改完设置无需重启输入法,也照顾到「改设置那一刻 Gboard 往往没在跑」的场景。

  • 状态位镜像:三个状态位(全角 / 中英标点 / 物理补全)的当前档位回传到设置页显示,长按整条可应急切换。

  • 界面:Material 3 首页;最上面是「自启动权限」入口与说明;快捷键反馈用贴底横幅(Gboard 进程里的 Toast 会被系统按应用通知设置拦掉,实测静默失效)。

已知限制

  • 中文态 Enter 不提交不覆盖软键盘:软键盘上的 Enter 不走 onKeyDown,本功能只对物理键盘生效。

    • 不过软键盘一般也遇不到这类问题
  • 自启动状态无法自检:联想把每个应用的自启动状态记在安全中心的私有 prefs 里,其它应用无权限读,Settings.Global/Secure 中没有对应的键(疑似这一块被魔改),所以 App 判断不出来是否已放行,只能自行前往系统设置确认。

其他

  • 仅针对 com.google.android.inputmethod.latin 的 Gboard,其他输入法无效。
  • 兼容 Gboard 17.2.2 ~ 18.3.1 / arm64:以 17.2.2 为开发基线,18.1.3 完成静态核验,最终在 18.3.1 实装 A/B 测试。理论上该范围和接近此范围的版本都可以生效,其它遥远版本可以尝试,但不保证效果。
  • 好消息是:除 DexKit 的结构化查找之外,功能挂的都是框架类 / 框架方法名,不硬编码混淆名。实测这三代 Gboard 的混淆名整批换了三套而本模块挂点一个都不用改。因此换版本时更可能是功能降级而不是完全崩溃。
  • 逆向、踩坑与实测数据整理在仓库 PRINCIPLE.md。欢迎研究学习。
  • 本仓库主要部分为 AIGC,可能有缺陷,欢迎审查和 PR。

⚠ 安装与升级注意

  • 安装后在 LSPosed 中启用模块即可 —— 作用域由模块静态声明(只有 Gboard),无需也无法手动勾选;随后杀死 Gboard 进程(或重启设备),让 hook 生效。
  • 打开模块 App 可见各开关;系统设置里给个自启动权限会让配置同步更稳。
  • 若要用严格模式(需要 BZK v1.7.0 以上):在 BZK 的「输入法增强 → 输入法适配管理」页的「使用系统框架」一段里把 Gboard 勾上(1.7.0 起 Gboard 是内置项,默认关),再打开本模块的严格模式开关。
    • 框架默认只在「最近用过的两个语言」之间轮转;要让快捷键按顺序轮满多个语言,长按那条进「轮转顺序」页,打开「覆盖默认轮转顺序」。
  • 需要 Android 8.1+ / LSPosed。
  • 本模块直接 hook 输入法的按键、提交与语言切换链路。不当配置可能导致切不到某个语言、标点 / 配对行为异常,或严格模式下无法切换语言(此时先关掉严格模式)。

Assets

1