Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,11 @@ jobs:
channel_flags=(--prerelease)
latest_flag=--latest=false
fi
# The APK is the only asset; its SHA-256 and the signing certificate
# The full and lite APKs are the assets; their SHA-256 and the signing certificate
# are in the release notes, and the licenses ship inside the APK.
gh release create "$GITHUB_REF_NAME" \
release-files/openIME-*-arm64-release.apk \
release-files/openIME-*-arm64-lite-release.apk \
--repo "$GITHUB_REPOSITORY" \
--verify-tag \
--draft \
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,11 @@ __pycache__/
*.jks
*.keystore
*.p12

# Local dictionary data generated by scripts/import_doubao_data.py; not distributed
app/src/main/assets/english-words.tsv
app/src/main/assets/handwriting-strokes.tsv
app/src/main/assets/rime-data/openime_dicts/wubi86.dict.yaml

# Compiled engine tables generated by scripts/build_imengine_data.py; not distributed
app/src/main/assets/imengine/
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@ GitHub Release 的发布说明;版本与发布流程见 [docs/RELEASE.md](docs

## [Unreleased]

## [0.0.10-beta.1] - 2026-10-14

第十个测试版:对齐参考产品(横屏留白、单手模式、键盘调节),新增双拼、五笔、手写与自研拼音引擎,语音模型可自行导入,并新增不含语音模型的精简安装包。从 0.0.9-beta.1 覆盖安装即可,用户数据保留。

- 发布两种安装包:**完整版**(内置离线语音模型,开箱即用)与**精简版**(不含语音模型,体积小很多,需要语音输入时到「设置 → 语音模型」导入自己的模型)。两者签名相同,可互相覆盖安装,用户数据保留。
- 新增「双拼」键盘(小鹤双拼 / 自然码两套键位,设置里可选):26 键两键一字,词库与拼音 26 键共用;进入「切换键盘」面板即可选;模糊音等其它设置不受影响。
- 新增「单手模式」(关闭 / 左手 / 右手):键面收窄到 76% 宽度并贴向所选一侧,位于设置 → 横屏分组;横竖屏通用,左右分离布局不叠加;随数据备份一起保存恢复。
- 新增「键盘调节」:停靠键盘在顶栏按住上下拖动即可实时调整键盘高度(约 20% 屏高 = 40 个百分点,松手即存);手势只在顶栏起手,不抢候选项与输入区的长按。
- 横屏对齐参考产品:键区两侧留白(26 键 6.485% / 九键 9.5%)、顶栏留白 8.2%、展开候选 9.5%、26 键空格键加宽(权重 154→210,与其横屏皮肤 133.6→182.2 同比例);窗口仍整宽、背景铺满。
- 语音热词:transducer 模型改用 modified_beam_search;录音时把「语音热词」(模型页新增管理入口,一行一个词)作为动态热词送入,paraformer/CTC 不受影响;bpe 类模型缺词表时自动禁用热词,避免原生退出。
- 语音 VAD:模型包可选带 silero_vad.onnx(角色 vad),逐块检测语音活动辅助端点判断;没有 VAD 的路径与之前完全一致。
- 拼音引擎支持模糊音:按启用规则展开(z/zh、c/ch、s/sh、n/l、f/h、r/l、an/ang、en/eng、in/ing、ian/iang、uan/uang),模糊候选带权重惩罚混排;模糊音开关下 26 键也走自研引擎。
- 手写:点选候选上屏后落笔区自动清空,可直接写下一个字;没认出候选时给出提示而不是空白。
- 浮动键盘按方向记忆位置:横屏、竖屏各自保留上次拖动到的位置,旋转后回到本方向的位置。
- 语音模型导入扩展:支持 zip / tar / tar.gz / tgz 压缩包;流式模型族扩展到 transducer(zipformer / zipformer2 / conformer / lstm / ebranchformer)、paraformer、zipformer2-ctc、nemo-ctc、t-one-ctc;支持包内 bpe.vocab(配合 manifest 的 modelingUnit)、transducer 热词表(自动切 modified_beam_search)和标点模型;单文件 CTC 仍必须用 manifest.json 明确类型。
- 自研拼音引擎(clean-room):全路径音节格子 + 209 万词二进制索引 + 50 万二元表 + Viterbi 整句;26 键拼音的候选与整句由它提供(实测:候选 top1 12/12、整句 top1 14/14),Rime 退居补充与兜底(简拼、用户词学习)。引擎数据由本地脚本编译,不随仓库分发。
- 手写入口补充到「切换键盘」面板(与五笔同列),工具面板入口保留。
- 新增「五笔」键盘:26 键界面按字根编码输入,候选与前缀提示来自 Rime 方案 `wubi86`。词表由本地数据构建,不随仓库分发;没有词表的构建不显示这个键盘。
- 新增「手写输入」面板:手写区落笔按基本笔画(横竖撇捺折)识别并给出候选,支持撤销与清空。识别表同样来自本地数据,没有它时保持隐藏。
- 英文 26 键的候选补全接入本地英语词表(按词频排名,最多取 2.6 万词);没有词表时保持原来的小词表行为。
- 词库导入脚本 `scripts/import_doubao_data.py`:把本地解码词表合并进 Rime 词典与九键快速词表(只读外部数据,不提交词表本身)。
- 构建脚本 `build_rime_prebuilt.py`:本地存在五笔词表时自动编译并校验 `wubi86` 表;`default.yaml` 注册 `wubi86` 方案。

## [0.0.9-beta.1] - 2026-10-10

第九个测试版:横屏默认改回铺满宽度的停靠键盘,竖屏和横屏各记一份键盘高度,横屏左右分离的九键改成“一手数字、一手拼音”。从 0.0.8-beta.1 覆盖安装即可,用户数据保留。
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.9-beta.1
0.0.10-beta.1
15 changes: 14 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ val rimeCompiledOnlySources = listOf(
"base.dict.yaml",
"ext.dict.yaml",
"others.dict.yaml",
"wubi86.dict.yaml",
"essay.txt",
)

/** -Popenime.lite=true 或环境变量 OPENIME_LITE=1:构建不含语音模型的精简包。 */
val liteBuild: Boolean =
(providers.gradleProperty("openime.lite").orNull ?: providers.environmentVariable("OPENIME_LITE").orNull)
.let { it == "true" || it == "1" }

val releaseSigningReady = listOf(
releaseKeystorePath,
releaseKeystorePassword,
Expand Down Expand Up @@ -125,7 +131,14 @@ android {
// first entries are aapt's defaults, which this list replaces.
ignoreAssetsPatterns += listOf(
"!.svn", "!.git", "!.ds_store", "!*.scc", ".*", "<dir>_*", "!CVS", "!thumbs.db", "!picasa.ini", "!*~",
) + rimeCompiledOnlySources.map { "!$it" }
) + rimeCompiledOnlySources.map { "!$it" } + if (liteBuild) {
// 精简包:不带语音模型。用户在「设置 → 语音模型」里导入自己的模型。
// 只排除两个模型目录;models/voice/manifest.json 与受信目录仍然保留,
// 这样应用知道"没有内置模型"而不是"安装包损坏"。
listOf("!bilingual-paraformer", "!punctuation")
} else {
emptyList()
}
}

compileOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ class DisplayEnvironmentInstrumentedTest {

@Test
fun landscapeContentBoxKeepsEveryRowInsideIt() {
// Landscape fills the whole width (padding 0), like Gboard and the Chinese keyboards.
// Landscape insets the key area by the reference skin's fractions: 6.485%
// per side for the 26-key and digit surfaces, 9.5% for the nine-key
// (ReferenceLandscapeInsets). The window stays full width and the
// background spans it; only the content moves inward.
// Rows used to keep the width they were measured with before the padding
// was applied: as wide as the window, clipped to the box.
for (mode in listOf(KeyboardMode.PINYIN_26, KeyboardMode.ENGLISH_26, KeyboardMode.PINYIN_9, KeyboardMode.DIGITS)) {
Expand All @@ -109,7 +112,19 @@ class DisplayEnvironmentInstrumentedTest {
val body = keyboard.findViewWithTag<ViewGroup>("keyboard-body")
assertNotNull("keyboard body", body)
val content = body.width - body.paddingLeft - body.paddingRight
assertEquals("$mode landscape fills the full width", 0, body.paddingLeft)
val fraction = if (mode == KeyboardMode.PINYIN_9) 0.095f else 0.0648508f
val expectedInset = (keyboard.width * fraction).toInt()
assertEquals(
"$mode landscape insets the key area on the reference fraction",
expectedInset.toFloat(),
body.paddingLeft.toFloat(),
1f,
)
assertEquals(
"$mode landscape insets both sides equally",
body.paddingLeft,
body.paddingRight,
)
for (index in 0 until body.childCount) {
val row = body.getChildAt(index)
assertTrue(
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@
android:exported="false"
android:label="@string/activity_label_about" />

<activity
android:name=".VoiceModelActivity"
android:exported="false"
android:label="@string/activity_label_voice_model" />

<activity
android:name=".DataManagementActivity"
android:exported="false"
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/assets/rime-data/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ schema_list:
- schema: cangjie5
- schema: stroke
- schema: terra_pinyin
- schema: wubi86
- schema: double_pinyin_flypy
- schema: double_pinyin

switcher:
caption: 〔方案選單〕
Expand Down
108 changes: 108 additions & 0 deletions app/src/main/assets/rime-data/double_pinyin.schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Rime schema
# encoding: utf-8
#
# 双拼(自然码键位)。两键一字,词库与拼音 26 键共用 luna_pinyin。
# 键位映射整理自 rime-double-pinyin(佛振等,GPL-3.0),与雾凇拼音的 double_pinyin 一致;
# openIME 按参考键盘的「双拼键盘」补齐此方案,不含 Lua 与外部依赖。

__include: luna_pinyin.schema:/

schema:
schema_id: double_pinyin
name: 自然码双拼
version: "1"
author:
- openIME
description: |
双拼·自然码键位。26 键键盘上两键一字,词库与拼音 26 键相同。

switches:
- name: ascii_mode
reset: 0
states: [ 中文, 西文 ]
- name: full_shape
states: [ 半角, 全角 ]
- name: zh_simp
reset: 1
states: [ 漢字, 汉字 ]
- name: ascii_punct
states: [ 。,, ., ]

speller:
alphabet: zyxwvutsrqponmlkjihgfedcba
initials: zyxwvutsrqponmlkjihgfedcba
delimiter: " '"
algebra:
- erase/^xx$/
- derive/^([jqxy])u$/$1v/
- derive/^([aoe])([ioun])$/$1$1$2/
- xform/^([aoe])(ng)?$/$1$1$2/
- xform/iu$/Ⓠ/
- xform/[iu]a$/Ⓦ/
- xform/[uv]an$/Ⓡ/
- xform/[uv]e$/Ⓣ/
- xform/ing$|uai$/Ⓨ/
- xform/^sh/Ⓤ/
- xform/^ch/Ⓘ/
- xform/^zh/Ⓥ/
- xform/uo$/Ⓞ/
- xform/[uv]n$/Ⓟ/
- xform/(.)i?ong$/$1Ⓢ/
- xform/[iu]ang$/Ⓓ/
- xform/(.)en$/$1Ⓕ/
- xform/(.)eng$/$1Ⓖ/
- xform/(.)ang$/$1Ⓗ/
- xform/ian$/Ⓜ/
- xform/(.)an$/$1Ⓙ/
- xform/iao$/Ⓒ/
- xform/(.)ao$/$1Ⓚ/
- xform/(.)ai$/$1Ⓛ/
- xform/(.)ei$/$1Ⓩ/
- xform/ie$/Ⓧ/
- xform/ui$/Ⓥ/
- xform/(.)ou$/$1Ⓑ/
- xform/in$/Ⓝ/
- xlit/ⓆⓌⓇⓉⓎⓊⒾⓄⓅⓈⒹⒻⒼⒽⓂⒿⒸⓀⓁⓏⓍⓋⒷⓃ/qwrtyuiopsdfghmjcklzxvbn/

translator:
prism: double_pinyin
preedit_format:
- xform/(^|[ '])aa/$1a/
- xform/(^|[ '])ee/$1e/
- xform/(^|[ '])oo/$1o/
- xform/([bpmnljqxy])n/$1in/
- xform/(\w)g/$1eng/
- xform/(\w)q/$1iu/
- xform/([gkhvuirzcs])w/$1ua/
- xform/(\w)w/$1ia/
- xform/([dtnlgkhjqxyvuirzcs])r/$1uan/
- xform/(\w)t/$1ve/
- xform/([gkhvuirzcs])y/$1uai/
- xform/(\w)y/$1ing/
- xform/([dtnlgkhvuirzcs])o/$1uo/
- xform/(\w)p/$1un/
- xform/([jqx])s/$1iong/
- xform/(\w)s/$1ong/
- xform/([jqxnlb])d/$1iang/
- xform/(\w)d/$1uang/
- xform/(\w)f/$1en/
- xform/(\w)h/$1ang/
- xform/(\w)j/$1an/
- xform/(\w)k/$1ao/
- xform/(\w)l/$1ai/
- xform/(\w)z/$1ei/
- xform/(\w)x/$1ie/
- xform/(\w)c/$1iao/
- xform/([dtgkhvuirzcs])v/$1ui/
- xform/(\w)b/$1ou/
- xform/(\w)m/$1ian/
- xform/([aoe])\1(\w)/$1$2/
- "xform/(^|[ '])v/$1zh/"
- "xform/(^|[ '])i/$1ch/"
- "xform/(^|[ '])u/$1sh/"
- xform/([jqxy])v/$1u/
- xform/([nl])v/$1ü/
- xform/ü/v/

simplifier:
option_name: zh_simp
108 changes: 108 additions & 0 deletions app/src/main/assets/rime-data/double_pinyin_flypy.schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Rime schema
# encoding: utf-8
#
# 双拼(小鹤键位)。两键一字,词库与拼音 26 键共用 luna_pinyin。
# 键位映射整理自 rime-double-pinyin(佛振等,GPL-3.0),与雾凇拼音的 double_pinyin_flypy 一致;
# openIME 按参考键盘的「双拼键盘」补齐此方案,不含 Lua 与外部依赖。

__include: luna_pinyin.schema:/

schema:
schema_id: double_pinyin_flypy
name: 小鹤双拼
version: "1"
author:
- openIME
description: |
双拼·小鹤键位。26 键键盘上两键一字,词库与拼音 26 键相同。

switches:
- name: ascii_mode
reset: 0
states: [ 中文, 西文 ]
- name: full_shape
states: [ 半角, 全角 ]
- name: zh_simp
reset: 1
states: [ 漢字, 汉字 ]
- name: ascii_punct
states: [ 。,, ., ]

speller:
alphabet: zyxwvutsrqponmlkjihgfedcba
initials: zyxwvutsrqponmlkjihgfedcba
delimiter: " '"
algebra:
- erase/^xx$/
- derive/^([jqxy])u$/$1v/
- derive/^([aoe])([ioun])$/$1$1$2/
- xform/^([aoe])(ng)?$/$1$1$2/
- xform/iu$/Ⓠ/
- xform/(.)ei$/$1Ⓦ/
- xform/uan$/Ⓡ/
- xform/[uv]e$/Ⓣ/
- xform/un$/Ⓨ/
- xform/^sh/Ⓤ/
- xform/^ch/Ⓘ/
- xform/^zh/Ⓥ/
- xform/uo$/Ⓞ/
- xform/ie$/Ⓟ/
- xform/(.)i?ong$/$1Ⓢ/
- xform/ing$|uai$/Ⓚ/
- xform/(.)ai$/$1Ⓓ/
- xform/(.)en$/$1Ⓕ/
- xform/(.)eng$/$1Ⓖ/
- xform/[iu]ang$/Ⓛ/
- xform/(.)ang$/$1Ⓗ/
- xform/ian$/Ⓜ/
- xform/(.)an$/$1Ⓙ/
- xform/(.)ou$/$1Ⓩ/
- xform/[iu]a$/Ⓧ/
- xform/iao$/Ⓝ/
- xform/(.)ao$/$1Ⓒ/
- xform/ui$/Ⓥ/
- xform/in$/Ⓑ/
- xlit/ⓆⓌⓇⓉⓎⓊⒾⓄⓅⓈⒹⒻⒼⒽⒿⓀⓁⓏⓍⒸⓋⒷⓃⓂ/qwrtyuiopsdfghjklzxcvbnm/

translator:
prism: double_pinyin_flypy
preedit_format:
- xform/(^|[ '])aa/$1a/
- xform/(^|[ '])ee/$1e/
- xform/(^|[ '])oo/$1o/
- xform/([bpmfdtnljqx])n/$1iao/
- xform/(\w)g/$1eng/
- xform/(\w)q/$1iu/
- xform/(\w)w/$1ei/
- xform/([dtnlgkhjqxyvuirzcs])r/$1uan/
- xform/(\w)t/$1ve/
- xform/(\w)y/$1un/
- xform/([dtnlgkhvuirzcs])o/$1uo/
- xform/(\w)p/$1ie/
- xform/([jqx])s/$1iong/
- xform/(\w)s/$1ong/
- xform/(\w)d/$1ai/
- xform/(\w)f/$1en/
- xform/(\w)h/$1ang/
- xform/(\w)j/$1an/
- xform/([gkhvuirzcs])k/$1uai/
- xform/(\w)k/$1ing/
- xform/([jqxnlb])l/$1iang/
- xform/(\w)l/$1uang/
- xform/(\w)z/$1ou/
- xform/([gkhvuirzcs])x/$1ua/
- xform/(\w)x/$1ia/
- xform/(\w)c/$1ao/
- xform/([dtgkhvuirzcs])v/$1ui/
- xform/(\w)b/$1in/
- xform/(\w)m/$1ian/
- xform/([aoe])\1(\w)/$1$2/
- "xform/(^|[ '])v/$1zh/"
- "xform/(^|[ '])i/$1ch/"
- "xform/(^|[ '])u/$1sh/"
- xform/([jqxy])v/$1u/
- xform/([nl])v/$1ü/
- xform/ü/v/

simplifier:
option_name: zh_simp
14 changes: 14 additions & 0 deletions app/src/main/assets/rime-data/wubi86.dict.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Rime dictionary: wubi86 (root table)
# encoding: utf-8
#
# librime resolves the schema's `dictionary: wubi86` to this file; the actual
# entries live in openime_dicts/wubi86.dict.yaml, which is generated locally
# (scripts/import_doubao_data.py) and is not part of the repository.

---
name: wubi86
version: "1"
sort: by_weight
import_tables:
- openime_dicts/wubi86
...
Loading
Loading