一个按 首字母 A–Z 自动分组 的英语生词本 Android App。记录不会的单词,填写 词性 / 中英文释义 / 例句 / 标签 / 笔记,可以用 自己配置的 AI 接口 一键查询补全,也可以直接和 AI 聊单词、句子、语法,最后用记忆卡片和测验把它们背下来。
- 现代 UI:Material 3(Material You 动态取色)+ Jetpack Compose,大圆角、大字重的 Expressive 风格
- 适配 Android 16 (API 36) 与 Android 17 (API 37):
compileSdk 37/targetSdk 37,全屏 edge-to-edge,预测返回手势,自适应布局 - 自带 AI,但接口由你自己提供:支持 OpenAI 兼容(OpenAI / DeepSeek / 通义 / Kimi / Ollama / LM Studio…)、Anthropic Claude、Google Gemini,可自定义地址、密钥、模型、温度、超时和系统提示词
- 本地优先:单词、分组、聊天都保存在设备上,可导出 JSON / CSV / 纯文本备份
- 244 个单元测试,lint 0 项
仓库地址:https://github.com/RokishiSensei/roxy-notebook
git clone https://github.com/RokishiSensei/roxy-notebook.git
| 模块 | 能力 |
|---|---|
| 四级词库 | 设置里新增「添加四级英语单词」:把内置的 331 个四级核心词汇一键加入「🎓 四级核心」分组,已存在的自动跳过,可反复点补齐;词库是 assets/wordpacks/cet4-core.json,纯 JSON 想加词直接改文件 |
| 重复规则 | 同一分组内不允许重复单词,不同分组之间允许同一个单词(例如 bank 可以同时在「四级核心」和「阅读生词」);再次导入同组同词会合并义项;删除分组只删除「只属于该组」的词条,不会把重复词搬到未分组;复制出的词条从零开始学习(不继承星级/掌握度/复习次数),原词条不受影响 |
| 复制词条 | 多选后新增「复制到分组」;「移动分组」弹窗里也有「同时复制一份到…」,可以把同一个词放进两个分组 |
| 自定义背景 | 设置 → 外观:主页背景和干净浏览背景是两套独立设置,各自可选 6 种底色 + 从相册选一张图片(图片会复制进应用内部,原图删掉也不影响)、各自用 背景浓度 0–100 滑杆 + 5 个快捷档位微调,点击 「保存」按钮 立即落盘并给出提示,还能一键「复制另一套」;顶部有总开关。浓度会同时驱动背景层自身 alpha 和 Material Surface 的半透明程度(100% 时 surface 降到 0.28,图片真正透出来);所有背景设置都用 commit() 同步写盘 |
| 干净浏览 | 选中某个分组后,列表上方出现一整条 「📖 干净浏览『分组名』」按钮(显示词条数),点它进入干净浏览:只剩单词、音标、词性和释义的纯净排版。所有控件都在底部工具栏(可横向滑动,4 秒自动隐藏、点任意处唤回):🔍 分组内搜索(匹配单词/音标/中英释义/标签/笔记)、右侧 A–Z 索引条(和主页一致:点字母直接滚动跳到该首字母的词,不筛选、不改变列表)、↩ 续读(跳回上次读到的那一条,按分组分别记忆)、⇅ 排序(A–Z / 最新在前 / 最早在前)、字号 80%–180%、🎨 背景快捷入口、✕ 退出。排序方式、字号、阅读位置都会记住。干净浏览是二级页面,按系统返回键即返回生词本 |
| 生词本 | 列表始终按首字母 A–Z 分组(顶部切换行已移除,右侧保留 A–Z 索引条 + 工具栏字母跳转弹窗);分组和日期都作为筛选条件叠加在字母分组之上;搜索(单词 / 音标 / 中英释义 / 标签 / 笔记 / 来源);筛选(重点词 ⭐、新词 / 学习中 / 熟悉 / 已掌握);点词条编辑、长按进入多选 |
| 词条排版 | 单词行分三层显示:① 单词 + 音标(朗读 / ⭐ 按钮固定宽度,所有行严格对齐);② 词性徽章 + 完整中文释义(最多 4 行,不再截断) + 右侧添加日期时间;③ 英文释义;④ 掌握度、分组、标签横向滑动条;⑤ 笔记预览。另提供「紧凑列表」模式 |
| AI 批量导入 | 生词本右下角「添加生词」上方新增 ✨ 按钮:粘贴或选择任意格式的单词文件(txt / csv / md / json,甚至直接复制别家的词表),交给 AI 整理成 App 格式;先预览再入库,可逐条勾选、识别重复词、显示分组与标签,确认后才写入 |
| 日历查看 | 工具栏新增 📅 按钮:弹出月历,有词条的日期高亮并显示条数,点某天只显示当天添加的单词(仍按 A–Z 排列),再点一次或点横幅 ✕ 取消;下方还有「最近有记录的日期」快捷列表 |
| 自定义分组 | 新建 / 重命名 / 删除分组,可选 12 种图标 + 6 种颜色;分组芯片带词条计数;「未分组」自动归类;删除分组不会删词条(回到未分组);设置页与统计页都有分组分布 |
| 多选批量操作 | 点标题栏的 ✓ 图标或长按任意词条进入多选,支持全选 / 取消全选,可批量删除(带二次确认)、标记 / 取消重点、移动分组 |
| 添加 / 编辑 | 英文单词或词组、音标、多词性多释义(16 种词性)、中文释义、英文释义、例句、来源、标签、笔记、掌握度、分组;也可在编辑页里用 AI 查询 一键补全 |
| AI 查词 | 在编辑页输入英文 → 点「AI 查询」→ AI 返回音标、全部词性义项(多义词会列全)、英文释义、例句、易错提示、标签,自动填入表单(不覆盖你已填的内容);返回 JSON 解析失败时会退回宽松文本解析,也可以查看模型原始回复 |
| AI 问答 | 底部新增「AI 问答」标签页:像聊天一样问单词用法、近义词区别、句子语法、让它改英文表达;支持快捷提问、多轮上下文、「正在思考」打字动画、一键复制回复 |
| 多会话聊天 | 可新建多个对话(自动用第一句话命名),左上角抽屉查看历史对话列表(轮数 + 相对时间 + 摘要),支持切换、重命名、单条删除、清空当前对话;设置里可导出全部对话为纯文本 |
| 复习 | 记忆卡片(正面可设为英文或中文);「查看释义 → 忘了 / 记住了」间隔重复调度;今天该背 / 只背重点 / 全部词随机抽背 / 今天新增的词 / 按分组随机抽背五种入口;今日目标双环进度 |
| 测验 | 独立的测验设置页:出题范围(全部 / 待复习 / 重点词 / 今天新增 / 指定分组)× 题型(中译英选择 / 英译中选择 / 看中文拼写 / 混合出题)× 题目数量(4–50);答题即时反馈并显示例句;结果页有正确率、逐题明细(含你答错的内容)、一键「重练错题」 |
| 发音 | 系统 TTS 朗读(支持 0.6x 慢速),列表、卡片、编辑器里都能点 |
| 设置 | 主题、Material You 取色、紧凑列表、每日目标、卡片方向、自动朗读、触感反馈、排序、AI 接口配置与连接测试、JSON / CSV 导入导出、AI 对话导出、清空数据 |
| 自适应 | 手机底部导航栏(4 个标签:生词本 / 复习 / AI 问答 / 设置);≥600dp(平板 / 折叠屏展开 / 横屏)自动切换为侧边 NavigationRail |
| 单元测试 | 106 个纯 JVM 测试:字母分组、排序、草稿校验、词性映射、CSV 读写、日期区间、日历(月份网格对齐 / 跨年翻月 / 按天分组 / 添加时间文案 / 分组字母序)、分组去重规则(同词跨组 / 同组拒绝 / 复制 / 移动并复制 / 删除分组 / 旧数据清理 / 四级词库解析)、两套背景配置的独立性与持久化、透明度换算、干净浏览排序/搜索、A–Z 索引跳转定位、阅读位置持久化、分组哨兵映射(未分组筛选)、AI JSON 解析(含代码块 / 花括号嵌套 / 非 JSON 兜底)、AI 结果合并、测验出题与选项唯一性、错题重练、聊天会话模型、导入格式(Robolectric 跑真实 SharedPreferences:Tab/竖线/单列、带/不带表头、括号英文释义与例句、重复合并、导出再导入)、批量导入解析(JSON 各种形状 + 逐行兜底) |
打开 设置 → AI 学习助手 → AI 已连接 / 未配置(或底部「AI 问答」标签页右上角齿轮)。
| 字段 | 说明 |
|---|---|
| 接口类型 | OPENAI(OpenAI 兼容,最常用)、ANTHROPIC(Claude)、GEMINI(Google) |
| API 地址 | 可填完整地址(https://api.deepseek.com/v1/chat/completions),也可只填到 /v1,App 会自动补全路径 |
| API Key | 本地保存,导出数据时不会包含;本地模型可留空 |
| 模型名称 | 手填,或点「获取在线模型」让 App 请求接口并列出该 Key 可用的全部模型,在弹窗里搜索、点选即可 |
| 温度 / 最大长度 / 超时 | 查词建议温度 0.2–0.4;超时默认 60 秒 |
| 提示词 | 查词 / 聊天 / 批量导入三套提示词都可编辑,可一键恢复默认 |
设置页顶部是配置档列表,可以保存多套接口并一键切换:
- 「+ 新增」创建空白配置;点任意一条即切换为当前使用(标「使用中」)
- 每条支持重命名、复制(做变体时不会丢原配置)、删除(至少保留一条)
- 左侧圆点表示是否已填好地址(绿色 = 可用)
- 典型用法:一套便宜的模型专门查词,一套能力强的模型专门做批量导入,随时切换互不影响
点「获取在线模型」会请求接口并列出该 Key 可用的全部模型,弹窗里可以搜索过滤、标出当前使用的模型:
| 接口类型 | 请求 | 解析字段 |
|---|---|---|
| OpenAI 兼容 | GET {base}/v1/models |
data[].id |
| Anthropic | GET {base}/v1/models |
data[].id / name |
| Gemini | GET {base}/v1beta/models?pageSize=200 |
models[].name(自动去掉 models/ 前缀) |
结果排序去重后展示。某些自建网关没有实现 /models,此时会明确提示「该接口不支持列出模型(HTTP xxx)」,手动填模型名即可。
常用地址示例:
| 服务 | API 地址 | 模型 |
|---|---|---|
| OpenAI | https://api.openai.com/v1 |
gpt-4o-mini |
| DeepSeek | https://api.deepseek.com/v1 |
deepseek-chat |
| 月之暗面 Kimi | https://api.moonshot.cn/v1 |
moonshot-v1-8k |
| 阿里通义 | https://dashscope.aliyuncs.com/compatible-mode/v1 |
qwen-plus |
| 智谱 GLM | https://open.bigmodel.cn/api/paas/v4 |
glm-4-flash |
| Ollama(电脑本地) | http://192.168.x.x:11434/v1 |
qwen2.5:7b |
| LM Studio(电脑本地) | http://192.168.x.x:1234/v1 |
任意已加载模型 |
填好点「测试连接」,成功会提示模型回显内容,失败会给出 HTTP 状态码和原因。 国内直连 OpenAI 一般需要代理;Android 默认禁止明文
http://,局域网自建服务请自行配置网络安全白名单。 不配置 AI 时,AI 相关按钮会隐藏或引导去设置,其余功能完全离线可用。
已构建好的安装包:
| 文件 | 说明 |
|---|---|
dist/Roxy-1.16.3-debug.apk |
调试包,包名 com.lexi.wordbook.debug,可直接安装,与 release 可共存 |
dist/Roxy-1.16.3-release.apk |
正式包,包名 com.lexi.wordbook,已用 keystore/lexi-release.jks 签名,体积更小 |
手机上打开「设置 → 应用 → 特殊权限 → 安装未知应用」允许文件管理器安装,然后点开 APK 即可。用 adb 安装:
adb install -r dist/Roxy-1.16.3-release.apkTTS 朗读依赖系统的文字转语音引擎(多数国行 ROM 需要装「Google 语音服务」或厂商 TTS)。没有引擎时其余功能不受影响。
| 组件 | 版本 |
|---|---|
| JDK | 21(keytool/Gradle 用;JDK 17 亦可) |
| Android SDK Platform | API 37(platforms;android-37.0)+ API 36 可选 |
| Android SDK Build-Tools | 37.0.0 |
| Gradle | 9.8.1(工程内已带 gradlew) |
| Android Gradle Plugin | 9.4.1 |
| Kotlin | 2.4.21(Kotlin 支持由 AGP 内置) |
| Compose BOM | 2026.09.00 + material3 1.4.0 |
# 1) 指向本机 SDK(若用 Android Studio 打开则自动生成)
echo "sdk.dir=/path/to/Android/Sdk" > local.properties
# 2) 构建
./gradlew :app:assembleDebug # 产物 app/build/outputs/apk/debug/app-debug.apk
./gradlew :app:assembleRelease # 产物 app/build/outputs/apk/release/app-release.apk
./gradlew :app:testDebugUnitTest # 244 个单元测试
./gradlew :app:lintDebug # 静态检查(当前 0 项)
./gradlew :app:installDebug # 直接装到已连接设备Windows 上如果工程路径含中文,gradle.properties 里的
android.overridePathCheck=true 已经帮你关掉了 AGP 的路径检查。
内存配置:
gradle.properties里org.gradle.jvmargs=-Xmx3072m、org.gradle.workers.max=2、kotlin.daemon.jvmargs=-Xmx1536m是有意收紧的。 D8/R8 在 dex 阶段会 fork 多个 worker,每个都占独立堆;在 GitHub 的 4 核 / 16 GB runner 上 用默认的-Xmx4096m加不限并发会直接把:app:mergeExtDexDebug撑死 (java.lang.OutOfMemoryError: Java heap space)。本机内存宽裕的话可以调大,但别把workers.max去掉,否则 CI 会再次 OOM。
注意:
gradle/wrapper/gradle-wrapper.properties里的distributionUrl是官方地址 (services.gradle.org),clone 下来直接能构建,CI 也用它。 如果你在离线环境,可以改成指向本地已解压的 Gradle 9.8.1(文件里有注释示例), 记得同时把validateDistributionUrl设为false。
local.properties里的sdk.dir是本机路径、且已被.gitignore排除,Android Studio 会自动重写。
直接用 Android Studio 打开工程根目录即可(需要支持 AGP 9.4 的版本)。首次同步会下载依赖。
keystore/keystore.properties 保存 release 签名信息:
storeFile=keystore/lexi-release.jks
storePassword=lexi2026
keyAlias=lexi
keyPassword=lexi2026想换成自己的密钥:删掉这个文件并重新执行 assembleRelease(会自动回落到 debug 签名),
或用 keytool 生成新密钥后修改此文件。上架应用市场请务必换成自己的密钥并妥善保管。
app/src/main/java/com/lexi/wordbook/
├─ MainActivity.kt # 单 Activity,edge-to-edge,主题注入
├─ WordBookApp.kt # Application + 手写依赖容器(无 DI 框架)
├─ ai/
│ ├─ AiClient.kt # HttpURLConnection 实现的 OpenAI 兼容 / Claude / Gemini 客户端 + 模型列表查询
│ ├─ AiConfigRepository.kt # 接口地址、密钥、模型、温度、超时、系统提示词
│ ├─ AiWordParser.kt # 从模型回复里抠出 JSON 并解析,失败时宽松解析
│ └─ ChatRepository.kt # 多会话聊天记录持久化(含旧版单会话迁移)
├─ data/
│ ├─ Word.kt # Word / Sense / PartOfSpeech / LearningStatus / WordKind / WordDraft
│ ├─ WordGroup.kt # 自定义分组与 AI 返回结构(AiWordResult / AiSense)
│ ├─ WordRepository.kt # JSON 持久化、Upsert、A–Z 排序、分组、JSON/CSV 导入导出
│ └─ PreferencesRepository.kt # 主题、目标、开关等偏好
├─ speech/SpeechPlayer.kt # 系统 TTS 封装(含慢速朗读)
├─ util/
│ ├─ Alphabet.kt # A–Z 分组 / 索引
│ ├─ Csv.kt # 零依赖 RFC4180 CSV 读写
│ └─ Dates.kt # 今日判断、相对日期、7 天区间
└─ ui/
├─ MainScreen.kt # 自适应导航壳(5 标签 NavigationBar / NavigationRail)+ 页面栈
├─ MainViewModel.kt # 状态、筛选、视图模式、分组、批量操作、复习调度、AI 调用、导入导出
├─ ai/AiAssistant.kt # 查词 / 聊天编排、提示词组装、AI 结果合并进草稿
├─ theme/ # Material 3 配色(含 Material You)、字体、圆角
├─ components/DesignSystem.kt # 词性徽章、掌握度胶囊、双环进度、分布条、空状态…
└─ screens/
├─ LibraryScreen.kt # 列表四视图 + A–Z 索引轨 + 筛选 + 多选批量操作
├─ GroupUi.kt # 分组芯片、分组管理弹窗、分组选择器、移动到分组
├─ WordEditorScreen.kt # 添加 / 编辑(多词性多释义 + AI 查询卡片 + 分组选择)
├─ ChatScreen.kt # AI 问答:多会话 + 历史对话抽屉
├─ AiSettingsScreen.kt # AI 接口配置页 + 在线模型列表获取与选择
├─ ReviewScreen.kt # 复习首页 + 记忆卡片 + 结算
├─ QuizModels.kt # 测验出题引擎(范围 / 题型 / 选项生成 / 错题重练)
├─ QuizScreen.kt # 测验设置页 + 答题页 + 结果明细页
└─ SettingsScreen.kt # 设置 + 导入导出 + AI 状态卡
- 只依赖
HttpURLConnection+org.json,没有引入任何网络库,release 包 1.45 MB。 - 三种协议分别组包:OpenAI 用
messages+Authorization: Bearer;Claude 用system+messages+x-api-key+anthropic-version;Gemini 用systemInstruction+contents+x-goog-api-key。 - 解析回复时依次尝试
choices[0].message.content/content[].text/candidates[0].content.parts[].text,再退回output_text/response/result/text/answer/message,兼容自建网关。 - 查词时系统提示词要求模型只返回 JSON;解析器会去掉 ```json 代码块、跳过前后废话、按花括号配对(正确处理字符串内的
{})取出对象。解析失败时用正则抓音标 + `n. 释义;释义` 形式的行做宽松解析,保证再差也能用。 - 合并策略:AI 结果不覆盖你已经填过的单词拼写、标签和笔记,多义词会作为多条释义追加。
- 聊天只带最近 12 轮上下文,避免 token 爆炸。
compileSdk = 37,targetSdk = 37:以 Android 17 为目标,同时兼容 Android 16。- Edge-to-edge:调用
enableEdgeToEdge(),主题里系统栏透明;所有页面用Scaffold的 inset padding,内容不会被状态栏 / 手势条遮挡。 - 预测返回:
android:enableOnBackInvokedCallback="true",Compose 的返回处理走系统预测动画。 - 自适应 / 可调整大小:
android:resizeableActivity="true",不锁定方向;布局用BoxWithConstraints在 600dp 断点切换导航形态,符合 Android 17 对「忽略方向与可调整大小限制」的要求。 - 动态取色:Android 12+ 使用
dynamicLightColorScheme/dynamicDarkColorScheme(Material You),可关闭。 - 组件更新:Material 3 1.4.0 稳定版的
ProgressRing/LoadingIndicator/StatusPill等 Expressive 风格自绘组件。 - 所有 UI 文案都走
strings.xml(values/中文 +values-en/英文),可通过androidResources.localeFilters控制打包语言。 - 深色模式、字体缩放、RTL 均已处理;
minSdk 26(Android 8.0)覆盖 99% 以上设备。 - 主题按 API 分级:
values/themes.xml(26–28)、values-v27(刘海屏)、values-v29(关闭系统栏自动蒙层),避免在低版本引用不存在的属性。
一行 = 一个词条,推荐用 Tab 分隔;也认 |、| 和逗号(逗号会按 CSV 引号规则解析)。表头可写可不写。
word phonetic senses tags group note status
diet /ˈdaɪət/ n. 日常饮食;n. 规定饮食;v. 节食 健康 减肥 饮食 注意区分 die NEW
senses单元格用;分隔多个义项,每项写成「词性 释义」;英文释义放括号里,例句写例: …- 没有表头时按上面的列顺序解析;只有两列时按「单词 | 释义」处理
group里写分组名,不存在会自动创建- 再次导入同一个单词会合并义项而不是产生重复条目
- 想快速记几个词,一行
word|释义就够 - 设置页可以「导出导入模板」,拿到标准表头照着填
适合把别的软件/文件里的词表搬进来:
- 点「添加生词」上方的 ✨ 按钮
- 粘贴剪贴板或选择文件(txt / csv / md / json 都能读)
- 点「交给 AI 整理」——内容多时会自动分段发送(每段约 40 行),带进度显示
- AI 返回后进入预览页:逐条显示单词、音标、全部义项、分组、标签,可以勾选/取消,自动标出词库里已有的「已存在」条目
- 「跳过重复」开关默认开启;确认后点「导入生词本」才真正写入
没有配置 AI 时按钮会引导去设置;AI 返回非 JSON 时会自动退回逐行解析(支持 1. diet /ˈdaɪət/ n. 日常饮食、word|n.|释义、word 释义、- word v. 释义 等写法),所以即使模型不听话也能用。整理提示词可以在界面里直接改。
导出的 JSON 就是 Word 列表:
[
{
"id": "…uuid…",
"text": "serendipity",
"phonetic": "/ˌserənˈdɪpəti/",
"senses": [
{ "partOfSpeech": "NOUN", "meaningZh": "机缘巧合", "meaningEn": "…", "example": "…" }
],
"status": "NEW",
"starred": false,
"source": "阅读",
"tags": ["雅思"],
"note": "",
"kind": "WORD",
"groupId": "…分组 id 或 null…",
"createdAt": 1760000000000,
"updatedAt": 1760000000000,
"reviewCount": 0,
"nextReviewAt": 1760000000000,
"lastReviewedAt": 0
}
]CSV/Tab 表头:word, phonetic, senses, tags, group, note, status
(一行一个词;senses 用「;」分隔多个义项,格式见上一节。)
数据存放位置(App 私有目录,卸载即丢失,建议定期导出备份):
| 文件 | 内容 |
|---|---|
shared_prefs/lexi_wordbook.xml |
词条 JSON + 分组 JSON |
shared_prefs/lexi_settings.xml |
主题、目标、开关 |
shared_prefs/lexi_ai.xml |
AI 配置档(可多套,含密钥,不参与导出) |
shared_prefs/lexi_chat.xml |
AI 对话记录(多个会话,每个最多保留 200 条) |
- 1.16.3 — 修复二级页切换分组时左上角标题不变:分组芯片原来直接调
onFilterChange,只换筛选、不更新决定标题的libraryLocation,于是列表换了、标题还停在上一个分组。现在芯片走与书架卡片同一条shelfDestination路由(新增onSelectGroup回调),标题随分组更新;切换分组时保留当前搜索词(同层移动),从书架进入则清空。另做了一轮代码清理:删掉 4 个从未被读取的LibraryScreen/BookshelfScreen参数(view、todayCount、todayWordIds、cet4Count,占用内存且误导读者以为有作用)、清理 15 处未使用的 import、修正设置页一处${'$'}{331}的冗余转义,并把openGroup/leaveGroup从 lambda 改为局部函数以便使用具名参数。 - 1.16.2 — 彻底修复「未分组」进不去且标题显示别的分组。根因:
null同时承担了「全部词条 = 不加分组筛选」和「未分组 = groupId 为 null」两个含义,于是点「未分组」时openGroupId为 null,而一级/二级的判断条件正是bookshelfGroup == null && !searchMode—— null 又等于「还没进任何分组」,所以书架被再次渲染,同时筛选仍停在未分组:界面看起来进了二级页(芯片高亮未分组、标题却是别的分组名),实际显示的是书架。现在改为密封类型LibraryLocation(Shelf / AllWords / Ungrouped / Group(id))表达四个互斥状态,不再用可空值表达多个意思;并把「卡片 → 目的地」「目的地 → 分组筛选」抽成可测试的纯函数shelfDestination/shelfFilterGroupId,新增 9 个单元测试钉住这三种路由(其中一条专门断言「全部词条与未分组不是同一个目的地」)。 - 1.16.1 — 书架两项修复与增强:① 新增书架搜索框(网格首行,按分组名 / emoji 过滤卡片),副标题实时变为「找到 N 个分组」,无匹配时提示并给「清除搜索」;顶栏原有的搜索图标仍是按单词内容搜索全部词条,两者互补。② 修复「未分组」进不去:
openGroup(null)原来把 null 当成「全部词条」的同义词,而「未分组」卡正是传 null,于是点它等于点「全部词条」;现在用独立哨兵ENTERED_ALL表示「全部词条」,null 只表示未分组,顶栏标题也据此区分(📚 全部词条 / 🗂️ 未分组)。③ 空分组不再是白屏:进入没有词条的分组会显示「「X」还没有词条」并给「添加生词」按钮 —— 之前白屏让人以为分组打不开。④ 分组页副标题改为显示该分组自己的词条数(原来显示全库总数,未分组 0 个词会写成「共 72 个词条」)。 - 1.16.0 — 生词本改为两级结构:① 一级页 = 我的书架(
BookshelfScreen),自适应网格卡片列出「全部词条」「未分组」和每个分组,显示 emoji、名称与词条数,空分组标注「空分组」;顶栏有搜索全部词条、管理分组、清理重复;右上角 📚 与分组数概览;书架为空时给出来源引导(添加四级词汇 / AI 批量导入)。② 二级页 = 分组详情(原LibraryScreen,仍是搜索 + 分组芯片 + 筛选 + A–Z 索引 + 多选),进入时自动把筛选切到该分组,顶栏标题显示分组名,左上角新增 ← 返回书架按钮,系统返回键/手势同样返回书架(多选模式下暂停拦截,先退出多选)。③ 从书架点「管理分组」会进入分组页并直接弹出分组管理弹窗,返回即回书架。 - 1.15.1 — 修复
average,regardless,miles,thinner这类输入仍失败(三个连带问题):① 「疑似密钥」误判:looksLikeSecret把「一行逗号分隔的长文本」当成 API Key(全角逗号不是 ASCII 空白,36 字符超过阈值),于是整段被替换成__SECRET__占位符,真正的单词表根本没发给 AI;现在先识别sk-/Bearer等明确前缀,再看能否拆成 2 个以上词形片段(是则为词表),单个长 token 还要求同时含字母+数字/符号两类字符,纯字母的长单词不再误判。② 占位符被当成单词:模型把__SECRET__原样返回,解析器就当成词条列进预览(截图里的__SECRET__+ 「无法识别的词条」);新增isPlausibleHeadword校验,占位符、URL、纯符号、超长串、以及模型自我解释的长中文句子一律丢弃。③ 「新增」标记误报:headwordsOf也按整行处理,导致一行多词时每个词都被标成「✨新增」;改为先拆词单位再收集。 - 1.15.0 — 修复 AI 整理只认「一行一个单词」:粘贴
apple, banana, cherry这种一行多个词时只识别出一个。三个原因:① 提示词只说「每一行产出一个元素」,没有说明一行可能有多个词;② 分块/截断检测按行数估算期望词数,一行 3 个词时期望值是 1,模型返回 3 反而被判为「答多了」,而真正的截断又检测不出来;③ 分块按行切,一行 20 个逗号分隔的词只当 1 行发出去,超出输出预算就截断。现在:提示词新增规则 10(明确列出,,;;|/、Tab 等分隔符,要求每个词单独产出,但复合词组按规则 6 整条保留);分块与截断检测改为按词单位计数;支持1.2)3、等列表序号自动剥离。不含分隔符的行整体保留,避免把thickening agent拆碎。 - 1.14.0 — 「清理重复词条」升级为独立页面并加主页入口:生词本标题栏新增 ✨ 图标,有重复时显示红色数字角标,点进去直接扫描全库。页面分三步 —— ① 选扫描范围(全部词条 / 未分组 / 任意分组,芯片切换即时重扫)并列出每个重复词的逐条详情(释义、音标、状态、⭐);② 两个选择:直接清理(自动合并:释义/标签取并集、保留创建时间最早的那条)或我自己选保留哪一条;③ 手动模式里每个重复词用单选按钮指定保留哪条,显示「加入时间」帮助判断,底部实时显示「将删除 N 条,保留 M 条」,可一键重置为自动选择。设置页的同名入口改为选择范围后跳转到该页面,合并逻辑只保留一份实现。
- 1.13.0 — 新增:添加时的组内查重与**「清理重复词条」工具**。① 无论是手动添加还是 AI 补全后保存,若同一个词已在该分组内存在,则不写入并提示「已经在 X 里了」;同一词存在于别的分组时允许添加,但会提示「已存在于 Y,这里再添加一份」。编辑已有词条(同 id)不受影响。② 设置 → 数据管理新增「清理重复词条」:弹窗里可选全部词条或某一个分组(每项后面显示该范围内的重复条数),确认后合并重复项 —— 保留创建时间最早的那条(列表顺序不跳),释义/标签/音标取并集,复习进度取最靠前的(状态、复习次数、⭐)。
- 1.12.2 — 修复重复单词仍会导入(三层原因):① 仓库层
upsert只按 id 匹配,而批量导入给每条都生成新 UUID,于是同一个词总能插进第二行 —— 现在同时按 (词头, 分组) 匹配,命中则写到已有条目的 id 上(覆盖而不是追加);upsertAll同样按该键合并义项与标签。② 助手层去重键太弱:原来distinctBy { rawWord.lowercase() }忽略分组,且只对「已入库的词」判重、不查批次内部 —— 截断重试时同一行被问了两次就会出现两行;现在按词头@分组在收集时合并。③ 预览层新增「有 N 个词被模型重复返回,已自动合并为一条」提示。 - 1.12.1 — 修复备份还原(两个独立 bug):① JSON 备份只导出 words、不导出 groups,还原到空 App 后每个词的 groupId 指向不存在的分组 —— 现在备份格式改为 {version, exportedAt, groups, words},导入时按分组名合并(同名分组复用而不是新建)并把备份里的 id 重映射到真实 id;旧格式的纯数组备份依然能导入,指向缺失分组的词会退回未分组而不是变成孤儿。② 导入格式用文件名判断,但系统文件选择器给的是没有扩展名的 URI,于是 CSV 备份被当成 JSON 解析、导入 0 条 —— 现在按内容判断(以 { 或 [ 开头才算 JSON)。
- 1.12.0 — 新增:词形变化自动补原形。提示词新增规则 8/9:输入是过去式 / 过去分词 / 现在分词 / 复数 / 比较级 / 第三人称单数 / -ing 时,除了你输入的这个词,AI 再额外输出一条词典原形( an → 保留 an + 补 un;�pples → 保留 �pples + 补 �pple),且只做词形还原、不联想同义词或派生词。预览页会把这些补充条目标上 ✨ 新增 徽章,顶部多一条「AI 另外补了 N 个原形词条」提示,旁边 「保存新增」开关可一键全部不保存(单条也能取消勾选)。区分依据是把模型返回的词头与你粘贴的原文做规范化比对(忽略大小写、序号、「1. 」「-」等前缀与包裹标点,且长行也能取出行首词头)。
- 1.11.0 — 新增:生词本一键回到顶部按钮(滚动超过 2 项后淡入出现在右下角悬浮按钮组里,点一下回到顶部与 A–Z 表头;超过 40 项时直接跳转不再做长动画);App 更名为 Roxy notebook(应用名 / 关于页 / AI 会话名 / 导出邮件标题同步更新;内部包名 com.lexi.wordbook 与存储键保持不变,覆盖安装不会丢数据)。
- 1.10.1 — 修复:分组管理里看不到复制按钮(1.10.0 加了 onCopyToGroup 参数但 LibraryScreen 调用时没传,按钮回调是空实现 —— 现在已接通);横屏顶部功能区顶掉列表(搜索框 / 分组芯片 / 干净浏览入口 / 筛选芯片原来放在不可滚动的 Column 里,横屏时高度被吃光,单词列表被压成 0 高完全看不见 —— 现在这些控件全部并入同一个 LazyColumn,与单词一起滚动,任何屏幕高度都能看到列表;同时修正因此偏移的 A–Z 索引跳转下标);横屏/竖屏安全区(enableEdgeToEdge 下 NavigationRail 与底部导航栏贴到屏幕物理边缘,横屏时点不到 —— 现在外壳加 WindowInsets.safeDrawing);A–Z 索引条与悬浮按钮重叠(索引条上移让出右下角按钮区)。
- 1.10.0 — 新增:AI 批量导入预览页可选择导入分组(预览页顶部新增「导入到分组」一行,点开可选任意已有分组 / 未分组 / 新建分组,也可以一键「按 AI 建议」还原;选择会覆盖模型给的每行分组);分组管理新增「复制单词到别的分组」(每个分组右侧的复制按钮 → 选目标分组,把该组全部词条复制过去,原组保留;重复词头自动跳过,同一分组内不会重复);复制出的词条从零开始(星级 / 掌握度 / 复习次数不继承,只带词典内容)。
- 1.9.3 — 修复:「交给 AI 整理」报 整理失败:stack size 8188KB(这是 StackOverflowError —— 1.9.1 我不小心在 AiAssistant 里加了一个与 companion 同名的实例方法 linesPerChunk,它调用自身形成无限递归,一点整理就爆栈。已重命名为 chunkSizeFor 并加回归测试;另外把 StackOverflowError / OutOfMemoryError 也纳入兜底提示)。
- 1.9.2 — 修复:AI 批量导入闪退(预览列表用「小写词头」当 LazyColumn 的 key,模型返回 Apple/apple 这类重复词头时 key 冲突直接抛异常 → 改用下标做 key,勾选状态同步下标化;批量导入与写入词库全链路加异常兜底,出错只在页面提示不再闪退);弹窗误把 API Key 当单词表(打开页面会自动读取剪贴板,若剪贴板里是 sk-… 这类密钥会被当成内容并发给模型 —— 现在识别密钥形态后不自动填入,手动粘贴或点击整理时给出明确提示;输入框右侧新增清空按钮);新增崩溃日志(WordBookApp 安装全局异常处理器写 last-crash.txt,设置 → 关于 可一键复制日志/清除)。
- 1.9.1 — 修复/新增:默认输出预算 2048 → 8192(每词条约 300 tokens,2048 只够 5 个词,这就是「23 个词只整理出 9 个」的原因;估算值也从 220 修正为 300);AI 设置里新增可调项——最大回复长度上限 8192 → 32768(带 2048/8192/16384/32768 快捷档)、「批量导入每次处理 N 行」独立滑杆(0=自动按预算推算,可手动设成不小于行数即可一次全部导入)、超时上限 180 → 300 秒,并实时显示当前每次处理多少行;复合词组算一个新词条(提示词新增规则 6/7: hickening agent、come away 这类多词表达整体处理、词性用 phr.,且每一行都必须产出一个元素不得跳过);预览页新增「调大一次处理量」快捷入口。
- 1.9.0 — 修复/新增:修复「AI 批量导入」不工作(默认 max_tokens 只有 2048,一次发 40 行必然在 JSON 中途被截断,数组没有闭合括号 → 解析全部失败、预览空白。现在①分块大小按输出预算自动计算(2048→7 行,8192→29 行),②数组被截断时会抢救出已完整返回的词条而不是整段丢弃,③某段返回的词条数少于输入行数时自动对半拆分重试;另外修复批量导入的重复检测(原来拿纯小写单词去比 headword@group 键,永远不匹配,已存在也被当成新词));无需整理格式:进入页面自动读取剪贴板,txt/csv/md/json、带序号、中英混排、整段课文都能直接交给 AI 补全音标/词性/释义/例句/标签/分组后预览勾选。
- 1.8.1 — 修复:「获取在线模型」结果不对(把 …/v1/chat/completions 这样的完整地址直接拼上 /models 导致 404;同时把接口返回的可读名 ame(如 GPT-4o Mini)当成模型 ID 收集进来 —— 现在先归一化地址再取 id,并过滤掉向量化 / 语音 / 画图 / 审核等非对话模型,弹窗会说明隐藏了几个);模型调用报 400(推理模型不接受 emperature、o 系列/gpt-5 把 max_tokens 改成 max_completion_tokens、连续同角色消息被拒 —— 现在按模型名自动调整参数,并在收到参数类 400 时自动降级重试最多 2 次;错误提示会直接指出是模型名问题还是参数问题)。
- 1.8.0 — 变更:移除「统计」页(底部标签由 5 个减为 4 个:生词本 / 复习 / AI 问答 / 设置,统计页代码与文案一并删除);修复词条卡片文字显示不全(单词行原来和音标争抢权重, utritionist 被截成 utritio… —— 改为单词优先、音标先让位;释义行原被 74dp 的日期槽挤成窄列 —— 日期挪到标签行右侧,中文释义现在占满整行宽度,行数上限 4→6,英文释义 2→3);图标换成新插画并完全保留原色(去掉上一版的雾面/调色处理,只做正方形取景 + 边缘羽化)。
- 1.7.1 — 图标换成用户提供的插画(AI 生成):以 ools/src/roxy-source.png 为源,裁切头像 → 背景压成深蓝雾面并压暗边缘 → 冷色调色,再导出全套自适应图标; ools/make_icon.py 可随时调参重跑。
- 1.7.0 — 修复/新增:修复「未分组」分组点开后列表空白(分组芯片用 ungrouped 哨兵表示未分组,但词条的 groupId 实际是 ull,两者直接比较永远不相等 —— 新增 oRealGroupId() 统一映射,生词本筛选与干净浏览都已修正);换成全新的应用图标:原创的蓝发水魔法少女(尖顶巫师帽 + 水滴与星光),由 ools/make_icon.py 用 Pillow 生成,自适应图标(前景/背景/单色三层)+ 各密度圆形与方形回退图标齐全。
- 1.6.3 — 调整:干净浏览的字母功能重做为右侧 A–Z 索引条(与主页一致),点字母滚动跳转到该首字母的第一条,不再把列表筛掉;不可用字母置灰、# 桶排在最后、位置随搜索/排序实时重算。
- 1.6.2 — 新增:干净浏览的底部工具栏新增两件套——🔤 字母索引(弹出 A–Z 网格,点字母即筛选跳转,当前字母高亮,可一键「显示全部」)与 ↩ 续读(按分组记住最后阅读位置,点一下回到上次读到的那一条;位置随滚动实时记录);干净浏览改为二级页面,系统返回键可返回上一级;工具栏改为横向滑动以容纳 7 个按钮。修复:clean_last_read 解码时把「未分组」(group key 为空字符串)当成非法条目丢弃,导致未分组永远无法续读;偏好写入统一改为 commit() 同步落盘。
- 1.6.1 — 修复:透明度方案改为「背景浓度」(数值越大背景越强,100% 时 Surface 不透明度从 0.60 降到 0.28,背景真正透出来;旧数据自动迁移);修复干净浏览会透出主页背景(打开干净浏览时主页背景自动让位,只画干净浏览自己的背景);主页背景与干净浏览背景旁各新增「保存」按钮(commit() 同步写盘 + 弹提示)。
- 1.6.0 — 新增/修复:背景改为透明度方案(0–100 滑杆 + 5 个快捷档位),透明度同时决定背景层 alpha 与 Surface 半透明程度;背景设置改用 commit() 同步保存,修复退出程序后设置丢失的问题;干净浏览新增分组内搜索 + 三种排序(A–Z / 最新 / 最早),并把全部控件(含退出按钮)移到底部工具栏(4 秒自动隐藏);排序方式与字号会持久化。
- 1.5.1 — 修复:背景设置完全不生效(原来背景只画在干净浏览页、且被页面自身的不透明 Surface 盖住,现在整个 App 都铺背景,并在有背景时把 Material Surface 调成半透明);干净浏览入口找不到(原来藏在分组芯片里的小图标,改成列表上方的整条按钮);主页背景与干净浏览背景拆成两套独立设置,各自可选图片/底色/压暗,支持互相复制,另加总开关。
- 1.5.0 — 新增:设置里一键添加四级英语单词(331 词内置词库 → 「四级核心」分组);分组间允许同一个单词、组内不允许重复的规则(导入、AI 批量导入、复制全部遵守);多选复制到分组与「移动并复制」;自定义背景(底色或相册图片);干净浏览模式(纯排版阅读 + 字号调节 + 自动隐藏工具栏);修复「关于」里版本号一直显示 1.0.0 的问题(改为读取 BuildConfig,并补充了构建/目标 SDK 说明)。
- 1.4.0 — 变更:移除列表顶部的「按字母 / 最近添加 / 今天新增 / 分组」切换行,列表固定按首字母 A–Z 分组,分组改为筛选条件;修复朗读 / ⭐ 按钮对不齐的问题(按钮移入固定宽度槽位);每个词条显示添加日期时间(今天 23:41 / 昨天 / N天前 / 完整日期);分组名称按字母排序,分组内词条同样按 A–Z;工具栏新增 📅 日历按钮,可按日期查看当天添加的单词;修复 startOfDay 在某些日期下可能偏移一天的隐患。
- 1.3.0 — 新增:词条行重排(释义完整换行显示,不再被截断,另加英文释义与笔记预览,标签可横向滑动);AI 接口支持多套配置档(新增 / 重命名 / 复制 / 删除 / 一键切换);导入格式大改(一行一个词、Tab/竖线/逗号任选、表头可选、词性义项写在同一个单元格、可导出模板),并在设置页写明格式说明;生词本右下角新增 ✨ AI 批量导入(粘贴或选文件 → AI 整理 → 预览勾选 → 入库,自动识别重复词、自动建分组);批量导入提示词可编辑。
- 1.2.0 — 新增:AI 设置里填好地址和 Key 后可「获取在线模型」(请求
/models接口列出该 Key 可用模型,支持搜索点选);AI 问答支持多会话,左上角抽屉查看 / 切换 / 重命名 / 删除历史对话;复习新增「全部词随机抽背」「今天新增的词」「按分组随机抽背」(带分组选择器);测验重做为独立设置页(范围 × 题型 × 题数)+ 逐题结果明细 + 错题重练。 - 1.1.0 — 新增:自定义 AI 接口(OpenAI 兼容 / Claude / Gemini)驱动的一键查词与 AI 问答聊天页;生词本新增「最近添加 / 今天新增 / 自定义分组」三种视图与当天新增徽章;多选批量删除、批量标重点、批量移动分组;统计页新增分组分布;AI 对话可导出 / 清空。
- 1.0.0 — 首个版本:A–Z 生词本、多词性编辑、记忆卡片 + 测验、统计、导入导出、Material 3 Expressive UI。
图标由 tools/make_icon.py(Pillow)从 tools/src/roxy-source.png 生成。整条流水线是可重跑的:
python tools/make_icon.py # 重新生成所有图标尺寸处理步骤:
- 裁切:以人物头部为中心取正方形(约 1.55 倍头高),保留头发、脸、领口与肩部
- 背景处理:把四角压成模糊的深蓝雾面(
HAZE/HAZE_DEEP),保留画面中心的人物细节 - 边缘压暗:径向压暗到 9,17,40,让明亮的脸在小尺寸下也能跳出来
- 冷色调色:压红、提蓝 + gamma 提亮中间调 + 轻微 S 曲线增加通透感
- 导出:自适应图标三层 + 各密度方形/圆形回退
| 输出 | 用途 |
|---|---|
mipmap-{m,h,xh,xxh,xxxh}dpi/ic_launcher_foreground.png |
自适应图标前景(画稿已缩进 66% 安全区) |
mipmap-*/ic_launcher_background.png |
自适应图标背景(深蓝渐变,和雾面同色系) |
drawable/ic_launcher_monochrome.xml |
主题图标(Android 13+)单色剪影,矢量 |
mipmap-*/ic_launcher.png + ic_launcher_round.png |
API 26 以下的方形 / 圆形回退 |
tools/icon-preview.png |
256px 预览 |
tools/icon-sizes.png |
96 / 72 / 48 / 36px 缩略对照,用来检查小尺寸辨识度 |
想换图:把新图覆盖到
tools/src/roxy-source.png(或改SRC常量),再跑一次脚本即可。裁切框、雾面强度、调色参数都在脚本顶部,注释里有说明。