Audit of README, docs/, android/README.md, android/contract.md and CLAUDE.md against the current server, web and Android code: V3 navigation and settings locations, Docker Hub release images, GitHub clone URL and CI, missing API endpoints and error codes, architecture of lazy chunks/themes/sources, media pages and routes, the Android bridge contract, and the new file-management and member-upload permissions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.0 KiB
Android 外壳
Kotlin 写的 WebView 外壳:加载与 H5 同一份 web/ 构建产物,只补 WebView 做不到
或做不好的部分——连接状态、设备名、原生整页图片、系统 TTS、Media3 后台播放与原生视频。
排版的唯一实现在 web/,外壳不重写排版;两条实现只会让「最小覆盖 CSS」的策略分叉。
与 H5 的接口见 contract.md。
构建
需要 JDK 17+、Android SDK 35、Build Tools 35.0.1(AGP 8.7.3、Kotlin 2.0.21、
Gradle Wrapper 8.11.1,compileSdk/targetSdk 35,minSdk 24)。从仓库根目录:
sh android/scripts/build-web-assets.sh # 构建 web/ 并复制到 app/src/main/assets/web-assets/
cd android
./gradlew :app:assembleDebug # Windows 原生终端用 gradlew.bat
产物为 android/app/build/outputs/apk/debug/app-debug.apk。编译通过不等于真机阅读、
播放验证通过。
- 服务地址:默认留空,首次启动在登录页填写并记住。个人构建可预设:
./gradlew :app:assembleDebug -PdefaultServerUrl=http://nas.local:5888(写入BuildConfig.DEFAULT_SERVER_URL,经桥接方法configuredServerUrl()交给 H5 预填)。 - 资源目录:
app/build.gradle.kts把 assets 根目录替换为src/main/assets/web-assets, 所以web/dist的内容平铺在 APK 的assets/下。该目录除README.txt占位外被 Git 忽略。 - release 构建在没有提供 keystore 时使用 debug 签名。
- 包名:
applicationId与namespace均为com.htmake.readerpro,Kotlin 源码在app/src/main/java/com/htmake/readerpro/(媒体快捷方式的 action 也以此为前缀)。旧包名cool.cnb.reader的安装与新包是两个不同的应用,不能覆盖升级,也不迁移其本地数据。
不需要 JDK 的检查
sh android/scripts/check-shell.sh # 秒级
sh android/scripts/verify-web-assets.sh # 在 build-web-assets.sh 之后运行
check-shell.sh:包名与目录一致;com.htmake.readerpro.*导入都能解析;JS 桥接契约双向一致 (只把ReaderBridge.kt、SpeechBridge.kt里紧跟@JavascriptInterface的方法算作桥接方法, 每个都必须在web/src/android-bridge.d.ts中声明,反之亦然;没有注解的 Kotlin 方法页面调不到, 不应声明);ReaderBridge.kt分支用到的渲染模式都在web/src/formats/types.ts的RenderMode中;NativePageView能解码 JPEG/PNG/WebP。verify-web-assets.sh:确认暂存的 Web 产物正是 Gradle 会打进 APK 的那份——否则 APK 能构建成功却启动即白屏。scripts/verify-web-apk.ps1(PowerShell):逐文件比对已构建 APK 的assets/与web/dist的哈希。
CI:GitHub Actions ci.yml 的 Android 任务依次运行 check-shell.sh、build-web-assets.sh、
verify-web-assets.sh 和 ./gradlew --no-daemon :app:assembleDebug,并上传 debug APK 制品;
宿主与来源
WebHost.kt 用 WebViewAssetLoader 把打包的客户端从
https://appassets.androidplatform.net/assets/index.html 提供,而不是 file://:
file:// 下对服务端的 fetch() 是不透明跨域请求,常见绕法是关掉 Web 安全。HTTPS 资源来源让
请求成为普通 CORS 请求(服务端 server/src/http/cors.ts 放行该来源),isSecureContext 为真,
IndexedDB 等 API 与浏览器一致。因为资源加载器不能为任意路径回退到 index.html,客户端只用哈希路由。
- 登录、会话与离线数据都由 H5 自己管理(与浏览器相同的存储层);外壳不注入令牌。
- 服务端通常是局域网
http://,所以清单允许明文流量,WebView 允许混合内容。 - 书内外链交给系统浏览器打开;
reader-res:、blob:、data:、about:与资源来源留在 WebView 内。 - 返回键先走 WebView 历史,没有历史时才退出。
onPause调用window.__readerFlush(),让阅读进度在切到后台前落盘。- 桌面快捷方式
OPEN_VIDEO/OPEN_MUSIC/OPEN_AUDIOBOOK分别打开#/media/video、#/media/music、#/media/audiobook。 - debug 构建开启 WebView 远程调试;页面 console 输出到 logcat(标签
ReaderWeb)。
分工:谁渲染什么
| 内容 | 渲染方 | 说明 |
|---|---|---|
| EPUB、TXT 等可重排内容 | WebView | 只有 WebView 实现了 CSS 多栏、内嵌字体、竖排、振假名;原生重做只会更差。 |
整页图片(漫画页、图片页,RenderMode = 'image') |
原生 NativePageView(shell ≥ 2) |
平台解码并按视口采样,避免 WebView 每页一次解码和布局。仅在「适应整页」(contain) 时使用;外壳返回 false 就由 WebView 照常绘制,没有第三种结果。 |
PDF(document) |
WebView | canOpenDocument() 目前总是返回 false:系统查看器是独立页面,会把读者带出应用。 |
路由由 H5 决定(web/src/ui/reader-view.ts 通过 pageHost 询问外壳),外壳不嗅探扩展名或 UA。
朗读与影音
- 朗读:
SpeechBridge.kt封装系统TextToSpeech(shell ≥ 3),注册为独立接口window.ReaderAndroidSpeech,H5 把它挂到ReaderAndroid.speech。队列和句子游标留在 JS; 没有原生朗读时回退到 WebView 的speechSynthesis。 - 影音:
media/下的MediaPlaybackService(Media3MediaSessionService,前台服务、通知与 后台播放)、MediaVideoActivity(原生视频画面,支持画中画)和MediaBridge。MediaBridge不是@JavascriptInterface,而是只对资源来源、只对主框架开放的WebMessageListener(window.ReaderMedia), 需要 WebView 支持WEB_MESSAGE_LISTENER,否则 H5 使用网页播放器。播放与朗读互相打断。
分发
AGPL 免费、无收入,不走应用商店,直接侧载 APK。当前 CI 只产出 debug APK 制品;Docker 镜像发布
(release.yml)不包含 APK。iOS 不在本目录范围内。