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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-11
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
## 上下文

参见 `proposal.md` 的“为什么”和 `specs/kindle-page-turn-animation/spec.md` 的行为契约。当前横向翻页模式由 `ReadView` 选择不同 `PageDelegate`,`HorizontalPageDelegate` 已提供当前页、前一页和后一页的 `CanvasRecorder` 快照以及统一的 `Scroller` 驱动。翻页模式整数同时进入全局阅读配置 JSON 和单本书 Room JSON;设置界面又直接把选项下标作为协议值,因此新增模式必须追加,不能插入或重排。

海信 Hi Reader Pro 实机只读调查确认系统存在 `com.hmct.epd.IEpdManager`,但相关 framework 类被标记为非 SDK `BLOCKED`,已提取接口中也没有方向、步数、区域或 swipe 波形能力。后续实机反馈还确认,普通 Android 绘制在清晰模式下基本没有动画,非清晰模式则存在不同程度的残影,因此普通 Canvas 路线已记为 `NO_GO_CLEAR_AND_ANIMATED`。

## 目标 / 非目标

**目标:**

- 沿用现有阅读页和 Delegate 边界,以最小增量加入可独立选择的软件扫屏模式。
- 将视觉进度、页面提交和连续输入拆成明确状态,保证每一页最多提交一次且失败可回退。
- 在不新增依赖、不改变 Room 结构和最低 SDK 21 的前提下,提供可自动测试的状态逻辑和可人工复现的墨水屏验收流程。
- 让调试包与设备正式版隔离,保持正式版应用和数据不受影响。

**非目标:**

- 不抽象或实现无真实 SDK 支撑的厂商硬件刷新层。
- 不尝试规避 Android 非 SDK 限制,不调用海信 Binder、系统签名能力或内核节点。
- 不改变现有其他五种动画、滚动排版、图片书强制模式、自动阅读、朗读或非相邻跳转逻辑。
- 不通过新的速度设置暴露调优参数;200ms 只作为极速模式观感较佳的软件候选,不作为核心门槛通过证据。

## 技术决策

### 1. 追加协议值并保持设置索引兼容

在 `PageAnim` 末尾追加值 `5`,并让 `ReadView` 为该值选择新的横向 Delegate。设置布局和单本书选择列表都把新选项追加在现有“无动画”之后,使物理下标继续与协议值一致;不重构其他模式的映射,避免扩大兼容面。普通模式的 `pageAnim`、墨水屏模式的 `pageAnimEInk` 和单本书 `ReadConfig.pageAnim` 沿用现有存储路径。“Kindle翻页动画”保留完整本地化文本,但设置按钮限制为单行并在宽度不足时尾部省略,避免第六个等宽按钮把整行撑高。

备选方案是在本次顺便把所有设置改成显式 ID 到协议值映射。它能降低未来重排风险,但会同时改动既有五种模式的读写路径,不符合聚焦变更原则;可在独立重构中处理。

### 2. 新 Delegate 复用现有页面录制结果

新增一个仅负责 Kindle 风格绘制和状态提交的 `HorizontalPageDelegate` 子类,复用现有 `curRecorder`、`prevRecorder`、`nextRecorder`,不再额外保存全屏 Bitmap。横屏双页天然作为同一个 `ReadView` 画布处理。

确认参考视频的 30fps 逐帧结果表明:纸张背景保持稳定,目标页内容停留在最终坐标并从翻页方向一侧逐步显现,起始页内容在同一区域逐步变浅,两页文字会在一段较宽区域内短暂叠印;不存在独立灰色竖条,也不存在整页平移。此前“硬裁剪目标页 + 约 10% 灰色渐变带”的模型已被实机观感否定,不得继续作为实现基础。

修订后的绘制单元使用宽幅空间交叉淡变遮罩。每帧先绘制完整起始页,再在隔离图层绘制目标页,并用带方向的透明度渐变限制目标图层:前沿之外一侧保持完整起始页,另一侧显示完整目标页,中间通过目标页透明度从 0 到 1 的连续变化自然产生旧墨迹变浅、新墨迹变深的叠印。遮罩只改变页面像素的混合比例,不额外绘制灰色、阴影或高亮颜色。首个验证值使用约 35% 视口宽度的交叉区,并在目标设备上依据参考观感调整;该比例不会作为用户设置暴露。

选择空间交叉淡变而不是逐像素 Bitmap 合成,是因为它可以通过 `Canvas` 隔离图层和透明度遮罩复用现有录制结果,同时保留内容驱动的灰阶叠印语义。实现需要验证 API 21 的 `saveLayer` 与 `DST_IN` 兼容路径;失败时直接显示正确目标页。

### 3. 分离扫动位置与内容灰阶缓动

首轮实机验证表明,120ms 基本不可辨,160ms 可辨但跳帧感明显,200ms 较完整而 250ms 偏慢;这些观察后来由用户复核为流畅模式结果,不是清晰模式证据。用户指出与参考视频最明显的剩余差异是当前线性扫动不够流畅。参考视频只有 30fps,且同时包含 E‑Ink 面板响应和相机采样,无法据此反推出 Kindle 控制器的精确曲线;逐帧只足以支持首尾变化较缓、中段变化更集中的视觉判断。

第二轮曾将归一化时间直接传入 `smoothstep`(`3t² - 2t³`),使扫动位置起步和收尾较慢、中段较快;在同一流畅模式下,120、160、200、220、250ms 五个候选均退化为“一闪而过”,肉眼无法辨认方向。该时间模型记为 `INVALID_EASING_MODEL`,不得作为生产参数;该结果只描述当轮流畅模式实测,不能据此归因清晰模式的面板调度或推断其行为。

修订后将两个维度分离:扫动位置恢复按真实经过时间线性跨越视口;仅对交叉区内的目标页透明度使用 `smoothstep`,让旧字变浅和新字变深的灰阶比例在两端更柔和,而不压缩边界穿越时间。Android `LinearGradient` 使用 0、0.25、0.5、0.75、1 五个采样点近似该曲线,遮罩颜色仍只有透明度,不绘制独立灰色。第三轮在流畅模式重点对比 200、220、250ms 后曾选择 220ms;最新四模式复核又确认极速模式动画最好且 200ms 主观更佳,因此软件时间线改为 200ms,但不得把这一选择描述为清晰模式通过。

第三轮与第一轮的主观观感没有明显区别,因此实机证据不能证明只对内容混合使用 `smoothstep` 带来了额外的可感知顺滑收益。保留该混合比例是因为它不压缩已验证的线性扫动位置,且仍符合参考视频首尾灰阶变化较缓的有限观察;不得据此声称还原 Kindle 的私有缓动或硬件波形。

KOReader 的公开实现也支持这一边界:在 Kindle PW5 等 MTK 设备上,它只向驱动提交方向和 12 个 swipe 步进,平滑波形由 Kindle 驱动生成;社区跨设备软件补丁采用线性硬分条刷新,在 Kobo 可用但已有 Android 墨水屏刷新不同步的公开反馈。两者都不能提供可直接移植的 Android 非线性时间曲线,因此本变更仍以目标设备实测为准,不复制硬分条模型,也不声称复刻私有波形。

不安排动画结束 500ms 后的额外 `invalidate`。Kindle 的补刷属于专有控制器行为,普通 Canvas 重绘无法指定同等波形,额外提交可能增加残影和耗电。

### 4. 手势只确认方向,不驱动可见进度

新 Delegate 单独处理触摸确认:移动阶段只判断是否越过现有触摸阈值、方向和目标页是否存在,不绘制新旧页混合状态;`ACTION_UP` 确认翻页后才准备录制结果并启动时间线。取消或未达到阈值的手势直接回到空闲态,不播放回弹动画。现有其他 Delegate 的跟手行为保持不变。

点击、音量键和键盘仍通过既有阅读入口发起上一页或下一页。目录、进度、恢复位置、自动阅读和朗读原本不依赖这一手动动画入口,本次不把它们接入新状态机。

### 5. 单动画状态机保证连续输入语义

将复杂的页面意图与 View 绘制分离成可单元测试的内部状态控制器,至少包含 `Idle`、`Animating`、`Returning` 和 `Committing`。每段记录起始页、方向、进度和递增的 generation;旧回调发现 generation 不匹配时立即退出,避免中断后的旧 `Scroller` 或 posted callback 再次提交页面。

- 空闲时的有效输入准备相邻页并进入 `Animating`。
- 动画中的同向输入先把当前段推进到终点,只调用一次现有页面提交入口,再为下一页启动新 generation;不保存无界动画 FIFO。
- 动画中的反向输入不提交目标页,而是从当前进度进入 `Returning` 并回到起始页,使净页面变化为零。
- 页面边界、内容准备失败或录制失败清空待处理意图并稳定在最后成功页面。
- 绘制时间超过预算或状态不一致时跳过视觉过程,只执行一次正确提交。

备选方案是完整排队每个动画,但连续输入会产生可见延迟;直接中断且不提交会丢页;只保存最终目标页又会让中间页提交与章节边界难以审计。单动画逐段快进在响应性、页数正确性和实现风险之间更平衡。

### 6. 新模式单独统一离散按键入口

现有实体键路径在长按翻页关闭时有 600ms leading-only 防抖,会丢弃窗口内的独立按键;`PageDelegate.keyTurnPage` 又在动画运行时直接返回。仅当当前 Delegate 是新模式时,活动层应把独立按键直接交给状态机,不使用该防抖;其他模式保留现状。

长按翻页关闭时忽略系统 repeat 事件,但每个新的物理按下都计入;开启时接受 repeat 事件并以单调时钟限制为最快约每最终动画时长一页。鼠标滚轮等未在规范中新增的入口沿用现有策略。

### 7. 主动选择和故障降级集中在动画入口

Hi Reader Pro 实机确认窗口动画、过渡动画和 Animator 时长比例均为 `0`,这是设备运行环境而不表示用户拒绝当前阅读模式。由于“Kindle翻页动画”是用户在应用内的明确主动选择,生产 Delegate 不读取这些全局比例,也不让 `ValueAnimator.areAnimatorsEnabled()` 阻断自身的 200ms 时间线;需要停止视觉效果时,用户可明确切换为“无动画”。页面录制异常、视口尺寸无效、绘制预算不足、生命周期结束或方向变化时,仍终止当前 generation 并稳定提交或回退,不能让视觉错误影响阅读位置。

### 8. 四种刷新模式复核推翻旧门槛

用户在生产路径复核后确认,前三轮候选和外部录像实际都来自流畅模式。最新四模式结果是:极速模式动画最好、200ms 主观更佳但有残影;流畅模式动画可见但卡顿且有残影;均衡模式很卡顿且仍有残影;清晰模式最终清晰但基本看不到动画。因此旧 `GO` 撤回,普通 Canvas 路线记录为 `NO_GO_CLEAR_AND_ANIMATED`。

通过条件仍是从清晰模式开始,肉眼能辨认方向、录像能看到移动中的新旧页边界,并在结束后恢复清晰且没有持续残影。当前证据不满足该条件,任务 5.2 保持未完成;不得用流畅或极速模式录像替代。

### 9. 海信自动组合刷新只适用于纵向滚动

Hi Reader Pro 的 `Activity` framework 确实包含自动组合刷新:真实纵向滚动越过阈值后设置动态刷新标志、临时切换较快模式,并在收到滚动结束回调后约一秒恢复原模式和清理残影。但横向触摸不会进入该分支,接口中也没有 Kindle 的方向与步数参数。

有界 `appDebug` 实验进一步验证:真实横向滑动保持动态标志为 `0`;纵向滚动可使标志发生 `0→1→0`。尝试仅使用标准 `MotionEvent` 和 View 滚动把该流程桥接到横向动画时,事件会递归进入应用手势处理并短暂改变刷新状态,不能可靠满足 `0→1→0`,记为 `NO_GO_UNSAFE_EVENT_BRIDGE`。实验代码已移除;2026-08-13 又通过真实系统列表滚动让 framework 将遗留动态标志恢复为 `0`,切回正式阅读页后当前应用刷新配置为清晰模式 `3`。不得将该桥接接入生产。

### 10. 实机测试隔离正式版

实机只构建、安装或更新 `appDebug`(`io.legado.app.debug`)。安装前再次只读确认包名;不得对 `io.legado.app.release` 执行安装覆盖、卸载、清数据或降级。调试数据独立创建,不从正式版复制书籍、配置或凭据;验收使用可公开或临时导入的测试文本。

### 11. 测试分层

把纯状态转换、协议值和设置映射作为 JVM 单元测试重点;Delegate 选择、裁剪方向、无效视口、录制失败和页面提交次数使用可行的 Android/Robolectric 或聚焦设备验证覆盖。实机还需确认系统动画时长比例为 `0` 时生产路径仍播放。物理 E‑Ink 是否显示扫动只能依赖外部观察,ADB 截图或录屏仅能证明应用生成了帧,不能代替面板验收。

## 风险 / 权衡

- [清晰模式实测基本看不到普通 Canvas 动画,非清晰模式保留动画却产生残影] → 记为 `NO_GO_CLEAR_AND_ANIMATED`;不推断厂商内部如何调度中间帧,停止把普通 Canvas 调参当作核心目标解决方案。
- [利用纵向组合刷新桥接横向动画导致事件递归或刷新状态悬挂] → 记为 `NO_GO_UNSAFE_EVENT_BRIDGE`,移除实验代码,不进入生产。
- [连续输入中旧回调二次提交页面] → 每段使用 generation,所有完成和中断路径集中到单一提交函数并测试提交计数。
- [交叉淡变在深色主题出现背景明暗接缝] → 遮罩只混合完整页面录制结果,不绘制独立颜色;覆盖浅色、深色和 E‑Ink 人工验证,若背景仍出现接缝则调整交叉区宽度或降级为无动画。
- [录制三页增加绘制成本] → 复用既有 CanvasRecorder,方向确定后只刷新当前页和一个目标页;预算不足直接无动画降级。
- [新增整数值破坏历史配置] → 只在尾部追加 `5`,补充 `0..4` 稳定性、JSON 读取和旧值兜底测试;不新增 Room 列或迁移。
- [“Kindle翻页动画”被理解为 Amazon 官方实现] → 本地化和维护说明只陈述视觉风格,不声称官方授权、专有波形或完全复刻。
- [调试安装影响用户正式版] → 使用独立 applicationId,安装前核对目标包,禁止触碰正式版和其数据。

## 迁移与回滚

本变更不修改 Room schema、最低 SDK、规则格式、导入 URI 或备份结构。升级时仅增加可选值,默认行为不变。若回滚实现,已经保存值 `5` 的新版本配置在旧代码中会按现有未知值路径降级为无动画;值 `5` 不得在未来复用于其他语义。回滚不需要数据库迁移,也不得为了清理该值改写用户阅读配置。
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## 为什么

Legado 现有横向翻页模式无法呈现 Kindle Paperwhite 5 那种快速、定向且最终清晰的电子墨水扫屏效果。实机复核确认,普通 Android 逐帧绘制在海信 Hi Reader Pro 上只能在“快速模式可见但有残影”和“清晰模式最终清晰但中间帧不可见”之间取舍,不能单独满足核心目标。系统自动组合刷新路线也已验证失败:它只响应纵向滚动,横向标准事件桥接会递归进入应用手势处理,无法安全用于翻页。本变更需要保留可复用的软件候选及完整失败证据,并明确在当前权限边界内不得把核心能力标记为实现完成。

## 变更内容

- 新增第六种独立翻页模式“Kindle翻页动画”,保持纸张背景稳定,通过宽幅空间交叉淡变让新页墨迹显现、旧页墨迹滞后消退,并按前后翻页方向形成短暂的文字叠印扫动。
- 将该模式接入普通阅读设置、墨水屏阅读设置和单本书覆盖设置,并同步维护现有八套语言资源;现有默认值和用户配置保持不变。
- 将动画限制在普通分页文字及少量插图的手动相邻翻页;目录跳转、进度跳转、自动阅读、朗读翻页、图片书、漫画和 RSS 保持现有行为。
- 统一该模式下点击、滑动松手、音量键和键盘的连续翻页状态:同向输入逐页快进并继续,反向输入按净意图抵消,长按沿用现有开关并限速。
- 用户主动选择该模式后,即使墨水屏设备把系统动画时长比例固定为 `0` 也播放翻页动画;视口无效、性能不足和渲染失败时仍无动画降级,保证最终页面、页数和章节边界正确。
- 撤回此前基于错误刷新模式记录得到的“清晰模式 GO”和 220ms 最终参数;将普通 Canvas 路线记录为 `NO_GO_CLEAR_AND_ANIMATED`,200ms 仅作为极速模式下观感较好的软件候选,不作为核心目标完成证据。
- 已在海信 Hi Reader Pro(`HLTE556N`、Android 11)上完成 framework 自动组合刷新审计与有界实验:纵向滚动能触发临时动态刷新并结束,横向滑动不会触发;标准事件桥接存在递归和刷新状态失控风险,记为 `NO_GO_UNSAFE_EVENT_BRIDGE`,不接入生产。
- 当前结论为 `NO_GO_CLEAR_AND_ANIMATED`:生产代码中的 200ms 软件效果只适用于普通屏幕或设备快速模式候选,不能等同 Kindle 的专用硬件 swipe 波形,也不能作为“清晰且有动画”已经实现的证据。
- **非目标**:不直接调用海信私有 Binder、隐藏 API 或 sysfs,不持久切换设备全局刷新模式,不实现 Kindle 专有硬件波形,不要求 root 或系统签名,不新增未经真实厂商行为验证的通用硬件适配抽象,不改变 RTL 排版、图片书、漫画、RSS 或网页端行为。

## 能力

### 新增能力

- `kindle-page-turn-animation`:规定 Kindle 风格软件扫屏动画的设置、适用范围、交互状态、兼容降级和实机验收要求。

### 修改能力

无。当前主规范中没有阅读翻页动画能力。

## 影响

- 受影响模块:仅 Android `app/`,主要涉及翻页模式常量、`ReadView`/`PageDelegate` 链路、阅读设置与单本书设置、字符串资源及聚焦测试;`modules/book/`、`modules/rhino/` 和 `modules/web/` 不受影响。
- 用户可感知变化:用户可以主动选择“Kindle翻页动画”;默认翻页方式不变。前进和后退具有相反扫动方向,扫动区只由新旧页面内容的灰阶叠印构成,不显示独立灰色条;横屏双页作为一个完整阅读区域扫过。
- 兼容性与持久化:新模式必须追加为整数值 `5`,既有 `0..4` 含义不得改变。全局 JSON 与单本书 Room JSON 可保存新值,无需新增数据库列或 Room 迁移;旧版本读取 `5` 时按现有兜底行为降级为无动画。
- 安全与设备边界:实现不得包含从 Hi Reader Pro 提取的私有 framework 文件,不得直接调用 `com.hmct.epd` 服务或写入 EPD 控制节点;调试验证只安装独立包名 `io.legado.app.debug`,不得卸载、覆盖或清理设备上的 `io.legado.app.release` 及其数据。
- 依赖:不新增第三方依赖,不升级现有依赖,不引入 Room 结构变化。
- 可观察验收:目标设备初始保持清晰模式;翻页期间肉眼和外部录像均可辨认移动中的新旧墨迹叠印区,动画结束后文字恢复清晰且没有持续残影,纸张背景不得出现独立灰条。连续 20 次有效下一页最终尽量前进 20 页;正反输入可抵消;竖屏、横屏双页、浅色和深色主题显示正确;系统动画时长比例为 `0` 时主动选择的新模式仍播放,性能或设备能力不足时直接稳定显示正确目标页,且无崩溃、白屏或重复提交。当前该清晰且有动画的门槛尚未通过。
Loading
Loading