Skip to content

About

按首字母 A–Z 自动分组的英语生词本 Android App。Material 3 + Jetpack Compose,数据本地存储,AI 接口自己配(OpenAI / Claude / Gemini 兼容)。

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Roxy notebook

Android CI License: MIT Platform Kotlin Compose

一个按 首字母 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 已连接 / 未配置(或底部「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.apk

TTS 朗读依赖系统的文字转语音引擎(多数国行 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

直接用 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 状态卡

六、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 爆炸。

七、Android 16 / 17 适配要点

  • 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(关闭系统栏自动蒙层),避免在低版本引用不存在的属性。

八、导入格式与 AI 批量导入

6.1 表格导入(设置 → 数据 → 从文件导入)

一行 = 一个词条,推荐用 Tab 分隔;也认 |、| 和逗号(逗号会按 CSV 引号规则解析)。表头可写可不写。

word	phonetic	senses	tags	group	note	status
diet	/ˈdaɪət/	n. 日常饮食;n. 规定饮食;v. 节食	健康 减肥	饮食	注意区分 die	NEW
  • senses 单元格用 ; 分隔多个义项,每项写成「词性 释义」;英文释义放括号里,例句写 例: …
  • 没有表头时按上面的列顺序解析;只有两列时按「单词 | 释义」处理
  • group 里写分组名,不存在会自动创建
  • 再次导入同一个单词会合并义项而不是产生重复条目
  • 想快速记几个词,一行 word|释义 就够
  • 设置页可以「导出导入模板」,拿到标准表头照着填

6.2 AI 批量导入(生词本右下角 ✨)

适合把别的软件/文件里的词表搬进来:

  1. 点「添加生词」上方的 ✨ 按钮
  2. 粘贴剪贴板或选择文件(txt / csv / md / json 都能读)
  3. 点「交给 AI 整理」——内容多时会自动分段发送(每段约 40 行),带进度显示
  4. AI 返回后进入预览页:逐条显示单词、音标、全部义项、分组、标签,可以勾选/取消,自动标出词库里已有的「已存在」条目
  5. 「跳过重复」开关默认开启;确认后点「导入生词本」才真正写入

没有配置 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. 裁切:以人物头部为中心取正方形(约 1.55 倍头高),保留头发、脸、领口与肩部
  2. 背景处理:把四角压成模糊的深蓝雾面(HAZE / HAZE_DEEP),保留画面中心的人物细节
  3. 边缘压暗:径向压暗到 9,17,40,让明亮的脸在小尺寸下也能跳出来
  4. 冷色调色:压红、提蓝 + gamma 提亮中间调 + 轻微 S 曲线增加通透感
  5. 导出:自适应图标三层 + 各密度方形/圆形回退
输出 用途
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 常量),再跑一次脚本即可。裁切框、雾面强度、调色参数都在脚本顶部,注释里有说明。

About

按首字母 A–Z 自动分组的英语生词本 Android App。Material 3 + Jetpack Compose,数据本地存储,AI 接口自己配(OpenAI / Claude / Gemini 兼容)。

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages