Files
diary-news/docs/android/04-milestones.md
Mavis 02f0260dfc docs(android): 完整方案 + logo 资源 + 启动屏
新增 docs/android/ 目录:
- README.md 总入口(快速上手 + 决策摘要 + 数据流)
- 01-architecture.md 模块划分 + 数据流 + 选型理由
- 02-api-contract.md 每个接口的请求/响应 + DTO 字段映射
- 03-build-run.md Gradle/SDK/网络安全白名单/真机调试
- 04-milestones.md 7 天里程碑 + DoD + E2E 测试场景

新增 assets/:
- logo/: 主图标 master + adaptive icon + 5 DPI launcher (方/圆)
- splash/: 启动屏 logo + 完整背景预览 + 5 DPI 资源
- android_resources/: 集成所需的 XML(adaptive icon/主题/颜色/字符串/drawable/layout)
- INTEGRATION.md 集成指南
- logo.svg + _make_logo.py 设计源

设计风格:参考用户提供的木质方块字母积木图,米色木纹底 +
深棕色字母 D,代表 'Diary',温暖私人日记感。

服务器体检:所有容器/API/DB/翻译主链路正常,TMT 本月已用 0.37%。
MaaS 备用通道上次已验证可用。
2026-06-10 14:11:43 +08:00

10 KiB

04 · 7 天里程碑

目标:第 7 天能装一个 APK 到真机,跑通 登录 → 列表 → 详情 → 收藏 → 离线缓存 完整链路。

工作量估计基于:全栈老哥(你已经会 Kotlin/Compose 基础),按一天 3-4 小时有效开发时间算。

每个里程碑有明确 DoD(Definition of Done)。不达 DoD 不算完成。


全局 DoD(贯穿 7 天)

  • ./gradlew assembleDebug 每次都 BUILD SUCCESSFUL
  • 真机/模拟器能装能跑,不闪退
  • git commit 节奏:每个里程碑一个 commit,信息写清楚
  • logcat 无 ERROR 级别的 crash
  • 没有 hardcoded 颜色 / 字体大小(全部走 MaterialTheme)

Day 1 — 工程骨架 + 鉴权三件套

目标:能登录、能持久化 token、401 自动 refresh、空 UI 能跳转。

任务清单

# 任务 文件 估时
1 新建 Android Studio 工程,改 libs.versions.toml + app/build.gradle.kts 见 03 30min
2 network_security_config.xml + 4 个 res 文件 见 03 §4 15min
3 DiaryNewsApp.kt(@HiltAndroidApp) app/ 5min
4 MainActivity.kt(@AndroidEntryPoint,装 AppNav) app/ 15min
5 DTO 文件 12 个 data/api/dto/ 1h
6 ApiService.kt data/api/ 30min
7 TokenStore.kt(EncryptedSharedPreferences) data/auth/ 30min
8 AuthInterceptor.kt + TokenAuthenticator.kt data/api/ 1h
9 NetworkModule.kt(Hilt) di/ 30min
10 LoginScreen.kt + LoginViewModel.kt ui/login/ 1h
11 AppNav.kt 骨架(Login ↔ Feed) ui/nav/ 15min
12 Theme.kt + Color.kt + Type.kt(复用 web 的蓝 #2080f0) ui/theme/ 30min

Day 1 DoD

  • ./gradlew assembleDebug BUILD SUCCESSFUL
  • 装到真机,启动能看到登录页
  • 输入 owner / test1234,点登录 → 跳到一个空白 Feed 页
  • 杀掉 app 再开 → 直接进 Feed 页(token 持久化生效)
  • 等 60min 后,任意接口请求 → 自动 refresh → 用户无感
  • logcat 里能看到 OkHttp 打印的请求日志(debug build)
  • 设置 → 应用 → Diary News → 存储,看不到明文 token(只在加密 SP 里)

验证方法

# Day 1 验证清单(贴在 issue / PR 描述里):
- [x] 登录成功,TokenStore 保存到 EncryptedSP
- [x] 重启 app 自动进 Feed(读 SP 成功)
- [x] 改密码 → 旧 token 在 server 失效 → app 收到 401 → 跳登录
- [x] 等 access 过期(60min)→ 任意请求 → 自动 refresh → 200

Day 2 — Feed 列表(分页 + 卡片)

目标:能滚能动,看到真实的新闻列表,卡片视觉对齐 web。

任务清单

# 任务 文件 估时
1 ArticlePagingSource.kt ui/feed/ 30min
2 FeedViewModel.kt(Paging 流) ui/feed/ 30min
3 ArticleCard.kt(对齐 web 视觉) ui/common/ 1h
4 FeedScreen.kt(LazyColumn + 加载状态) ui/feed/ 1h
5 源筛选 + 关键词搜索(下拉 + 输入) ui/feed/ 1h
6 空状态 / 错误状态 / 加载状态组件 ui/common/ 30min

Day 2 DoD

  • 列表能加载第一页(50 条)
  • 滑到底自动加载下一页(Paging 3 自动)
  • 卡片展示:源 / 语言 / 分类 tag / 时间 / 原标题(灰)/ 中标题 / 插图 / 译文正文摘要 / 评论钩子
  • 源筛选:选一个源,列表只剩该源
  • 关键词搜索:输入 "AI" → 列表过滤
  • 无网络时显示空状态 + 重试按钮
  • 滚得快时图片不卡(Coil 默认就 OK)

视觉对齐清单

对照 web frontend/src/views/Feed.vue:

  • 卡片圆角、间距、字号 1:1
  • 中文标题字号 18sp,原标题 13sp 灰
  • 评论钩子块背景 #f6f8ff,左边框 #2080f0 3dp

Day 3 — 详情页(三段 Tab)

目标:点卡片进详情,看到完整译文 + 评论 + 原文。

任务清单

# 任务 文件 估时
1 ArticleDetailDtodomain.ArticleDetail mapper data/ 30min
2 ArticleViewModel.kt(StateFlow<UiState>) ui/article/ 30min
3 ArticleScreen.kt 骨架 + TabRow ui/article/ 30min
4 ArticleTabs.kt:CommentaryTab / TranslationTab / OriginalTab ui/article/ 1h
5 TranslationTab:WebView 渲染 body_zh_formatted(无 JS) ui/article/ 1h
6 OriginalTab:WebView 渲染 body_html(无 JS) ui/article/ 15min
7 详情页顶部卡片(标题 / 分类 / 时间 / 插图) ui/article/ 30min
8 收藏按钮 ☆(Day 4 接 API,Day 3 先显示静态) ui/article/ 15min

Day 3 DoD

  • 点列表卡片 → 进详情页
  • 顶部展示:原标题(灰)/ 中标题(主)/ 插图 / 分类 tag / 发布时间 / 作者
  • 三个 Tab 可切换:评论 / 译文 / 原文
  • 译文 Tab 用 body_zh_formatted,如果没有就 fallback 到 body_zh_text
  • WebView 不开 JS(验证方式:runJavaScript 调用失败)
  • 详情页旋转屏幕 / 切换深色模式不崩

Day 4 — 收藏(乐观更新 + Room 缓存)

目标:能收藏 / 取消,UI 秒响应,失败可回滚。

任务清单

# 任务 文件 估时
1 Room:AppDatabase.kt + BookmarkDao.kt + BookmarkCacheEntity data/db/ 1h
2 BookmarkRepository.kt(乐观更新) data/repository/ 1h
3 DatabaseModule.kt(Hilt) di/ 15min
4 详情页收藏按钮接 ArticleViewModel.toggleBookmark() ui/article/ 30min
5 BookmarksScreen.kt + BookmarksViewModel.kt ui/bookmarks/ 1h
6 离线显示:无网络时用 Room 缓存渲染 ui/bookmarks/ 30min

Day 4 DoD

  • 详情页点 ☆ → 立刻变实心(乐观更新)
  • 后台调 POST /bookmarks,失败时 → 回滚 ☆ + Snackbar "收藏失败,已撤销"
  • 收藏列表页能看到所有收藏
  • 离线时收藏列表仍可看(读 Room 缓存)
  • 滑动列表时无卡顿(用 LazyColumn + key)

Day 5 — 源 + 订阅

目标:能看所有源、订阅 / 取消订阅。

任务清单

# 任务 文件 估时
1 SourceRepository.kt data/repository/ 30min
2 SourcesScreen.kt + SourcesViewModel.kt ui/sources/ 1h
3 订阅按钮 + POST/DELETE /subscriptions ui/sources/ 1h
4 底部导航栏:Feed / Sources / Bookmarks ui/nav/ 1h

Day 5 DoD

  • 底部三 Tab:Feed / Sources / Bookmarks
  • Sources 页能看到所有 enabled 源,带订阅状态
  • 点订阅按钮 → 立刻变化 + 后台请求
  • 失败 Snackbar 提示 + 回滚
  • Feed 页源筛选多选能跨 Tab 状态保留(navigation 状态管理)

Day 6 — 主题 + 通用打磨

目标:视觉统一、空状态友好、加载体验好。

任务清单

# 任务 文件 估时
1 Material3 主题:浅色 + 深色(Dynamic Color,Android 12+) ui/theme/ 1h
2 自定义启动屏 + 应用图标 res/ 30min
3 通用加载 / 空 / 错误组件统一风格 ui/common/ 30min
4 下拉刷新(SwipeRefreshLayout / PullToRefreshContainer) ui/feed/ 30min
5 网络状态监听(ConnectivityManager) data/ 30min
6 离线条 / Snackbar 提示 ui/common/ 30min
7 长按卡片 → 分享 / 复制 URL 菜单 ui/common/ 30min

Day 6 DoD

  • 切深色模式 → 全 app 颜色自动切(阅读体验好)
  • 启动屏不闪白(用 SplashScreen API)
  • 列表支持下拉刷新
  • 飞行模式下进入 app → 各页有合理提示
  • 长按文章卡片弹出菜单:复制链接 / 在浏览器打开 / 分享

Day 7 — 收尾 + 真机端到端测试 + APK 打包

目标:出第一个 release-ready APK,完整跑一遍所有功能。

任务清单

# 任务 文件 估时
1 README.md(装 app 说明 + 调试指南) 根目录 30min
2 ProGuard / R8 配置 + release build 验证 proguard-rules.pro 1h
3 真机端到端测试(用 e2e-checklist.md 列表) 2h
4 crash 报告接入(可选:Sentry / Firebase Crashlytics) 1h
5 修最后发现的 bug 1h

Day 7 DoD

  • ./gradlew assembleReleaseapp-release-unsigned.apk
  • debug APK 跑完下面所有场景,全过

E2E 测试场景清单

  • 第一次启动 → 登录页
  • 登录成功 → 进 Feed
  • 列表能加载,滑到底加载更多
  • 源筛选 / 关键词搜索生效
  • 点卡片进详情,三段 Tab 都能切
  • 收藏 / 取消收藏 UI 秒响应,后台请求成功
  • 收藏页能看到刚收藏的
  • 杀进程,重启,直接进 Feed(token 持久化)
  • 飞行模式启动,各页空状态 + 离线缓存(收藏)仍可用
  • access 过期后任意请求,自动 refresh,UI 无感
  • 改密码,旧 token → app 跳登录

风险 & 应对

风险 概率 影响 应对
Compose / Kotlin 版本不兼容,build 失败 严格用本文档指定版本;失败先查 Compose-Kotlin Compatibility Map
后端 schema 改了,app 跑不起来 启动时 catch SerializationException,跳错误页 + 重启按钮
真机 7.0 / 7.1 系统太老,某些 API 没有 用 AndroidX 兼容层;遇到问题查 API 24 compat matrix
网络安全白名单加错,全 app 报错 第 1 次 build 一定先跑清单 Day 1 DoD
11 章(ProGuard)没做就 release,接口全找不到 Day 7 必须先跑 ./gradlew assembleRelease 再装机
EncryptedSharedPreferences 在某些定制 ROM 上崩 极低 catch GeneralSecurityException,fallback 引导用户重新登录

后续迭代方向(7 天后看)

优先级 功能 估时
P1 FCM 推送(新文章到时通知) 2-3 天
P1 阅读历史(本地) 半天
P2 平板 adaptive(WindowSizeClass + 双栏) 1-2 天
P2 离线下载包(整周报导出) 2-3 天
P3 Wear OS 端 1 周
P3 主屏 widget 2-3 天
P3 多账号切换 1 天

任务量快速核算

Day 估时 主要内容
1 7h 工程 + 鉴权
2 4.5h Feed + 分页 + 卡片
3 4.5h 详情 + WebView
4 4h 收藏 + Room
5 3.5h 源 + 订阅 + 底导
6 4h 主题 + 通用
7 5.5h 测试 + APK
总计 ~33h 7 个有效工作日