16 KiB
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 的双层 token(Typography + 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
@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)
}
@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: FAIL(token API 尚不存在)
Step 3: Write minimal implementation
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,
)
data class TouchTargetTokens(
val buttonMinHeight: Dp,
val listItemMinHeight: Dp,
val iconTapMinSize: Dp,
val inputMinHeight: Dp,
)
@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
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
@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
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
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
@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
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
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
@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
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
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
@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
Text(
text = chapter.title,
style = LocalTypographyTokens.current.readingTitle.toTextStyle()
)
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
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
@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
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
- [ ] 聊天页大字模式无气泡截断
- [ ] 阅读页正文行高稳定
- [ ] 设置页列表项可触达 >= 52dp
- [ ] 3 分钟任务可完成
Step 2: Run full test suites
Run:
cd app-android && ./gradlew :app:testDebugUnitTestcd 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
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。