feat: 增加主题模式切换(跟随系统/浅色/暗色),修复暗色模式字体颜色不可读问题
This commit is contained in:
519
docs/superpowers/plans/2026-05-13-theme-mode-plan.md
Normal file
519
docs/superpowers/plans/2026-05-13-theme-mode-plan.md
Normal file
@@ -0,0 +1,519 @@
|
||||
# 主题模式切换 Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Add theme mode toggle (follow-system/light/dark) and fix dark mode font visibility
|
||||
|
||||
**Architecture:** Add ThemeMode enum + PreferencesManager storage, thread themeMode through InspectionCameraTheme, replace all hardcoded Color values with MaterialTheme.colorScheme.* across all screens
|
||||
|
||||
**Tech Stack:** Jetpack Compose, Material3, DataStore Preferences
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add ThemeMode enum
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/com/inspection/camera/data/models/WatermarkModels.kt`
|
||||
|
||||
- [ ] **Step 1: Add ThemeMode enum**
|
||||
|
||||
Add after the existing enums at end of file:
|
||||
|
||||
```kotlin
|
||||
enum class ThemeMode {
|
||||
FOLLOW_SYSTEM,
|
||||
LIGHT,
|
||||
DARK
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify compilation**
|
||||
|
||||
Run: `cd android-CheckShot && ./gradlew :app:compileDebugKotlin`
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/data/models/WatermarkModels.kt
|
||||
git commit -m "feat: add ThemeMode enum (follow-system/light/dark)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Add themeMode to PreferencesManager
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/com/inspection/camera/data/PreferencesManager.kt`
|
||||
|
||||
- [ ] **Step 1: Add KEY_THEME_MODE + themeMode Flow + setter**
|
||||
|
||||
After `KEY_FILE_NAME_TEMPLATE` line, add:
|
||||
```kotlin
|
||||
private val KEY_THEME_MODE = stringPreferencesKey("theme_mode")
|
||||
```
|
||||
|
||||
After `fileNameTemplate` Flow, add:
|
||||
```kotlin
|
||||
val themeMode: Flow<ThemeMode> = context.dataStore.data.map { prefs ->
|
||||
val mode = prefs[KEY_THEME_MODE] ?: ThemeMode.FOLLOW_SYSTEM.name
|
||||
try {
|
||||
ThemeMode.valueOf(mode)
|
||||
} catch (e: Exception) {
|
||||
ThemeMode.FOLLOW_SYSTEM
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After `setFileNameTemplate`, add:
|
||||
```kotlin
|
||||
suspend fun setThemeMode(mode: ThemeMode) {
|
||||
context.dataStore.edit { prefs ->
|
||||
prefs[KEY_THEME_MODE] = mode.name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Add import for ThemeMode above existing imports:
|
||||
```kotlin
|
||||
import com.inspection.camera.data.models.ThemeMode
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify compilation**
|
||||
|
||||
Run: `cd android-CheckShot && ./gradlew :app:compileDebugKotlin`
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/data/PreferencesManager.kt
|
||||
git commit -m "feat: add themeMode preference to DataStore"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Update InspectionCameraTheme to accept themeMode
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/com/inspection/camera/ui/theme/Theme.kt`
|
||||
|
||||
- [ ] **Step 1: Replace darkTheme param with themeMode**
|
||||
|
||||
Change the function signature from:
|
||||
```kotlin
|
||||
fun InspectionCameraTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit
|
||||
)
|
||||
```
|
||||
to:
|
||||
```kotlin
|
||||
fun InspectionCameraTheme(
|
||||
themeMode: ThemeMode = ThemeMode.FOLLOW_SYSTEM,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val isDark = when (themeMode) {
|
||||
ThemeMode.FOLLOW_SYSTEM -> isSystemInDarkTheme()
|
||||
ThemeMode.LIGHT -> false
|
||||
ThemeMode.DARK -> true
|
||||
}
|
||||
val colorScheme = if (isDark) DarkColorScheme else LightColorScheme
|
||||
// ... rest stays the same
|
||||
}
|
||||
```
|
||||
|
||||
Also remove the old `darkTheme: Boolean = isSystemInDarkTheme()` parameter and the `val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme` line since it's now inside the function.
|
||||
|
||||
Add import:
|
||||
```kotlin
|
||||
import com.inspection.camera.data.models.ThemeMode
|
||||
```
|
||||
|
||||
The final function should look like:
|
||||
```kotlin
|
||||
@Composable
|
||||
fun InspectionCameraTheme(
|
||||
themeMode: ThemeMode = ThemeMode.FOLLOW_SYSTEM,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val isDark = when (themeMode) {
|
||||
ThemeMode.FOLLOW_SYSTEM -> isSystemInDarkTheme()
|
||||
ThemeMode.LIGHT -> false
|
||||
ThemeMode.DARK -> true
|
||||
}
|
||||
val colorScheme = if (isDark) DarkColorScheme else LightColorScheme
|
||||
// ... rest unchanged
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify compilation**
|
||||
|
||||
Run: `cd android-CheckShot && ./gradlew :app:compileDebugKotlin`
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/ui/theme/Theme.kt
|
||||
git commit -m "refactor: replace darkTheme param with themeMode in InspectionCameraTheme"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Update MainActivity to pass themeMode
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/com/inspection/camera/ui/MainActivity.kt`
|
||||
|
||||
- [ ] **Step 1: Read themeMode from PreferencesManager and pass to theme**
|
||||
|
||||
Add `var themeMode` state variable and collect the Flow. Pass to `InspectionCameraTheme`:
|
||||
|
||||
```kotlin
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.inspection.camera.data.models.ThemeMode
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var preferencesManager: PreferencesManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
preferencesManager = PreferencesManager(this)
|
||||
|
||||
setContent {
|
||||
var themeMode by remember { mutableStateOf(ThemeMode.FOLLOW_SYSTEM) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
preferencesManager.themeMode.collect { themeMode = it }
|
||||
}
|
||||
|
||||
InspectionCameraTheme(themeMode = themeMode) {
|
||||
MainApp(preferencesManager = preferencesManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Remove `import androidx.compose.foundation.isSystemInDarkTheme` if it was previously used only through `InspectionCameraTheme`'s default.
|
||||
|
||||
- [ ] **Step 2: Verify compilation**
|
||||
|
||||
Run: `cd android-CheckShot && ./gradlew :app:compileDebugKotlin`
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/ui/MainActivity.kt
|
||||
git commit -m "feat: read themeMode preference and pass to InspectionCameraTheme"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Add theme selector to SettingsScreen
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/com/inspection/camera/ui/settings/SettingsScreen.kt`
|
||||
|
||||
- [ ] **Step 1: Add themeMode state and collect from preferences**
|
||||
|
||||
Add state variable alongside existing ones:
|
||||
```kotlin
|
||||
var themeMode by remember { mutableStateOf(ThemeMode.FOLLOW_SYSTEM) }
|
||||
```
|
||||
|
||||
Add collection logic:
|
||||
```kotlin
|
||||
scope.launch {
|
||||
preferencesManager.themeMode.collect { themeMode = it }
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add theme section UI before "通用设置" section**
|
||||
|
||||
Add before the "通用设置" section (before `Spacer(modifier = Modifier.height(16.dp))` that precedes `SettingsSection(title = "通用设置")`):
|
||||
|
||||
```kotlin
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 主题设置
|
||||
SettingsSection(title = "主题设置") {
|
||||
SettingsItem(title = "界面主题") {
|
||||
listOf(
|
||||
ThemeMode.FOLLOW_SYSTEM to "跟随系统",
|
||||
ThemeMode.LIGHT to "浅色模式",
|
||||
ThemeMode.DARK to "暗色模式"
|
||||
).forEach { (mode, label) ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { scope.launch { preferencesManager.setThemeMode(mode) } }
|
||||
.padding(vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = themeMode == mode,
|
||||
onClick = { scope.launch { preferencesManager.setThemeMode(mode) } }
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(label)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace hardcoded settings colors. Change `CardDefaults.cardColors(containerColor = Color.White)` (line 352):
|
||||
```kotlin
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
|
||||
```
|
||||
|
||||
Change section title `Primary` (line 358) to `MaterialTheme.colorScheme.primary`.
|
||||
|
||||
Note: TopAppBar colors, description text colors, etc. will be fixed in Task 7 (global pass).
|
||||
|
||||
Add import:
|
||||
```kotlin
|
||||
import com.inspection.camera.data.models.ThemeMode
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Verify compilation**
|
||||
|
||||
Run: `cd android-CheckShot && ./gradlew :app:compileDebugKotlin`
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/ui/settings/SettingsScreen.kt
|
||||
git commit -m "feat: add theme selector UI to settings screen"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Replace hardcoded colors in GalleryScreen
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/com/inspection/camera/ui/gallery/GalleryScreen.kt`
|
||||
|
||||
- [ ] **Step 1: Replace color values**
|
||||
|
||||
Replace TopAppBar colors (lines 132-137):
|
||||
```kotlin
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
titleContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
actionIconContentColor = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
```
|
||||
|
||||
Replace `Color.LightGray` placeholder background (line 171):
|
||||
```kotlin
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
```
|
||||
|
||||
Replace `Color.Red` delete icon tint (line 239):
|
||||
```kotlin
|
||||
Icon(Icons.Default.Delete, contentDescription = "删除", tint = MaterialTheme.colorScheme.error)
|
||||
```
|
||||
|
||||
Replace `Color.Red` delete button text (line 274):
|
||||
```kotlin
|
||||
Text("确定删除", color = MaterialTheme.colorScheme.error)
|
||||
```
|
||||
|
||||
Replace `Color.Black.copy(alpha = 0.05f)` zoomable background (line 296):
|
||||
```kotlin
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
```
|
||||
|
||||
Remove unused imports: `import com.inspection.camera.ui.theme.Primary`
|
||||
|
||||
- [ ] **Step 2: Verify compilation**
|
||||
|
||||
Run: `cd android-CheckShot && ./gradlew :app:compileDebugKotlin`
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/ui/gallery/GalleryScreen.kt
|
||||
git commit -m "fix: replace hardcoded colors with MaterialTheme.colorScheme in GalleryScreen"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Replace hardcoded colors in MergeScreen
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/com/inspection/camera/ui/merge/MergeScreen.kt`
|
||||
|
||||
- [ ] **Step 1: Replace color values**
|
||||
|
||||
TopAppBar colors (lines 294-298):
|
||||
```kotlin
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
titleContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
```
|
||||
|
||||
Auto-import text color (line 344):
|
||||
```kotlin
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
```
|
||||
|
||||
Quality/layout button backgrounds (lines 368, 636):
|
||||
```kotlin
|
||||
.background(if (imageQuality == quality) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant)
|
||||
```
|
||||
|
||||
Quality button text (line 374):
|
||||
```kotlin
|
||||
color = if (imageQuality == quality) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface
|
||||
```
|
||||
|
||||
Placeholder background (line 406):
|
||||
```kotlin
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
```
|
||||
|
||||
Selection overlay (line 438):
|
||||
```kotlin
|
||||
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.5f), CircleShape)
|
||||
```
|
||||
|
||||
Check icon tint (lines 443, 649):
|
||||
```kotlin
|
||||
tint = MaterialTheme.colorScheme.onPrimary
|
||||
```
|
||||
|
||||
Add icon tint (line 451):
|
||||
```kotlin
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
```
|
||||
|
||||
Layout option backgrounds (line 636):
|
||||
```kotlin
|
||||
.background(if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant)
|
||||
```
|
||||
|
||||
Layout option text (line 643):
|
||||
```kotlin
|
||||
color = if (isSelected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface
|
||||
```
|
||||
|
||||
Remove unused import: `import com.inspection.camera.ui.theme.Primary`
|
||||
|
||||
- [ ] **Step 2: Verify compilation**
|
||||
|
||||
Run: `cd android-CheckShot && ./gradlew :app:compileDebugKotlin`
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/ui/merge/MergeScreen.kt
|
||||
git commit -m "fix: replace hardcoded colors with MaterialTheme.colorScheme in MergeScreen"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Replace hardcoded colors in SettingsScreen (remaining)
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/com/inspection/camera/ui/settings/SettingsScreen.kt`
|
||||
|
||||
- [ ] **Step 1: Replace remaining hardcoded colors**
|
||||
|
||||
Save button text (line 117):
|
||||
```kotlin
|
||||
Text("保存", color = MaterialTheme.colorScheme.onPrimary)
|
||||
```
|
||||
|
||||
TopAppBar colors (lines 120-124):
|
||||
```kotlin
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
titleContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
```
|
||||
|
||||
Description text colors (lines 160, 274, 324):
|
||||
```kotlin
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
```
|
||||
|
||||
```kotlin
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
```
|
||||
|
||||
```kotlin
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
```
|
||||
|
||||
Section title color (line 358) — already changed in Task 5 to `MaterialTheme.colorScheme.primary`.
|
||||
|
||||
- [ ] **Step 2: Verify compilation**
|
||||
|
||||
Run: `cd android-CheckShot && ./gradlew :app:compileDebugKotlin`
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/ui/settings/SettingsScreen.kt
|
||||
git commit -m "fix: replace hardcoded TopAppBar/card/description colors with MaterialTheme.colorScheme in SettingsScreen"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Verify CameraScreen overlay colors unchanged
|
||||
|
||||
**Files:**
|
||||
- Inspect: `app/src/main/java/com/inspection/camera/ui/camera/CameraScreen.kt`
|
||||
|
||||
- [ ] **Step 1: Confirm camera overlay colors are preserved**
|
||||
|
||||
CameraScreen uses semi-transparent overlays over the live camera preview:
|
||||
- `Color.Black.copy(alpha = 0.3f)` for top/bottom control bars
|
||||
- `Color.White` for FAB and icons
|
||||
- `Color.Black.copy(alpha = 0.6f)` for location card
|
||||
- `Color.Black.copy(alpha = 0.9f)` for flash effect
|
||||
|
||||
These should remain as-is since they overlay live camera preview, not app background.
|
||||
|
||||
- [ ] **Step 2: Optional — replace non-overlay colors if any are missed**
|
||||
|
||||
The PermissionRequest composable uses no hardcoded colors that need changing (text with default style inherits from MaterialTheme).
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/main/java/com/inspection/camera/ui/camera/CameraScreen.kt
|
||||
git commit -m "chore: confirm CameraScreen overlay colors are correct for dark mode"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Verification: Full build
|
||||
|
||||
- [ ] **Step 1: Full build**
|
||||
|
||||
```bash
|
||||
cd android-CheckShot && ./gradlew :app:assembleDebug
|
||||
```
|
||||
Expected: BUILD SUCCESSFUL
|
||||
|
||||
- [ ] **Step 2: Remove unused imports across all modified files**
|
||||
|
||||
Ensure no unused imports remain (e.g., `import com.inspection.camera.ui.theme.Primary` removed from GalleryScreen, MergeScreen).
|
||||
Reference in New Issue
Block a user