Skip to content

Commit 6812adf

Browse files
committed
feat: 引入夜间模式功能并更新主题颜色。
1 parent 4031a66 commit 6812adf

File tree

7 files changed

+106
-27
lines changed

7 files changed

+106
-27
lines changed

app/build.gradle.kts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ plugins {
55
alias(libs.plugins.kotlin.compose)
66
}
77

8+
import java.util.Properties
9+
810
android {
911
namespace = "com.xuyutech.hongbaoshu"
1012
compileSdk = 36
@@ -23,17 +25,17 @@ android {
2325
create("release") {
2426
val keystorePropertiesFile = rootProject.file("keystore.properties")
2527
if (keystorePropertiesFile.exists()) {
26-
val properties = java.util.Properties()
28+
val properties = Properties()
2729
properties.load(keystorePropertiesFile.inputStream())
2830

2931
storeFile = if (properties.containsKey("storeFile")) {
30-
file(properties["storeFile"] as String)
32+
file(properties.getProperty("storeFile"))
3133
} else {
3234
file("release.jks")
3335
}
34-
storePassword = properties["storePassword"] as String
35-
keyAlias = properties["keyAlias"] as String
36-
keyPassword = properties["keyPassword"] as String
36+
storePassword = properties.getProperty("storePassword")
37+
keyAlias = properties.getProperty("keyAlias")
38+
keyPassword = properties.getProperty("keyPassword")
3739
} else {
3840
// Fallback for CI/CD or if file missing (won't be able to sign release)
3941
// You can add logic here or leave empty/dummy to avoid build error but skip signing

app/src/main/java/com/xuyutech/hongbaoshu/reader/ReaderScreen.kt

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,14 @@ fun ReaderScreen(
8989
}
9090
}
9191

92-
BoxWithConstraints(
93-
modifier = Modifier.fillMaxSize()
94-
) {
92+
com.xuyutech.hongbaoshu.ui.theme.HongbaoshuTheme(darkTheme = state.value.isNightMode) {
93+
androidx.compose.material3.Surface(
94+
modifier = Modifier.fillMaxSize(),
95+
color = MaterialTheme.colorScheme.background
96+
) {
97+
BoxWithConstraints(
98+
modifier = Modifier.fillMaxSize()
99+
) {
95100
val screenHeightPx = with(density) { maxHeight.toPx().toInt() }
96101
val screenWidthPx = with(density) { maxWidth.toPx().toInt() }
97102
// 页面正文区域上下各 10% 留白,中间 80% 填充文字
@@ -106,7 +111,8 @@ fun ReaderScreen(
106111
val textStyle = TextStyle(
107112
fontSize = fontSizeSp,
108113
fontWeight = FontWeight.Normal,
109-
lineHeight = lineHeightSp
114+
lineHeight = lineHeightSp,
115+
color = MaterialTheme.colorScheme.onBackground
110116
)
111117
val annotationFontSizeSp = fontSizeSp * 0.85f
112118
val annotationStyle = TextStyle(
@@ -136,7 +142,8 @@ fun ReaderScreen(
136142
val tStyle = TextStyle(
137143
fontSize = fSizeSp,
138144
fontWeight = FontWeight.Normal,
139-
lineHeight = lHeightSp
145+
lineHeight = lHeightSp,
146+
color = Color.Black // PageConfig is used for measurement, not rendering
140147
)
141148
val aFontSizeSp = fSizeSp * 0.85f
142149
val aStyle = TextStyle(
@@ -263,9 +270,7 @@ fun ReaderScreen(
263270
}
264271

265272
Box(
266-
modifier = Modifier
267-
.fillMaxSize()
268-
.background(Color(0xFFFAF5EE))
273+
modifier = Modifier.fillMaxSize()
269274
) {
270275
when {
271276
state.value.isLoading -> {
@@ -385,7 +390,9 @@ fun ReaderScreen(
385390
},
386391
fontSizeLevel = state.value.fontSizeLevel,
387392
onFontSizeChange = { level -> viewModel.setFontSize(level) },
388-
onDismiss = { showMenu.value = false }
393+
onDismiss = { showMenu.value = false },
394+
onToggleNightMode = { viewModel.toggleNightMode() },
395+
isNightMode = state.value.isNightMode
389396
)
390397
}
391398

@@ -408,6 +415,8 @@ fun ReaderScreen(
408415
)
409416
}
410417
}
418+
}
419+
}
411420
}
412421

413422
/**
@@ -464,7 +473,9 @@ private fun MenuPanel(
464473
onNarrationToggle: (Boolean) -> Unit,
465474
fontSizeLevel: Int,
466475
onFontSizeChange: (Int) -> Unit,
467-
onDismiss: () -> Unit
476+
onDismiss: () -> Unit,
477+
onToggleNightMode: () -> Unit,
478+
isNightMode: Boolean
468479
) {
469480
Column(
470481
modifier = Modifier
@@ -499,7 +510,7 @@ private fun MenuPanel(
499510
)
500511
}
501512

502-
// Quick Actions Row (TOC)
513+
// Quick Actions Row (TOC + Night Mode)
503514
Row(
504515
modifier = Modifier
505516
.fillMaxWidth()
@@ -671,6 +682,30 @@ private fun MenuPanel(
671682
)
672683
}
673684

685+
// Night Mode Row
686+
Row(
687+
modifier = Modifier
688+
.fillMaxWidth()
689+
.padding(bottom = 12.dp),
690+
horizontalArrangement = Arrangement.SpaceBetween,
691+
verticalAlignment = Alignment.CenterVertically
692+
) {
693+
Column {
694+
Text("夜间模式", style = MaterialTheme.typography.bodyLarge)
695+
Text(
696+
"启用深色主题",
697+
style = MaterialTheme.typography.bodySmall,
698+
color = MaterialTheme.colorScheme.onSurfaceVariant
699+
)
700+
}
701+
Switch(
702+
checked = isNightMode,
703+
onCheckedChange = { onToggleNightMode() }
704+
)
705+
}
706+
707+
HorizontalDivider(modifier = Modifier.padding(bottom = 12.dp))
708+
674709
// Font Size Slider
675710
var sliderValue by remember(fontSizeLevel) { androidx.compose.runtime.mutableFloatStateOf(fontSizeLevel.toFloat()) }
676711
Row(
@@ -788,7 +823,6 @@ private fun PageContent(
788823
Box(
789824
modifier = Modifier
790825
.fillMaxSize()
791-
.background(Color(0xFFFAF5EE))
792826
.padding(horizontal = 20.dp)
793827
) {
794828
// 正文区域布局:上部 10% 留白,中间 80% 容器,3% 缓冲区,底部 7% 留白
@@ -835,7 +869,7 @@ private fun PageContent(
835869
Text(
836870
text = "${globalPageInfo.first} / ${globalPageInfo.second}",
837871
style = MaterialTheme.typography.labelSmall,
838-
color = Color.Gray,
872+
color = MaterialTheme.colorScheme.onSurfaceVariant,
839873
modifier = Modifier
840874
.fillMaxWidth()
841875
.align(Alignment.BottomCenter)

app/src/main/java/com/xuyutech/hongbaoshu/reader/ReaderState.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ data class ReaderState(
2323
val needPlayFirstSentence: Boolean = false, // 需要播放当前页第一句(翻页后)
2424
val narrationEnabled: Boolean = false, // 朗读模式是否开启
2525
val currentPageSentenceIds: List<String> = emptyList(), // 当前页的句子 ID 列表
26-
val lastPlayedSentenceId: String? = null // 上一个播放完成的句子 ID(用于查找下一句)
26+
val lastPlayedSentenceId: String? = null, // 上一个播放完成的句子 ID(用于查找下一句)
27+
val isNightMode: Boolean = false // 夜间模式
2728
)

app/src/main/java/com/xuyutech/hongbaoshu/reader/ReaderViewModel.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,8 @@ class ReaderViewModel(
342342
book = bookResult.book,
343343
missingAudio = bookResult.missingSentenceAudioIds,
344344
currentChapterIndex = cappedChapterIndex,
345-
pageIndex = saved.pageIndex // 由 UI 层校验
345+
pageIndex = saved.pageIndex, // 由 UI 层校验
346+
isNightMode = saved.isNightMode
346347
)
347348
restoreAudioState(saved)
348349
}.onFailure { e ->
@@ -449,6 +450,16 @@ class ReaderViewModel(
449450
}
450451
// 如果开启,由 UI 层触发播放第一句
451452
}
453+
454+
/**
455+
* 切换夜间模式
456+
*/
457+
fun toggleNightMode() {
458+
val current = _state.value ?: return
459+
val newMode = !current.isNightMode
460+
_state.value = current.copy(isNightMode = newMode)
461+
persistState()
462+
}
452463

453464
/**
454465
* 暂停朗读(保持开关状态,用于返回封面时)
@@ -489,7 +500,8 @@ class ReaderViewModel(
489500
narrationPosition = audioManager.state.value.narrationPosition,
490501
bgmIndex = audioManager.state.value.bgmIndex,
491502
bgmEnabled = audioManager.state.value.bgmEnabled,
492-
bgmVolume = audioManager.state.value.bgmVolume
503+
bgmVolume = audioManager.state.value.bgmVolume,
504+
isNightMode = current.isNightMode
493505
)
494506
)
495507
}

app/src/main/java/com/xuyutech/hongbaoshu/reader/SwipeablePageContainer.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.runtime.setValue
2020
import androidx.compose.ui.Modifier
2121
import androidx.compose.ui.draw.shadow
2222
import androidx.compose.ui.graphics.Color
23+
import androidx.compose.material3.MaterialTheme
2324
import androidx.compose.ui.input.pointer.pointerInput
2425
import androidx.compose.ui.platform.LocalConfiguration
2526
import androidx.compose.ui.platform.LocalDensity
@@ -73,7 +74,7 @@ fun SwipeablePageContainer(
7374
Box(
7475
modifier = Modifier
7576
.fillMaxSize()
76-
.background(Color(0xFFFAF5EE)) // 底色,模拟纸张
77+
.background(MaterialTheme.colorScheme.background) // 使用主题背景色
7778
.pointerInput(pageIndex, canGoPrev, canGoNext, onCenterTap, onTopDoubleTap) {
7879
detectTapGestures(
7980
onDoubleTap = { offset ->
@@ -185,7 +186,7 @@ fun SwipeablePageContainer(
185186
elevation = if (offset != 0f) 4.dp else 0.dp,
186187
clip = false
187188
)
188-
.background(Color(0xFFFAF5EE))
189+
.background(MaterialTheme.colorScheme.background) // 使用主题背景色
189190
) {
190191
content(pageIndex)
191192
}

app/src/main/java/com/xuyutech/hongbaoshu/storage/ProgressStore.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ data class ProgressState(
2424
val narrationPosition: Long = 0L,
2525
val bgmIndex: Int = 0,
2626
val bgmEnabled: Boolean = false,
27-
val bgmVolume: Float = 1.0f
27+
val bgmVolume: Float = 1.0f,
28+
val isNightMode: Boolean = false
2829
)
2930

3031
class ProgressStore(private val context: Context) {
@@ -36,6 +37,7 @@ class ProgressStore(private val context: Context) {
3637
private val keyBgmIndex = intPreferencesKey("bgm_index")
3738
private val keyBgmEnabled = booleanPreferencesKey("bgm_enabled")
3839
private val keyBgmVolume = floatPreferencesKey("bgm_volume")
40+
private val keyIsNightMode = booleanPreferencesKey("is_night_mode")
3941

4042
val progress: Flow<ProgressState> = context.progressDataStore.data.map { prefs ->
4143
ProgressState(
@@ -45,7 +47,8 @@ class ProgressStore(private val context: Context) {
4547
narrationPosition = prefs[keyNarrationPos] ?: 0L,
4648
bgmIndex = prefs[keyBgmIndex] ?: 0,
4749
bgmEnabled = prefs[keyBgmEnabled] ?: false,
48-
bgmVolume = prefs[keyBgmVolume] ?: 1.0f
50+
bgmVolume = prefs[keyBgmVolume] ?: 1.0f,
51+
isNightMode = prefs[keyIsNightMode] ?: false
4952
)
5053
}
5154

@@ -63,6 +66,7 @@ class ProgressStore(private val context: Context) {
6366
prefs[keyBgmIndex] = state.bgmIndex
6467
prefs[keyBgmEnabled] = state.bgmEnabled
6568
prefs[keyBgmVolume] = state.bgmVolume
69+
prefs[keyIsNightMode] = state.isNightMode
6670
}
6771
}
6872

app/src/main/java/com/xuyutech/hongbaoshu/ui/theme/Theme.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,33 @@ import androidx.compose.material3.darkColorScheme
55
import androidx.compose.material3.lightColorScheme
66
import androidx.compose.runtime.Composable
77

8-
private val LightColors = lightColorScheme()
9-
private val DarkColors = darkColorScheme()
8+
import androidx.compose.ui.graphics.Color
9+
10+
private val LightColors = lightColorScheme(
11+
primary = Color(0xFF8B4513), // 传统的褐色/书本色
12+
onPrimary = Color.White,
13+
secondary = Color(0xFFD2B48C), // 浅褐色
14+
onSecondary = Color.Black,
15+
background = Color(0xFFFAF5EE), // 羊皮纸/米色背景
16+
onBackground = Color(0xFF2D2D2D), // 深灰色文字
17+
surface = Color(0xFFFAF5EE),
18+
onSurface = Color(0xFF2D2D2D),
19+
surfaceVariant = Color(0xFFE0D5C1), // 稍深一点的米色,用于控件背景
20+
onSurfaceVariant = Color(0xFF4A4A4A)
21+
)
22+
23+
private val DarkColors = darkColorScheme(
24+
primary = Color(0xFFA0522D),
25+
onPrimary = Color.White,
26+
secondary = Color(0xFF8B4513),
27+
onSecondary = Color.White,
28+
background = Color(0xFF1E1E1E), // 深色背景
29+
onBackground = Color(0xFFE0E0E0), // 浅灰色文字
30+
surface = Color(0xFF1E1E1E),
31+
onSurface = Color(0xFFE0E0E0),
32+
surfaceVariant = Color(0xFF2C2C2C),
33+
onSurfaceVariant = Color(0xFFB0B0B0)
34+
)
1035

1136
@Composable
1237
fun HongbaoshuTheme(

0 commit comments

Comments
 (0)