design: add dog park easter egg spec

This commit is contained in:
xfy 2026-06-16 15:29:34 +08:00
parent bd4dad4794
commit 5714802c89
2 changed files with 177 additions and 1 deletions

3
.gitignore vendored
View File

@ -22,5 +22,6 @@ node_modules/
.omc/ .omc/
logs/ logs/
.claude/ .claude/
docs/superpowers/ docs/superpowers/*
!docs/superpowers/specs/
.worktrees/ .worktrees/

View File

@ -0,0 +1,175 @@
# 关于页面「小狗乐园」彩蛋设计
## 背景
在「关于鸭鸭日历」页面中,版本号目前是一个无实际功能的 `TextButton`。本设计为其增加一个隐藏彩蛋:连续点击版本号 7 次后进入「小狗乐园」页面,全屏循环播放一段彩蛋视频。
## 目标
- 提升应用趣味性,给用户一个可发现的隐藏彩蛋。
- 保持现有架构:所有 UI 与业务逻辑留在 `:core``:app` 仅作为 Activity 壳。
- 不引入过度复杂的依赖或状态管理。
## 设计决策摘要
| 决策项 | 选择 | 说明 |
|--------|------|------|
| 总点击次数 | 7 次 | 足够隐藏,又不会太难触发 |
| 提示开始时机 | 第 4 次点击 | 前 3 次静默,避免普通用户误触时被打扰 |
| 提示文案 | 「再点击 N 下进入小狗乐园」 | N 为剩余次数,简洁明确 |
| 超时重置 | 1.5 秒 | 与 Android 开发者选项等经典彩蛋保持一致节奏 |
| 进度持久化 | 不持久化 | 离开页面或超时即重置 |
| 提示组件 | 系统 Toast | 最符合「小气泡」语义,轻量 |
| 视频播放 | Media3 ExoPlayer | 功能强、与 Compose 集成成熟 |
| 视频位置 | `core/src/main/assets/video/enter_screen_bg1.mp4` | 与现有 `app_icon.webp` 等资源保持一致 |
| 视频显示 | 等比裁剪铺满(`RESIZE_MODE_ZOOM` | 填满屏幕,视觉沉浸 |
| 声音 | 静音 | 不打扰用户 |
| 屏幕方向 | 跟随系统 | 不强制横竖屏 |
| 退出方式 | 系统返回键 | 支持预测性返回手势Activity 自然 finish |
| 进入过渡动画 | 淡入 400ms | 营造进入彩蛋的仪式感 |
| 退出过渡动画 | 默认 slide | 保持现有返回风格 |
## 触发机制(关于页面)
### 状态
- 在 `AboutScreen` 内使用 `remember { mutableIntStateOf(0) }` 保存当前连续点击次数。
- 计数为局部状态,不提升到 ViewModel也不持久化。
- `AboutScreen` 离开 Composition 时计数自然消失。
### 点击行为
| 当前点击次数 | 行为 |
|--------------|------|
| 1 ~ 3 | 计数 +1无 Toast |
| 4 | Toast「再点击 3 下进入小狗乐园」 |
| 5 | Toast「再点击 2 下进入小狗乐园」 |
| 6 | Toast「再点击 1 下进入小狗乐园」 |
| 7 | 调用 `onNavigateToDogPark()`,进入彩蛋页面 |
### 超时重置
每次点击启动/重启一个 `LaunchedEffect`
- 在 1.5 秒内收到下一次点击:取消旧 Job计数 +1。
- 1.5 秒内无新点击Job 执行,计数重置为 0。
## 导航链路
```
MainActivity
└── startActivityWithSlide → AboutActivity
└── onNavigateToDogPark → startActivityWithSlide → DogParkActivity
```
- `AboutActivity``AboutScreen` 新增回调 `onNavigateToDogPark: () -> Unit`
- `DogParkActivity` 继承 `BaseActivity`,自动获得 edge-to-edge 和 slide 转场支持。
## 小狗乐园页面
### 组件
- `DogParkScreen`:位于 `:core`,无业务参数,只负责全屏视频播放。
- `DogParkActivity`:位于 `:app`,继承 `BaseActivity`,壳逻辑。
### 视频播放
- 使用 Media3 ExoPlayer + `PlayerView`
- 通过 Compose `AndroidView` 嵌入 `PlayerView`
- 配置:
- `resizeMode = RESIZE_MODE_ZOOM`:等比裁剪铺满全屏。
- `useController = false`:不显示播放控件。
- `player.volume = 0f`:静音。
- `repeatMode = Player.REPEAT_MODE_ONE`:循环播放。
- 媒体源 URI`asset:///video/enter_screen_bg1.mp4`
### 生命周期
- `onStart``player.play()`
- `onStop``player.pause()`
- `onDestroy``player.release()`
- 使用 `DisposableEffect` 绑定释放逻辑。
### 退出
- 用户按系统返回键或执行返回手势时 Activity finish。
- 预测性返回手势由 Manifest 中的 `android:enableOnBackInvokedCallback="true"` 支持。
- 退出动画保留 `BaseActivity` 默认 slide。
## 过渡动画
### 进入动画(淡入)
- 新增 `app/src/main/res/anim/fade_in.xml`
- `DogParkActivity.onCreate` 中:
- Android 14+`overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, R.anim.fade_in, R.anim.fade_out)`
- 低版本:`overridePendingTransition(R.anim.fade_in, R.anim.fade_out)`(在 `super.onCreate` 之后、`setContent` 之前调用)
- 淡入时长约 400ms。
### 退出动画
- 保留 `BaseActivity` 默认的 slide 返回动画,不覆盖。
## 资源
- 视频文件:
- 来源:`~/Pictures/enter_screen_bg1.mp4`
- 目标:`core/src/main/assets/video/enter_screen_bg1.mp4`
- 动画资源:
- 新增 `app/src/main/res/anim/fade_in.xml`
## 依赖变更
### `gradle/libs.versions.toml`
新增:
```toml
[versions]
androidx-media3 = "1.6.1"
[libraries]
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "androidx-media3" }
androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "androidx-media3" }
```
### `core/build.gradle.kts`
新增:
```kotlin
implementation(libs.androidx.media3.exoplayer)
implementation(libs.androidx.media3.ui)
```
## 错误处理
- 视频加载/准备失败时,直接 `finishWithSlideBack()` 静默返回关于页面。
- 不显示弹窗或 Snackbar避免破坏彩蛋体验。
## 测试计划
### 单元测试
- 抽离纯函数 `getToastMessage(clickCount: Int): String?` 并测试:
- 1 ~ 3 返回 `null`
- 4 返回「再点击 3 下进入小狗乐园」
- 5 返回「再点击 2 下进入小狗乐园」
- 6 返回「再点击 1 下进入小狗乐园」
- 7 返回 `null`(此时已跳转)
### 手动测试
- 连续点击版本号 7 次,确认进入 `DogParkActivity`
- 点击过程中停顿 1.5 秒,确认计数重置。
- 确认进入动画为淡入。
- 确认视频全屏、静音、无控件、循环播放。
- 确认系统返回键正常退出,并回到关于页面。
- 确认低版本(< Android 14 Android 14+ 的淡入动画都生效
## 未包含在本期
- 多次进入彩蛋后的不同内容。
- 视频下载/动态更新。
- 屏幕常亮保持。
- 分享彩蛋入口。