Files
life-echo/docs/plans/2026-03-06-android-large-typography-implementation.md
iammm0 cca8ce7731 docs: 新增 Android 大字号排版方案文档
新增 Android 大字号排版的设计与实施计划文档,便于跨设备继续开发并保持上下文一致。
2026-03-09 09:08:38 +08:00

465 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Android Large Typography Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:**`app-android` 中完成全局大字模式排版与触达重构,让大字模式下文本“稳、清晰、易读”,并保证聊天/阅读/设置等高频页面一致体验。
**Architecture:** 基于 `LifeechoTheme` 的双层 tokenTypography + TouchTarget注入将 UI 从硬编码 `sp/dp` 迁移到语义化 token。先改主题与公共组件再改聊天/阅读/设置高频模块,最后做全局扫尾与验收。
**Tech Stack:** Kotlin, Jetpack Compose Material3, Compose UI Test, JUnit4, Gradle
**Related Skills:** @superpowers:executing-plans, @superpowers:verification-before-completion
---
### Task 1: 建立双层令牌与主题切换入口
**Files:**
- Create: `app-android/app/src/main/java/com/huaga/life_echo/ui/theme/TypographyTokens.kt`
- Create: `app-android/app/src/main/java/com/huaga/life_echo/ui/theme/TouchTargetTokens.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/theme/Theme.kt`
- Test: `app-android/app/src/test/java/com/huaga/life_echo/ui/theme/TypographyTokensTest.kt`
- Test: `app-android/app/src/test/java/com/huaga/life_echo/ui/theme/TouchTargetTokensTest.kt`
**Step 1: Write the failing test**
```kotlin
@Test
fun largeTypography_bodyWeightAndLineHeightRatio_areAccessible() {
val large = largeTypographyTokens()
val body = large.bodyPrimary
val ratio = body.lineHeight.value / body.fontSize.value
assertTrue(body.fontWeight.weight >= FontWeight.Medium.weight)
assertTrue(ratio in 1.45f..1.65f)
}
```
```kotlin
@Test
fun largeTouchTarget_minimumHeights_areAtLeast52dp() {
val large = largeTouchTargetTokens()
assertTrue(large.buttonMinHeight.value >= 52f)
assertTrue(large.listItemMinHeight.value >= 52f)
assertTrue(large.iconTapMinSize.value >= 52f)
}
```
**Step 2: Run test to verify it fails**
Run: `cd app-android && ./gradlew :app:testDebugUnitTest --tests "com.huaga.life_echo.ui.theme.*TokensTest" -i`
Expected: FAILtoken API 尚不存在)
**Step 3: Write minimal implementation**
```kotlin
data class TextToken(
val fontSize: TextUnit,
val lineHeight: TextUnit,
val fontWeight: FontWeight,
val letterSpacing: TextUnit,
val fontFamily: FontFamily = FontFamily.SansSerif,
)
data class TypographyTokens(
val headingPrimary: TextToken,
val bodyPrimary: TextToken,
val bodySecondary: TextToken,
val caption: TextToken,
val button: TextToken,
val bubble: TextToken,
val readingTitle: TextToken,
val readingBody: TextToken,
)
```
```kotlin
data class TouchTargetTokens(
val buttonMinHeight: Dp,
val listItemMinHeight: Dp,
val iconTapMinSize: Dp,
val inputMinHeight: Dp,
)
```
```kotlin
@Composable
fun rememberTypographyTokens(): TypographyTokens {
val large = AppSettings.rememberLargeFontMode()
return remember(large) { if (large) largeTypographyTokens() else normalTypographyTokens() }
}
```
**Step 4: Run test to verify it passes**
Run: `cd app-android && ./gradlew :app:testDebugUnitTest --tests "com.huaga.life_echo.ui.theme.*TokensTest" -i`
Expected: PASS
**Step 5: Commit**
```bash
cd app-android
git add app/src/main/java/com/huaga/life_echo/ui/theme/Theme.kt \
app/src/main/java/com/huaga/life_echo/ui/theme/TypographyTokens.kt \
app/src/main/java/com/huaga/life_echo/ui/theme/TouchTargetTokens.kt \
app/src/test/java/com/huaga/life_echo/ui/theme/TypographyTokensTest.kt \
app/src/test/java/com/huaga/life_echo/ui/theme/TouchTargetTokensTest.kt
git commit -m "feat(android): add typography and touch target token system"
```
### Task 2: 提供统一文本/触达 helper阻断页面硬编码继续扩散
**Files:**
- Create: `app-android/app/src/main/java/com/huaga/life_echo/ui/theme/TextTokenExtensions.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/theme/Dimensions.kt`
- Test: `app-android/app/src/test/java/com/huaga/life_echo/ui/theme/TextTokenExtensionsTest.kt`
**Step 1: Write the failing test**
```kotlin
@Test
fun toTextStyle_mapsTokenFieldsExactly() {
val token = TextToken(
fontSize = 18.sp,
lineHeight = 28.sp,
fontWeight = FontWeight.Medium,
letterSpacing = 0.2.sp,
fontFamily = FontFamily.SansSerif
)
val style = token.toTextStyle()
assertEquals(18.sp, style.fontSize)
assertEquals(28.sp, style.lineHeight)
assertEquals(FontWeight.Medium, style.fontWeight)
assertEquals(0.2.sp, style.letterSpacing)
}
```
**Step 2: Run test to verify it fails**
Run: `cd app-android && ./gradlew :app:testDebugUnitTest --tests "com.huaga.life_echo.ui.theme.TextTokenExtensionsTest" -i`
Expected: FAIL`toTextStyle()` 不存在)
**Step 3: Write minimal implementation**
```kotlin
fun TextToken.toTextStyle(): TextStyle = TextStyle(
fontSize = fontSize,
lineHeight = lineHeight,
fontWeight = fontWeight,
letterSpacing = letterSpacing,
fontFamily = fontFamily
)
```
并在 `Dimensions.kt` 中把原有 `AppTypographyData` 标记为过渡层,内部改为委托新 token避免双源
**Step 4: Run test to verify it passes**
Run: `cd app-android && ./gradlew :app:testDebugUnitTest --tests "com.huaga.life_echo.ui.theme.TextTokenExtensionsTest" -i`
Expected: PASS
**Step 5: Commit**
```bash
cd app-android
git add app/src/main/java/com/huaga/life_echo/ui/theme/TextTokenExtensions.kt \
app/src/main/java/com/huaga/life_echo/ui/theme/Dimensions.kt \
app/src/test/java/com/huaga/life_echo/ui/theme/TextTokenExtensionsTest.kt
git commit -m "refactor(android): add text token style helpers"
```
### Task 3: 改造公共组件(设置页基石)
**Files:**
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/common/SettingItem.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/common/SectionCard.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/common/AppScaffold.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/common/MarkdownText.kt`
- Test: `app-android/app/src/androidTest/java/com/huaga/life_echo/ui/components/common/SettingItemLargeModeTest.kt`
**Step 1: Write the failing test**
```kotlin
@Test
fun settingItem_largeMode_hasMinTouchHeightAndReadableTypography() {
composeRule.setContent {
AppSettings.largeFontMode = true
LifeechoTheme {
SettingItem(icon = AppIcons.FormatSize, label = "大字模式", type = SettingItemType.TOGGLE, value = true)
}
}
composeRule.onNodeWithText("大字模式").assertExists()
composeRule.onNodeWithText("大字模式").assertIsDisplayed()
}
```
**Step 2: Run test to verify it fails**
Run:
`cd app-android && ./gradlew :app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.huaga.life_echo.ui.components.common.SettingItemLargeModeTest`
Expected: FAIL触达最小高度/排版尚未统一)
**Step 3: Write minimal implementation**
```kotlin
val typography = LocalTypographyTokens.current
val touch = LocalTouchTargetTokens.current
Row(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = touch.listItemMinHeight)
.clickable(...)
)
```
`SectionCard`/`AppScaffold` 的标题与间距统一改为 token`MarkdownText` 的 paragraph/quote/code 同步走 token阅读页会复用
**Step 4: Run test to verify it passes**
Run: same command as Step 2
Expected: PASS
**Step 5: Commit**
```bash
cd app-android
git add app/src/main/java/com/huaga/life_echo/ui/components/common/SettingItem.kt \
app/src/main/java/com/huaga/life_echo/ui/components/common/SectionCard.kt \
app/src/main/java/com/huaga/life_echo/ui/components/common/AppScaffold.kt \
app/src/main/java/com/huaga/life_echo/ui/components/common/MarkdownText.kt \
app/src/androidTest/java/com/huaga/life_echo/ui/components/common/SettingItemLargeModeTest.kt
git commit -m "refactor(android): unify common component typography and touch targets"
```
### Task 4: 改造聊天组件(气泡 + 输入 + 语音)
**Files:**
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/chat/MessageBubble.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/chat/ChatInputField.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/chat/VoiceRecordButton.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/chat/MessageList.kt`
- Test: `app-android/app/src/androidTest/java/com/huaga/life_echo/ui/components/chat/LargeModeChatTypographyTest.kt`
**Step 1: Write the failing test**
```kotlin
@Test
fun chatInput_largeMode_respectsMinHeightAndReadableText() {
composeRule.setContent {
AppSettings.largeFontMode = true
LifeechoTheme {
ChatInputField(value = "", onValueChange = {}, onSend = {})
}
}
composeRule.onNodeWithText("输入消息...").assertExists()
}
```
**Step 2: Run test to verify it fails**
Run:
`cd app-android && ./gradlew :app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.huaga.life_echo.ui.components.chat.LargeModeChatTypographyTest`
Expected: FAIL输入框/语音按钮仍有硬编码高度与字体)
**Step 3: Write minimal implementation**
```kotlin
val typography = LocalTypographyTokens.current
val touch = LocalTouchTargetTokens.current
OutlinedTextField(
textStyle = typography.bodyPrimary.toTextStyle(),
modifier = Modifier.heightIn(min = touch.inputMinHeight)
)
```
并将聊天气泡正文、流式文本、时间分隔、语音按钮文案全部迁移到 token。
**Step 4: Run test to verify it passes**
Run: same command as Step 2
Expected: PASS
**Step 5: Commit**
```bash
cd app-android
git add app/src/main/java/com/huaga/life_echo/ui/components/chat/MessageBubble.kt \
app/src/main/java/com/huaga/life_echo/ui/components/chat/ChatInputField.kt \
app/src/main/java/com/huaga/life_echo/ui/components/chat/VoiceRecordButton.kt \
app/src/main/java/com/huaga/life_echo/ui/components/chat/MessageList.kt \
app/src/androidTest/java/com/huaga/life_echo/ui/components/chat/LargeModeChatTypographyTest.kt
git commit -m "refactor(android): migrate chat typography and touch areas to tokens"
```
### Task 5: 改造阅读组件(章节正文/标题/目录)
**Files:**
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterReadingView.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/FullTextReadingView.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterCard.kt`
- Test: `app-android/app/src/androidTest/java/com/huaga/life_echo/ui/components/memoir/LargeModeReadingTypographyTest.kt`
**Step 1: Write the failing test**
```kotlin
@Test
fun readingView_largeMode_usesReadableHeadingAndBody() {
composeRule.setContent {
AppSettings.largeFontMode = true
LifeechoTheme {
ChapterReadingView(chapter = sampleChapter())
}
}
composeRule.onNodeWithText("第1章").assertExists()
composeRule.onNodeWithText(sampleChapter().title).assertExists()
}
```
**Step 2: Run test to verify it fails**
Run:
`cd app-android && ./gradlew :app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.huaga.life_echo.ui.components.memoir.LargeModeReadingTypographyTest`
Expected: FAIL阅读页仍存在硬编码 `sp` 与固定行高)
**Step 3: Write minimal implementation**
```kotlin
Text(
text = chapter.title,
style = LocalTypographyTokens.current.readingTitle.toTextStyle()
)
```
```kotlin
MarkdownText(
content = processedContent,
fontSize = LocalTypographyTokens.current.readingBody.fontSize.value.toInt(),
lineHeight = LocalTypographyTokens.current.readingBody.lineHeight.value.toInt()
)
```
并将目录卡片/章号/提示文案统一迁移到 token。
**Step 4: Run test to verify it passes**
Run: same command as Step 2
Expected: PASS
**Step 5: Commit**
```bash
cd app-android
git add app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterReadingView.kt \
app/src/main/java/com/huaga/life_echo/ui/components/memoir/FullTextReadingView.kt \
app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterCard.kt \
app/src/androidTest/java/com/huaga/life_echo/ui/components/memoir/LargeModeReadingTypographyTest.kt
git commit -m "refactor(android): migrate memoir reading typography to tokens"
```
### Task 6: 改造高频页面与全局扫尾screen 层)
**Files:**
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ProfileScreen.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyMemoirScreen.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt`
- Modify: `app-android/app/src/main/java/com/huaga/life_echo/ui/screens/*.kt`(其余硬编码清理)
- Test: `app-android/app/src/test/java/com/huaga/life_echo/ui/theme/HardcodedTypographyGuardTest.kt`
**Step 1: Write the failing test**
```kotlin
@Test
fun uiDirectory_shouldNotIntroduceNewHardcodedSpValues() {
val offenders = scanUiFilesForPattern("""\b\d+(\.\d+)?\.sp\b""")
.filterNot { allowedLegacyPattern(it) }
assertTrue("Found hardcoded sp: $offenders", offenders.isEmpty())
}
```
**Step 2: Run test to verify it fails**
Run: `cd app-android && ./gradlew :app:testDebugUnitTest --tests "com.huaga.life_echo.ui.theme.HardcodedTypographyGuardTest" -i`
Expected: FAIL现有硬编码命中
**Step 3: Write minimal implementation**
- 优先处理高频页面(聊天、阅读、设置)
- 然后按文件批次替换其余 screen 层硬编码
- 必要保留项加白名单注释并记录原因
**Step 4: Run test to verify it passes**
Run: same command as Step 2
Expected: PASS或仅剩明确白名单
**Step 5: Commit**
```bash
cd app-android
git add app/src/main/java/com/huaga/life_echo/ui/screens \
app/src/test/java/com/huaga/life_echo/ui/theme/HardcodedTypographyGuardTest.kt
git commit -m "refactor(android): migrate screens to typography and touch tokens"
```
### Task 7: 完整验证与文档更新
**Files:**
- Modify: `app-android/README.md`
- Modify: `docs/plans/2026-03-06-android-large-typography-design.md`(补实现状态)
- Create: `docs/plans/2026-03-06-android-large-typography-test-report.md`
**Step 1: Write the failing verification checklist**
```markdown
- [ ] 聊天页大字模式无气泡截断
- [ ] 阅读页正文行高稳定
- [ ] 设置页列表项可触达 >= 52dp
- [ ] 3 分钟任务可完成
```
**Step 2: Run full test suites**
Run:
- `cd app-android && ./gradlew :app:testDebugUnitTest`
- `cd app-android && ./gradlew :app:connectedDebugAndroidTest`
Expected:
- Unit tests: BUILD SUCCESSFUL
- Android tests: all passed
**Step 3: Run manual DoD scenario**
Run 手工流程并记录:
- 打开大字模式
- 阅读章节
- 返回目录
- 发送消息
- 记录耗时和阅读反馈
**Step 4: Update docs with evidence**
- 在测试报告中写入命令、结果、设备、截图路径、失败重试记录(如有)。
**Step 5: Commit**
```bash
git add app-android/README.md \
docs/plans/2026-03-06-android-large-typography-design.md \
docs/plans/2026-03-06-android-large-typography-test-report.md
git commit -m "docs(android): add large typography migration verification report"
```
---
## Execution Notes
- 每个 Task 完成后先做局部 code review再进入下一 Task。
- 严格保持“测试先行(红)-> 最小实现(绿)-> 重构(保持绿)”。
- 若出现 UI 回归,优先回滚当前 Task 的改动,不跨 Task 修补。
- 完成前必须执行 @superpowers:verification-before-completion。