manager: Rewrite UI state management
This commit is contained in:
@@ -6,114 +6,187 @@ import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Stable
|
||||
object CardConfig {
|
||||
// 卡片透明度
|
||||
var cardAlpha by mutableFloatStateOf(1f)
|
||||
internal set
|
||||
// 卡片亮度
|
||||
var cardDim by mutableFloatStateOf(0f)
|
||||
internal set
|
||||
// 卡片阴影
|
||||
var cardElevation by mutableStateOf(0.dp)
|
||||
var isShadowEnabled by mutableStateOf(true)
|
||||
var isCustomAlphaSet by mutableStateOf(false)
|
||||
var isCustomDimSet by mutableStateOf(false)
|
||||
var isUserDarkModeEnabled by mutableStateOf(false)
|
||||
var isUserLightModeEnabled by mutableStateOf(false)
|
||||
var isCustomBackgroundEnabled by mutableStateOf(false)
|
||||
internal set
|
||||
|
||||
// 功能开关
|
||||
var isShadowEnabled by mutableStateOf(true)
|
||||
internal set
|
||||
var isCustomBackgroundEnabled by mutableStateOf(false)
|
||||
internal set
|
||||
|
||||
var isCustomAlphaSet by mutableStateOf(false)
|
||||
internal set
|
||||
var isCustomDimSet by mutableStateOf(false)
|
||||
internal set
|
||||
var isUserDarkModeEnabled by mutableStateOf(false)
|
||||
internal set
|
||||
var isUserLightModeEnabled by mutableStateOf(false)
|
||||
internal set
|
||||
|
||||
// 配置键名
|
||||
private object Keys {
|
||||
const val CARD_ALPHA = "card_alpha"
|
||||
const val CARD_DIM = "card_dim"
|
||||
const val CUSTOM_BACKGROUND_ENABLED = "custom_background_enabled"
|
||||
const val IS_SHADOW_ENABLED = "is_shadow_enabled"
|
||||
const val IS_CUSTOM_ALPHA_SET = "is_custom_alpha_set"
|
||||
const val IS_CUSTOM_DIM_SET = "is_custom_dim_set"
|
||||
const val IS_USER_DARK_MODE_ENABLED = "is_user_dark_mode_enabled"
|
||||
const val IS_USER_LIGHT_MODE_ENABLED = "is_user_light_mode_enabled"
|
||||
}
|
||||
|
||||
fun updateAlpha(alpha: Float, isCustom: Boolean = true) {
|
||||
cardAlpha = alpha.coerceIn(0f, 1f)
|
||||
if (isCustom) isCustomAlphaSet = true
|
||||
}
|
||||
|
||||
fun updateDim(dim: Float, isCustom: Boolean = true) {
|
||||
cardDim = dim.coerceIn(0f, 1f)
|
||||
if (isCustom) isCustomDimSet = true
|
||||
}
|
||||
|
||||
fun updateShadow(enabled: Boolean, elevation: Dp = 4.dp) {
|
||||
isShadowEnabled = enabled
|
||||
cardElevation = if (enabled) elevation else 0.dp
|
||||
}
|
||||
|
||||
fun updateBackground(enabled: Boolean) {
|
||||
isCustomBackgroundEnabled = enabled
|
||||
// 自定义背景时自动禁用阴影以获得更好的视觉效果
|
||||
if (enabled) {
|
||||
updateShadow(false)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateThemePreference(darkMode: Boolean?, lightMode: Boolean?) {
|
||||
isUserDarkModeEnabled = darkMode ?: false
|
||||
isUserLightModeEnabled = lightMode ?: false
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
cardAlpha = 1f
|
||||
cardDim = 0f
|
||||
cardElevation = 0.dp
|
||||
isShadowEnabled = true
|
||||
isCustomBackgroundEnabled = false
|
||||
isCustomAlphaSet = false
|
||||
isCustomDimSet = false
|
||||
isUserDarkModeEnabled = false
|
||||
isUserLightModeEnabled = false
|
||||
}
|
||||
|
||||
fun setThemeDefaults(isDarkMode: Boolean) {
|
||||
if (!isCustomAlphaSet) {
|
||||
updateAlpha(if (isDarkMode) 0.88f else 1f, false)
|
||||
}
|
||||
if (!isCustomDimSet) {
|
||||
updateDim(if (isDarkMode) 0.25f else 0f, false)
|
||||
}
|
||||
// 暗色模式下默认启用轻微阴影
|
||||
if (isDarkMode && !isCustomBackgroundEnabled) {
|
||||
updateShadow(true, 2.dp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存卡片配置到SharedPreferences
|
||||
*/
|
||||
fun save(context: Context) {
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val prefs = context.getSharedPreferences("card_settings", Context.MODE_PRIVATE)
|
||||
prefs.edit().apply {
|
||||
putFloat("card_alpha", cardAlpha)
|
||||
putFloat("card_dim", cardDim)
|
||||
putBoolean("custom_background_enabled", isCustomBackgroundEnabled)
|
||||
putBoolean("is_shadow_enabled", isShadowEnabled)
|
||||
putBoolean("is_custom_alpha_set", isCustomAlphaSet)
|
||||
putBoolean("is_custom_dim_set", isCustomDimSet)
|
||||
putBoolean("is_user_dark_mode_enabled", isUserDarkModeEnabled)
|
||||
putBoolean("is_user_light_mode_enabled", isUserLightModeEnabled)
|
||||
putFloat(Keys.CARD_ALPHA, cardAlpha)
|
||||
putFloat(Keys.CARD_DIM, cardDim)
|
||||
putBoolean(Keys.CUSTOM_BACKGROUND_ENABLED, isCustomBackgroundEnabled)
|
||||
putBoolean(Keys.IS_SHADOW_ENABLED, isShadowEnabled)
|
||||
putBoolean(Keys.IS_CUSTOM_ALPHA_SET, isCustomAlphaSet)
|
||||
putBoolean(Keys.IS_CUSTOM_DIM_SET, isCustomDimSet)
|
||||
putBoolean(Keys.IS_USER_DARK_MODE_ENABLED, isUserDarkModeEnabled)
|
||||
putBoolean(Keys.IS_USER_LIGHT_MODE_ENABLED, isUserLightModeEnabled)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从SharedPreferences加载卡片配置
|
||||
*/
|
||||
fun load(context: Context) {
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
cardAlpha = prefs.getFloat("card_alpha", 1f)
|
||||
cardDim = prefs.getFloat("card_dim", 0f)
|
||||
isCustomBackgroundEnabled = prefs.getBoolean("custom_background_enabled", false)
|
||||
isShadowEnabled = prefs.getBoolean("is_shadow_enabled", true)
|
||||
isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false)
|
||||
isCustomDimSet = prefs.getBoolean("is_custom_dim_set", false)
|
||||
isUserDarkModeEnabled = prefs.getBoolean("is_user_dark_mode_enabled", false)
|
||||
isUserLightModeEnabled = prefs.getBoolean("is_user_light_mode_enabled", false)
|
||||
updateShadowEnabled(isShadowEnabled)
|
||||
val prefs = context.getSharedPreferences("card_settings", Context.MODE_PRIVATE)
|
||||
cardAlpha = prefs.getFloat(Keys.CARD_ALPHA, 1f).coerceIn(0f, 1f)
|
||||
cardDim = prefs.getFloat(Keys.CARD_DIM, 0f).coerceIn(0f, 1f)
|
||||
isCustomBackgroundEnabled = prefs.getBoolean(Keys.CUSTOM_BACKGROUND_ENABLED, false)
|
||||
isShadowEnabled = prefs.getBoolean(Keys.IS_SHADOW_ENABLED, true)
|
||||
isCustomAlphaSet = prefs.getBoolean(Keys.IS_CUSTOM_ALPHA_SET, false)
|
||||
isCustomDimSet = prefs.getBoolean(Keys.IS_CUSTOM_DIM_SET, false)
|
||||
isUserDarkModeEnabled = prefs.getBoolean(Keys.IS_USER_DARK_MODE_ENABLED, false)
|
||||
isUserLightModeEnabled = prefs.getBoolean(Keys.IS_USER_LIGHT_MODE_ENABLED, false)
|
||||
|
||||
// 应用阴影设置
|
||||
updateShadow(isShadowEnabled, if (isShadowEnabled) 4.dp else 0.dp)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新阴影启用状态
|
||||
*/
|
||||
@Deprecated("使用 updateShadow 替代", ReplaceWith("updateShadow(enabled)"))
|
||||
fun updateShadowEnabled(enabled: Boolean) {
|
||||
isShadowEnabled = enabled
|
||||
cardElevation = 0.dp
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主题模式默认值
|
||||
*/
|
||||
fun setThemeDefaults(isDarkMode: Boolean) {
|
||||
if (!isCustomAlphaSet) {
|
||||
cardAlpha = 1f
|
||||
}
|
||||
if (!isCustomDimSet) {
|
||||
cardDim = if (isDarkMode) 0.5f else 0f
|
||||
}
|
||||
updateShadowEnabled(isShadowEnabled)
|
||||
updateShadow(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取卡片颜色配置
|
||||
*/
|
||||
@Composable
|
||||
fun getCardColors(originalColor: Color) = CardDefaults.cardColors(
|
||||
containerColor = originalColor.copy(alpha = CardConfig.cardAlpha),
|
||||
contentColor = determineContentColor(originalColor)
|
||||
)
|
||||
object CardStyleProvider {
|
||||
|
||||
/**
|
||||
* 获取卡片阴影配置
|
||||
*/
|
||||
@Composable
|
||||
fun getCardElevation() = CardDefaults.cardElevation(
|
||||
defaultElevation = CardConfig.cardElevation,
|
||||
pressedElevation = CardConfig.cardElevation,
|
||||
focusedElevation = CardConfig.cardElevation,
|
||||
hoveredElevation = CardConfig.cardElevation,
|
||||
draggedElevation = CardConfig.cardElevation,
|
||||
disabledElevation = CardConfig.cardElevation
|
||||
)
|
||||
@Composable
|
||||
fun getCardColors(originalColor: Color) = CardDefaults.cardColors(
|
||||
containerColor = originalColor.copy(alpha = CardConfig.cardAlpha),
|
||||
contentColor = determineContentColor(originalColor),
|
||||
disabledContainerColor = originalColor.copy(alpha = CardConfig.cardAlpha * 0.38f),
|
||||
disabledContentColor = determineContentColor(originalColor).copy(alpha = 0.38f)
|
||||
)
|
||||
|
||||
/**
|
||||
* 根据背景颜色、主题模式和用户设置确定内容颜色
|
||||
*/
|
||||
@Composable
|
||||
private fun determineContentColor(originalColor: Color): Color {
|
||||
val isDarkTheme = isSystemInDarkTheme()
|
||||
if (ThemeConfig.isThemeChanging) {
|
||||
return if (isDarkTheme) Color.White else Color.Black
|
||||
}
|
||||
@Composable
|
||||
fun getCardElevation() = CardDefaults.cardElevation(
|
||||
defaultElevation = CardConfig.cardElevation,
|
||||
pressedElevation = if (CardConfig.isShadowEnabled) {
|
||||
(CardConfig.cardElevation.value + 4).dp
|
||||
} else 0.dp,
|
||||
focusedElevation = if (CardConfig.isShadowEnabled) {
|
||||
(CardConfig.cardElevation.value + 6).dp
|
||||
} else 0.dp,
|
||||
hoveredElevation = if (CardConfig.isShadowEnabled) {
|
||||
(CardConfig.cardElevation.value + 2).dp
|
||||
} else 0.dp,
|
||||
draggedElevation = if (CardConfig.isShadowEnabled) {
|
||||
(CardConfig.cardElevation.value + 8).dp
|
||||
} else 0.dp,
|
||||
disabledElevation = 0.dp
|
||||
)
|
||||
|
||||
return when {
|
||||
CardConfig.isUserLightModeEnabled -> Color.Black
|
||||
!isDarkTheme && originalColor.luminance() > 0.5f -> Color.Black
|
||||
isDarkTheme -> Color.White
|
||||
else -> if (originalColor.luminance() > 0.5f) Color.Black else Color.White
|
||||
@Composable
|
||||
private fun determineContentColor(originalColor: Color): Color {
|
||||
val isDarkTheme = isSystemInDarkTheme()
|
||||
|
||||
return when {
|
||||
ThemeConfig.isThemeChanging -> {
|
||||
if (isDarkTheme) Color.White else Color.Black
|
||||
}
|
||||
CardConfig.isUserLightModeEnabled -> Color.Black
|
||||
CardConfig.isUserDarkModeEnabled -> Color.White
|
||||
else -> {
|
||||
val luminance = originalColor.luminance()
|
||||
val threshold = if (isDarkTheme) 0.4f else 0.6f
|
||||
if (luminance > threshold) Color.Black else Color.White
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向后兼容
|
||||
@Composable
|
||||
fun getCardColors(originalColor: Color) = CardStyleProvider.getCardColors(originalColor)
|
||||
|
||||
@Composable
|
||||
fun getCardElevation() = CardStyleProvider.getCardElevation()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.sukisu.ultra.ui.theme
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
@@ -9,9 +8,7 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -28,7 +25,6 @@ import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
@@ -36,28 +32,31 @@ import coil.compose.AsyncImagePainter
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.sukisu.ultra.ui.theme.util.BackgroundTransformation
|
||||
import com.sukisu.ultra.ui.theme.util.saveTransformedBackground
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* 主题配置对象,管理应用的主题相关状态
|
||||
*/
|
||||
@Stable
|
||||
object ThemeConfig {
|
||||
// 主题状态
|
||||
var customBackgroundUri by mutableStateOf<Uri?>(null)
|
||||
var forceDarkMode by mutableStateOf<Boolean?>(null)
|
||||
var currentTheme by mutableStateOf<ThemeColors>(ThemeColors.Default)
|
||||
var useDynamicColor by mutableStateOf(false)
|
||||
|
||||
// 背景状态
|
||||
var backgroundImageLoaded by mutableStateOf(false)
|
||||
var needsResetOnThemeChange by mutableStateOf(false)
|
||||
var isThemeChanging by mutableStateOf(false)
|
||||
var preventBackgroundRefresh by mutableStateOf(false)
|
||||
|
||||
// 主题变化检测
|
||||
private var lastDarkModeState: Boolean? = null
|
||||
|
||||
fun detectThemeChange(currentDarkMode: Boolean): Boolean {
|
||||
val isChanged = lastDarkModeState != null && lastDarkModeState != currentDarkMode
|
||||
val hasChanged = lastDarkModeState != null && lastDarkModeState != currentDarkMode
|
||||
lastDarkModeState = currentDarkMode
|
||||
return isChanged
|
||||
return hasChanged
|
||||
}
|
||||
|
||||
fun resetBackgroundState() {
|
||||
@@ -66,11 +65,171 @@ object ThemeConfig {
|
||||
}
|
||||
isThemeChanging = true
|
||||
}
|
||||
|
||||
fun updateTheme(
|
||||
theme: ThemeColors? = null,
|
||||
dynamicColor: Boolean? = null,
|
||||
darkMode: Boolean? = null
|
||||
) {
|
||||
theme?.let { currentTheme = it }
|
||||
dynamicColor?.let { useDynamicColor = it }
|
||||
darkMode?.let { forceDarkMode = it }
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
customBackgroundUri = null
|
||||
forceDarkMode = null
|
||||
currentTheme = ThemeColors.Default
|
||||
useDynamicColor = false
|
||||
backgroundImageLoaded = false
|
||||
isThemeChanging = false
|
||||
preventBackgroundRefresh = false
|
||||
lastDarkModeState = null
|
||||
}
|
||||
}
|
||||
|
||||
object ThemeManager {
|
||||
private const val PREFS_NAME = "theme_prefs"
|
||||
|
||||
fun saveThemeMode(context: Context, forceDark: Boolean?) {
|
||||
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit {
|
||||
putString("theme_mode", when (forceDark) {
|
||||
true -> "dark"
|
||||
false -> "light"
|
||||
null -> "system"
|
||||
})
|
||||
}
|
||||
ThemeConfig.forceDarkMode = forceDark
|
||||
}
|
||||
|
||||
fun loadThemeMode(context: Context) {
|
||||
val mode = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
.getString("theme_mode", "system")
|
||||
|
||||
ThemeConfig.forceDarkMode = when (mode) {
|
||||
"dark" -> true
|
||||
"light" -> false
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun saveThemeColors(context: Context, themeName: String) {
|
||||
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit {
|
||||
putString("theme_colors", themeName)
|
||||
}
|
||||
ThemeConfig.currentTheme = ThemeColors.fromName(themeName)
|
||||
}
|
||||
|
||||
fun loadThemeColors(context: Context) {
|
||||
val themeName = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
.getString("theme_colors", "default") ?: "default"
|
||||
ThemeConfig.currentTheme = ThemeColors.fromName(themeName)
|
||||
}
|
||||
|
||||
fun saveDynamicColorState(context: Context, enabled: Boolean) {
|
||||
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit {
|
||||
putBoolean("use_dynamic_color", enabled)
|
||||
}
|
||||
ThemeConfig.useDynamicColor = enabled
|
||||
}
|
||||
|
||||
|
||||
fun loadDynamicColorState(context: Context) {
|
||||
val enabled = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean("use_dynamic_color", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
ThemeConfig.useDynamicColor = enabled
|
||||
}
|
||||
}
|
||||
|
||||
object BackgroundManager {
|
||||
private const val TAG = "BackgroundManager"
|
||||
|
||||
fun saveAndApplyCustomBackground(
|
||||
context: Context,
|
||||
uri: Uri,
|
||||
transformation: BackgroundTransformation? = null
|
||||
) {
|
||||
try {
|
||||
val finalUri = if (transformation != null) {
|
||||
context.saveTransformedBackground(uri, transformation)
|
||||
} else {
|
||||
copyImageToInternalStorage(context, uri)
|
||||
}
|
||||
|
||||
saveBackgroundUri(context, finalUri)
|
||||
ThemeConfig.customBackgroundUri = finalUri
|
||||
CardConfig.updateBackground(true)
|
||||
resetBackgroundState(context)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "保存背景失败: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearCustomBackground(context: Context) {
|
||||
saveBackgroundUri(context, null)
|
||||
ThemeConfig.customBackgroundUri = null
|
||||
CardConfig.updateBackground(false)
|
||||
resetBackgroundState(context)
|
||||
}
|
||||
|
||||
fun loadCustomBackground(context: Context) {
|
||||
val uriString = context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.getString("custom_background", null)
|
||||
|
||||
val newUri = uriString?.toUri()
|
||||
val preventRefresh = context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.getBoolean("prevent_background_refresh", false)
|
||||
|
||||
ThemeConfig.preventBackgroundRefresh = preventRefresh
|
||||
|
||||
if (!preventRefresh || ThemeConfig.customBackgroundUri?.toString() != newUri?.toString()) {
|
||||
Log.d(TAG, "加载自定义背景: $uriString")
|
||||
ThemeConfig.customBackgroundUri = newUri
|
||||
ThemeConfig.backgroundImageLoaded = false
|
||||
CardConfig.updateBackground(newUri != null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveBackgroundUri(context: Context, uri: Uri?) {
|
||||
context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE).edit {
|
||||
putString("custom_background", uri?.toString())
|
||||
putBoolean("prevent_background_refresh", false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetBackgroundState(context: Context) {
|
||||
ThemeConfig.backgroundImageLoaded = false
|
||||
ThemeConfig.preventBackgroundRefresh = false
|
||||
context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE).edit {
|
||||
putBoolean("prevent_background_refresh", false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyImageToInternalStorage(context: Context, uri: Uri): Uri? {
|
||||
return try {
|
||||
val inputStream = context.contentResolver.openInputStream(uri) ?: return null
|
||||
val fileName = "custom_background_${System.currentTimeMillis()}.jpg"
|
||||
val file = File(context.filesDir, fileName)
|
||||
|
||||
FileOutputStream(file).use { outputStream ->
|
||||
val buffer = ByteArray(8 * 1024)
|
||||
var read: Int
|
||||
while (inputStream.read(buffer).also { read = it } != -1) {
|
||||
outputStream.write(buffer, 0, read)
|
||||
}
|
||||
outputStream.flush()
|
||||
}
|
||||
inputStream.close()
|
||||
|
||||
Uri.fromFile(file)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "复制图片失败: ${e.message}", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用主题
|
||||
*/
|
||||
@Composable
|
||||
fun KernelSUTheme(
|
||||
darkTheme: Boolean = when(ThemeConfig.forceDarkMode) {
|
||||
@@ -84,198 +243,223 @@ fun KernelSUTheme(
|
||||
val context = LocalContext.current
|
||||
val systemIsDark = isSystemInDarkTheme()
|
||||
|
||||
// 检测系统主题变化并保存状态
|
||||
val themeChanged = ThemeConfig.detectThemeChange(systemIsDark)
|
||||
LaunchedEffect(systemIsDark, themeChanged) {
|
||||
if (ThemeConfig.forceDarkMode == null && themeChanged) {
|
||||
Log.d("ThemeSystem", "系统主题变化检测: 从 ${!systemIsDark} 变为 $systemIsDark")
|
||||
ThemeConfig.resetBackgroundState()
|
||||
|
||||
if (!ThemeConfig.preventBackgroundRefresh) {
|
||||
context.loadCustomBackground()
|
||||
}
|
||||
|
||||
CardConfig.apply {
|
||||
load(context)
|
||||
if (!isCustomAlphaSet) {
|
||||
cardAlpha = if (systemIsDark) 0.50f else 1f
|
||||
}
|
||||
if (!isCustomDimSet) {
|
||||
cardDim = if (systemIsDark) 0.5f else 0f
|
||||
}
|
||||
save(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SystemBarStyle(
|
||||
darkMode = darkTheme
|
||||
)
|
||||
|
||||
// 初始加载配置
|
||||
LaunchedEffect(Unit) {
|
||||
context.loadThemeMode()
|
||||
context.loadThemeColors()
|
||||
context.loadDynamicColorState()
|
||||
CardConfig.load(context)
|
||||
|
||||
if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) {
|
||||
context.loadCustomBackground()
|
||||
ThemeConfig.backgroundImageLoaded = false
|
||||
}
|
||||
|
||||
ThemeConfig.preventBackgroundRefresh = context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.getBoolean("prevent_background_refresh", true)
|
||||
}
|
||||
// 初始化主题
|
||||
ThemeInitializer(context = context, systemIsDark = systemIsDark)
|
||||
|
||||
// 创建颜色方案
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
if (darkTheme) createDynamicDarkColorScheme(context) else createDynamicLightColorScheme(context)
|
||||
}
|
||||
darkTheme -> createDarkColorScheme()
|
||||
else -> createLightColorScheme()
|
||||
}
|
||||
val colorScheme = createColorScheme(context, darkTheme, dynamicColor)
|
||||
|
||||
// 根据暗色模式和自定义背景调整卡片配置
|
||||
val isDarkModeWithCustomBackground = darkTheme && ThemeConfig.customBackgroundUri != null
|
||||
if (darkTheme && !dynamicColor) {
|
||||
CardConfig.setThemeDefaults(true)
|
||||
} else if (!darkTheme && !dynamicColor) {
|
||||
CardConfig.setThemeDefaults(false)
|
||||
}
|
||||
CardConfig.updateShadowEnabled(!isDarkModeWithCustomBackground)
|
||||
|
||||
val backgroundUri = rememberSaveable { mutableStateOf(ThemeConfig.customBackgroundUri) }
|
||||
|
||||
LaunchedEffect(ThemeConfig.customBackgroundUri) {
|
||||
backgroundUri.value = ThemeConfig.customBackgroundUri
|
||||
}
|
||||
|
||||
val bgImagePainter = backgroundUri.value?.let {
|
||||
rememberAsyncImagePainter(
|
||||
model = it,
|
||||
onError = { err ->
|
||||
Log.e("ThemeSystem", "背景图加载失败: ${err.result.throwable.message}")
|
||||
ThemeConfig.customBackgroundUri = null
|
||||
context.saveCustomBackground(null)
|
||||
},
|
||||
onSuccess = {
|
||||
Log.d("ThemeSystem", "背景图加载成功")
|
||||
ThemeConfig.backgroundImageLoaded = true
|
||||
ThemeConfig.isThemeChanging = false
|
||||
|
||||
ThemeConfig.preventBackgroundRefresh = true
|
||||
context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.edit { putBoolean("prevent_background_refresh", true) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val transition = updateTransition(
|
||||
targetState = ThemeConfig.backgroundImageLoaded,
|
||||
label = "bgTransition"
|
||||
)
|
||||
val bgAlpha by transition.animateFloat(
|
||||
label = "bgAlpha",
|
||||
transitionSpec = {
|
||||
spring(
|
||||
dampingRatio = 0.8f,
|
||||
stiffness = 300f
|
||||
)
|
||||
}
|
||||
) { loaded -> if (loaded) 1f else 0f }
|
||||
|
||||
DisposableEffect(systemIsDark) {
|
||||
onDispose {
|
||||
if (ThemeConfig.isThemeChanging) {
|
||||
ThemeConfig.isThemeChanging = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算适用的暗化值
|
||||
val dimFactor = CardConfig.cardDim
|
||||
// 系统栏样式
|
||||
SystemBarController(darkTheme)
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(-2f)
|
||||
.background(if (darkTheme) if (CardConfig.isCustomBackgroundEnabled) { colorScheme.surfaceContainerLow } else { colorScheme.background }
|
||||
else if (CardConfig.isCustomBackgroundEnabled) { colorScheme.surfaceContainerLow } else { colorScheme.background })
|
||||
)
|
||||
|
||||
// 自定义背景层
|
||||
backgroundUri.value?.let {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(-1f)
|
||||
.alpha(bgAlpha)
|
||||
) {
|
||||
// 背景图片
|
||||
bgImagePainter?.let { painter ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.paint(
|
||||
painter = painter,
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
.graphicsLayer {
|
||||
alpha = (painter.state as? AsyncImagePainter.State.Success)?.let { 1f } ?: 0f
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 亮度调节层 (根据cardDim调整)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
if (darkTheme) Color.Black.copy(alpha = 0.6f + dimFactor * 0.3f)
|
||||
else Color.White.copy(alpha = 0.1f + dimFactor * 0.2f)
|
||||
)
|
||||
)
|
||||
|
||||
// 边缘渐变遮罩
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
if (darkTheme) Color.Black.copy(alpha = 0.5f + dimFactor * 0.2f)
|
||||
else Color.Black.copy(alpha = 0.2f + dimFactor * 0.1f)
|
||||
),
|
||||
radius = 1200f
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 背景层
|
||||
BackgroundLayer(darkTheme)
|
||||
// 内容层
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(1f)
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize().zIndex(1f)) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建动态深色颜色方案
|
||||
*/
|
||||
@Composable
|
||||
private fun ThemeInitializer(context: Context, systemIsDark: Boolean) {
|
||||
val themeChanged = ThemeConfig.detectThemeChange(systemIsDark)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
// 处理系统主题变化
|
||||
LaunchedEffect(systemIsDark, themeChanged) {
|
||||
if (ThemeConfig.forceDarkMode == null && themeChanged) {
|
||||
Log.d("ThemeSystem", "系统主题变化: $systemIsDark")
|
||||
ThemeConfig.resetBackgroundState()
|
||||
|
||||
if (!ThemeConfig.preventBackgroundRefresh) {
|
||||
BackgroundManager.loadCustomBackground(context)
|
||||
}
|
||||
|
||||
CardConfig.apply {
|
||||
load(context)
|
||||
setThemeDefaults(systemIsDark)
|
||||
save(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始加载配置
|
||||
LaunchedEffect(Unit) {
|
||||
scope.launch {
|
||||
ThemeManager.loadThemeMode(context)
|
||||
ThemeManager.loadThemeColors(context)
|
||||
ThemeManager.loadDynamicColorState(context)
|
||||
CardConfig.load(context)
|
||||
|
||||
if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) {
|
||||
BackgroundManager.loadCustomBackground(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BackgroundLayer(darkTheme: Boolean) {
|
||||
val backgroundUri = rememberSaveable { mutableStateOf(ThemeConfig.customBackgroundUri) }
|
||||
|
||||
LaunchedEffect(ThemeConfig.customBackgroundUri) {
|
||||
backgroundUri.value = ThemeConfig.customBackgroundUri
|
||||
}
|
||||
|
||||
// 默认背景
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(-2f)
|
||||
.background(
|
||||
if (CardConfig.isCustomBackgroundEnabled) {
|
||||
MaterialTheme.colorScheme.surfaceContainerLow
|
||||
} else {
|
||||
MaterialTheme.colorScheme.background
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// 自定义背景
|
||||
backgroundUri.value?.let { uri ->
|
||||
CustomBackgroundLayer(uri = uri, darkTheme = darkTheme)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CustomBackgroundLayer(uri: Uri, darkTheme: Boolean) {
|
||||
val painter = rememberAsyncImagePainter(
|
||||
model = uri,
|
||||
onError = { error ->
|
||||
Log.e("ThemeSystem", "背景加载失败: ${error.result.throwable.message}")
|
||||
ThemeConfig.customBackgroundUri = null
|
||||
},
|
||||
onSuccess = {
|
||||
Log.d("ThemeSystem", "背景加载成功")
|
||||
ThemeConfig.backgroundImageLoaded = true
|
||||
ThemeConfig.isThemeChanging = false
|
||||
}
|
||||
)
|
||||
|
||||
val transition = updateTransition(
|
||||
targetState = ThemeConfig.backgroundImageLoaded,
|
||||
label = "backgroundTransition"
|
||||
)
|
||||
|
||||
val alpha by transition.animateFloat(
|
||||
label = "backgroundAlpha",
|
||||
transitionSpec = {
|
||||
spring(
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
stiffness = Spring.StiffnessMedium
|
||||
)
|
||||
}
|
||||
) { loaded -> if (loaded) 1f else 0f }
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(-1f)
|
||||
.alpha(alpha)
|
||||
) {
|
||||
// 背景图片
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.paint(painter = painter, contentScale = ContentScale.Crop)
|
||||
.graphicsLayer {
|
||||
this.alpha = (painter.state as? AsyncImagePainter.State.Success)?.let { 1f } ?: 0f
|
||||
}
|
||||
)
|
||||
|
||||
// 遮罩层
|
||||
BackgroundOverlay(darkTheme = darkTheme)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BackgroundOverlay(darkTheme: Boolean) {
|
||||
val dimFactor = CardConfig.cardDim
|
||||
|
||||
// 主要遮罩层
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
if (darkTheme) {
|
||||
Color.Black.copy(alpha = 0.3f + dimFactor * 0.4f)
|
||||
} else {
|
||||
Color.White.copy(alpha = 0.05f + dimFactor * 0.3f)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// 边缘渐变遮罩
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
if (darkTheme) {
|
||||
Color.Black.copy(alpha = 0.2f + dimFactor * 0.2f)
|
||||
} else {
|
||||
Color.Black.copy(alpha = 0.05f + dimFactor * 0.1f)
|
||||
}
|
||||
),
|
||||
radius = 1000f
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun createColorScheme(
|
||||
context: Context,
|
||||
darkTheme: Boolean,
|
||||
dynamicColor: Boolean
|
||||
): ColorScheme {
|
||||
return when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
if (darkTheme) createDynamicDarkColorScheme(context)
|
||||
else createDynamicLightColorScheme(context)
|
||||
}
|
||||
darkTheme -> createDarkColorScheme()
|
||||
else -> createLightColorScheme()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SystemBarController(darkMode: Boolean) {
|
||||
val context = LocalContext.current
|
||||
val activity = context as ComponentActivity
|
||||
|
||||
SideEffect {
|
||||
activity.enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.auto(
|
||||
Color.Transparent.toArgb(),
|
||||
Color.Transparent.toArgb(),
|
||||
) { darkMode },
|
||||
navigationBarStyle = if (darkMode) {
|
||||
SystemBarStyle.dark(Color.Transparent.toArgb())
|
||||
} else {
|
||||
SystemBarStyle.light(
|
||||
Color.Transparent.toArgb(),
|
||||
Color.Transparent.toArgb()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
@Composable
|
||||
private fun createDynamicDarkColorScheme(context: Context): ColorScheme {
|
||||
@@ -288,9 +472,6 @@ private fun createDynamicDarkColorScheme(context: Context): ColorScheme {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建动态浅色颜色方案
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
@Composable
|
||||
private fun createDynamicLightColorScheme(context: Context): ColorScheme {
|
||||
@@ -303,11 +484,6 @@ private fun createDynamicLightColorScheme(context: Context): ColorScheme {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 创建深色颜色方案
|
||||
*/
|
||||
@Composable
|
||||
private fun createDarkColorScheme() = darkColorScheme(
|
||||
primary = ThemeConfig.currentTheme.primaryDark,
|
||||
@@ -347,9 +523,6 @@ private fun createDarkColorScheme() = darkColorScheme(
|
||||
surfaceContainerHighest = ThemeConfig.currentTheme.surfaceContainerHighestDark,
|
||||
)
|
||||
|
||||
/**
|
||||
* 创建浅色颜色方案
|
||||
*/
|
||||
@Composable
|
||||
private fun createLightColorScheme() = lightColorScheme(
|
||||
primary = ThemeConfig.currentTheme.primaryLight,
|
||||
@@ -389,218 +562,32 @@ private fun createLightColorScheme() = lightColorScheme(
|
||||
surfaceContainerHighest = ThemeConfig.currentTheme.surfaceContainerHighestLight,
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* 复制图片到应用内部存储并提升持久性
|
||||
*/
|
||||
private fun Context.copyImageToInternalStorage(uri: Uri): Uri? {
|
||||
return try {
|
||||
val contentResolver: ContentResolver = contentResolver
|
||||
val inputStream: InputStream = contentResolver.openInputStream(uri) ?: return null
|
||||
|
||||
val fileName = "custom_background.jpg"
|
||||
val file = File(filesDir, fileName)
|
||||
|
||||
val backupFile = File(filesDir, "${fileName}.backup")
|
||||
val outputStream = FileOutputStream(backupFile)
|
||||
val buffer = ByteArray(4 * 1024)
|
||||
var read: Int
|
||||
|
||||
while (inputStream.read(buffer).also { read = it } != -1) {
|
||||
outputStream.write(buffer, 0, read)
|
||||
}
|
||||
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
inputStream.close()
|
||||
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
backupFile.renameTo(file)
|
||||
|
||||
Uri.fromFile(file)
|
||||
} catch (e: Exception) {
|
||||
Log.e("ImageCopy", "复制图片失败: ${e.message}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存并应用自定义背景
|
||||
*/
|
||||
// 向后兼容
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun Context.saveAndApplyCustomBackground(uri: Uri, transformation: BackgroundTransformation? = null) {
|
||||
val finalUri = if (transformation != null) {
|
||||
saveTransformedBackground(uri, transformation)
|
||||
} else {
|
||||
copyImageToInternalStorage(uri)
|
||||
kotlinx.coroutines.GlobalScope.launch {
|
||||
BackgroundManager.saveAndApplyCustomBackground(this@saveAndApplyCustomBackground, uri, transformation)
|
||||
}
|
||||
|
||||
// 保存到配置文件
|
||||
getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.edit {
|
||||
putString("custom_background", finalUri?.toString())
|
||||
// 设置阻止刷新标志为false,允许新设置的背景加载一次
|
||||
putBoolean("prevent_background_refresh", false)
|
||||
}
|
||||
|
||||
ThemeConfig.customBackgroundUri = finalUri
|
||||
ThemeConfig.backgroundImageLoaded = false
|
||||
ThemeConfig.preventBackgroundRefresh = false
|
||||
CardConfig.cardElevation = 0.dp
|
||||
CardConfig.isCustomBackgroundEnabled = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自定义背景
|
||||
*/
|
||||
fun Context.saveCustomBackground(uri: Uri?) {
|
||||
val newUri = uri?.let { copyImageToInternalStorage(it) }
|
||||
|
||||
// 保存到配置文件
|
||||
getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.edit {
|
||||
putString("custom_background", newUri?.toString())
|
||||
if (uri == null) {
|
||||
// 如果清除背景,也重置阻止刷新标志
|
||||
putBoolean("prevent_background_refresh", false)
|
||||
} else {
|
||||
// 设置阻止刷新标志为false,允许新设置的背景加载一次
|
||||
putBoolean("prevent_background_refresh", false)
|
||||
}
|
||||
}
|
||||
|
||||
ThemeConfig.customBackgroundUri = newUri
|
||||
ThemeConfig.backgroundImageLoaded = false
|
||||
ThemeConfig.preventBackgroundRefresh = false
|
||||
|
||||
if (uri != null) {
|
||||
CardConfig.cardElevation = 0.dp
|
||||
CardConfig.isCustomBackgroundEnabled = true
|
||||
saveAndApplyCustomBackground(uri)
|
||||
} else {
|
||||
BackgroundManager.clearCustomBackground(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载自定义背景
|
||||
*/
|
||||
fun Context.loadCustomBackground() {
|
||||
val uriString = getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.getString("custom_background", null)
|
||||
|
||||
val newUri = uriString?.toUri()
|
||||
val preventRefresh = getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.getBoolean("prevent_background_refresh", false)
|
||||
|
||||
ThemeConfig.preventBackgroundRefresh = preventRefresh
|
||||
|
||||
if (!preventRefresh || ThemeConfig.customBackgroundUri?.toString() != newUri?.toString()) {
|
||||
Log.d("ThemeSystem", "加载自定义背景: $uriString, 阻止刷新: $preventRefresh")
|
||||
ThemeConfig.customBackgroundUri = newUri
|
||||
ThemeConfig.backgroundImageLoaded = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存主题模式
|
||||
*/
|
||||
fun Context.saveThemeMode(forceDark: Boolean?) {
|
||||
getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.edit {
|
||||
putString(
|
||||
"theme_mode", when (forceDark) {
|
||||
true -> "dark"
|
||||
false -> "light"
|
||||
null -> "system"
|
||||
}
|
||||
)
|
||||
}
|
||||
ThemeConfig.forceDarkMode = forceDark
|
||||
ThemeConfig.needsResetOnThemeChange = forceDark == null
|
||||
ThemeManager.saveThemeMode(this, forceDark)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载主题模式
|
||||
*/
|
||||
fun Context.loadThemeMode() {
|
||||
val mode = getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.getString("theme_mode", "system")
|
||||
|
||||
ThemeConfig.forceDarkMode = when(mode) {
|
||||
"dark" -> true
|
||||
"light" -> false
|
||||
else -> null
|
||||
}
|
||||
ThemeConfig.needsResetOnThemeChange = ThemeConfig.forceDarkMode == null
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存主题颜色
|
||||
*/
|
||||
fun Context.saveThemeColors(themeName: String) {
|
||||
getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.edit {
|
||||
putString("theme_colors", themeName)
|
||||
}
|
||||
|
||||
ThemeConfig.currentTheme = ThemeColors.fromName(themeName)
|
||||
ThemeManager.saveThemeColors(this, themeName)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载主题颜色
|
||||
*/
|
||||
fun Context.loadThemeColors() {
|
||||
val themeName = getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.getString("theme_colors", "default")
|
||||
|
||||
ThemeConfig.currentTheme = ThemeColors.fromName(themeName ?: "default")
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存动态颜色状态
|
||||
*/
|
||||
fun Context.saveDynamicColorState(enabled: Boolean) {
|
||||
getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.edit {
|
||||
putBoolean("use_dynamic_color", enabled)
|
||||
}
|
||||
ThemeConfig.useDynamicColor = enabled
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载动态颜色状态
|
||||
*/
|
||||
fun Context.loadDynamicColorState() {
|
||||
val enabled = getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
|
||||
.getBoolean("use_dynamic_color", true)
|
||||
|
||||
ThemeConfig.useDynamicColor = enabled
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SystemBarStyle(
|
||||
darkMode: Boolean,
|
||||
statusBarScrim: Color = Color.Transparent,
|
||||
navigationBarScrim: Color = Color.Transparent,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val activity = context as ComponentActivity
|
||||
|
||||
SideEffect {
|
||||
activity.enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.auto(
|
||||
statusBarScrim.toArgb(),
|
||||
statusBarScrim.toArgb(),
|
||||
) { darkMode },
|
||||
navigationBarStyle = when {
|
||||
darkMode -> SystemBarStyle.dark(
|
||||
navigationBarScrim.toArgb()
|
||||
)
|
||||
|
||||
else -> SystemBarStyle.light(
|
||||
navigationBarScrim.toArgb(),
|
||||
navigationBarScrim.toArgb(),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
ThemeManager.saveDynamicColorState(this, enabled)
|
||||
}
|
||||
@@ -1,24 +1,23 @@
|
||||
package com.sukisu.ultra.ui.theme.component
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTransformGestures
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Fullscreen
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
@@ -44,48 +43,62 @@ fun ImageEditorDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: (Uri) -> Unit
|
||||
) {
|
||||
var scale by remember { mutableFloatStateOf(1f) }
|
||||
var offsetX by remember { mutableFloatStateOf(0f) }
|
||||
var offsetY by remember { mutableFloatStateOf(0f) }
|
||||
// 图像变换状态
|
||||
val transformState = remember { ImageTransformState() }
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
var lastScale by remember { mutableFloatStateOf(1f) }
|
||||
var lastOffsetX by remember { mutableFloatStateOf(0f) }
|
||||
var lastOffsetY by remember { mutableFloatStateOf(0f) }
|
||||
|
||||
// 尺寸状态
|
||||
var imageSize by remember { mutableStateOf(Size.Zero) }
|
||||
var screenSize by remember { mutableStateOf(Size.Zero) }
|
||||
|
||||
// 动画状态
|
||||
val animationSpec = spring<Float>(
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
stiffness = Spring.StiffnessMedium
|
||||
)
|
||||
|
||||
val animatedScale by animateFloatAsState(
|
||||
targetValue = scale,
|
||||
targetValue = transformState.scale,
|
||||
animationSpec = animationSpec,
|
||||
label = "ScaleAnimation"
|
||||
)
|
||||
|
||||
val animatedOffsetX by animateFloatAsState(
|
||||
targetValue = offsetX,
|
||||
targetValue = transformState.offsetX,
|
||||
animationSpec = animationSpec,
|
||||
label = "OffsetXAnimation"
|
||||
)
|
||||
|
||||
val animatedOffsetY by animateFloatAsState(
|
||||
targetValue = offsetY,
|
||||
targetValue = transformState.offsetY,
|
||||
animationSpec = animationSpec,
|
||||
label = "OffsetYAnimation"
|
||||
)
|
||||
val updateTransformation = remember {
|
||||
{ newScale: Float, newOffsetX: Float, newOffsetY: Float ->
|
||||
val scaleDiff = abs(newScale - lastScale)
|
||||
val offsetXDiff = abs(newOffsetX - lastOffsetX)
|
||||
val offsetYDiff = abs(newOffsetY - lastOffsetY)
|
||||
if (scaleDiff > 0.01f || offsetXDiff > 1f || offsetYDiff > 1f) {
|
||||
scale = newScale
|
||||
offsetX = newOffsetX
|
||||
offsetY = newOffsetY
|
||||
lastScale = newScale
|
||||
lastOffsetX = newOffsetX
|
||||
lastOffsetY = newOffsetY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
val scaleToFullScreen = remember {
|
||||
{
|
||||
if (imageSize.height > 0 && screenSize.height > 0) {
|
||||
val newScale = screenSize.height / imageSize.height
|
||||
updateTransformation(newScale, 0f, 0f)
|
||||
transformState.updateTransform(newScale, 0f, 0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val saveImage: () -> Unit = remember {
|
||||
{
|
||||
scope.launch {
|
||||
try {
|
||||
val transformation = BackgroundTransformation(
|
||||
transformState.scale,
|
||||
transformState.offsetX,
|
||||
transformState.offsetY
|
||||
)
|
||||
val savedUri = context.saveTransformedBackground(imageUri, transformation)
|
||||
savedUri?.let { onConfirm(it) }
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,124 +114,298 @@ fun ImageEditorDialog(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black.copy(alpha = 0.9f))
|
||||
.background(
|
||||
Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color.Black.copy(alpha = 0.9f),
|
||||
Color.Black.copy(alpha = 0.95f)
|
||||
),
|
||||
radius = 800f
|
||||
)
|
||||
)
|
||||
.onSizeChanged { size ->
|
||||
screenSize = Size(size.width.toFloat(), size.height.toFloat())
|
||||
}
|
||||
) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(imageUri)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = stringResource(R.string.settings_custom_background),
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer(
|
||||
scaleX = animatedScale,
|
||||
scaleY = animatedScale,
|
||||
translationX = animatedOffsetX,
|
||||
translationY = animatedOffsetY
|
||||
)
|
||||
.pointerInput(Unit) {
|
||||
detectTransformGestures { _, pan, zoom, _ ->
|
||||
scope.launch {
|
||||
try {
|
||||
val newScale = (scale * zoom).coerceIn(0.5f, 3f)
|
||||
val maxOffsetX = max(0f, size.width * (newScale - 1) / 2)
|
||||
val maxOffsetY = max(0f, size.height * (newScale - 1) / 2)
|
||||
val newOffsetX = if (maxOffsetX > 0) {
|
||||
(offsetX + pan.x).coerceIn(-maxOffsetX, maxOffsetX)
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
val newOffsetY = if (maxOffsetY > 0) {
|
||||
(offsetY + pan.y).coerceIn(-maxOffsetY, maxOffsetY)
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
updateTransformation(newScale, newOffsetX, newOffsetY)
|
||||
} catch (_: Exception) {
|
||||
updateTransformation(lastScale, lastOffsetX, lastOffsetY)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onSizeChanged { size ->
|
||||
imageSize = Size(size.width.toFloat(), size.height.toFloat())
|
||||
}
|
||||
// 图像显示区域
|
||||
ImageDisplayArea(
|
||||
imageUri = imageUri,
|
||||
animatedScale = animatedScale,
|
||||
animatedOffsetX = animatedOffsetX,
|
||||
animatedOffsetY = animatedOffsetY,
|
||||
transformState = transformState,
|
||||
onImageSizeChanged = { imageSize = it },
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.align(Alignment.TopCenter),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(Color.Black.copy(alpha = 0.6f))
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.cancel),
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { scaleToFullScreen() },
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(Color.Black.copy(alpha = 0.6f))
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Fullscreen,
|
||||
contentDescription = stringResource(R.string.reprovision),
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
try {
|
||||
val transformation = BackgroundTransformation(scale, offsetX, offsetY)
|
||||
val savedUri = context.saveTransformedBackground(imageUri, transformation)
|
||||
savedUri?.let { onConfirm(it) }
|
||||
} catch (_: Exception) {
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(Color.Black.copy(alpha = 0.6f))
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = stringResource(R.string.confirm),
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(Color.Black.copy(alpha = 0.6f))
|
||||
.padding(16.dp)
|
||||
.align(Alignment.BottomCenter)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.image_editor_hint),
|
||||
color = Color.White,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
// 顶部工具栏
|
||||
TopToolbar(
|
||||
onDismiss = onDismiss,
|
||||
onFullscreen = scaleToFullScreen,
|
||||
onConfirm = saveImage,
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
|
||||
// 底部提示信息
|
||||
BottomHintCard(
|
||||
modifier = Modifier.align(Alignment.BottomCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像变换状态管理类
|
||||
*/
|
||||
private class ImageTransformState {
|
||||
var scale by mutableFloatStateOf(1f)
|
||||
var offsetX by mutableFloatStateOf(0f)
|
||||
var offsetY by mutableFloatStateOf(0f)
|
||||
|
||||
private var lastScale = 1f
|
||||
private var lastOffsetX = 0f
|
||||
private var lastOffsetY = 0f
|
||||
|
||||
fun updateTransform(newScale: Float, newOffsetX: Float, newOffsetY: Float) {
|
||||
val scaleDiff = abs(newScale - lastScale)
|
||||
val offsetXDiff = abs(newOffsetX - lastOffsetX)
|
||||
val offsetYDiff = abs(newOffsetY - lastOffsetY)
|
||||
|
||||
if (scaleDiff > 0.01f || offsetXDiff > 1f || offsetYDiff > 1f) {
|
||||
scale = newScale
|
||||
offsetX = newOffsetX
|
||||
offsetY = newOffsetY
|
||||
lastScale = newScale
|
||||
lastOffsetX = newOffsetX
|
||||
lastOffsetY = newOffsetY
|
||||
}
|
||||
}
|
||||
|
||||
fun resetToLast() {
|
||||
scale = lastScale
|
||||
offsetX = lastOffsetX
|
||||
offsetY = lastOffsetY
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像显示区域组件
|
||||
*/
|
||||
@Composable
|
||||
private fun ImageDisplayArea(
|
||||
imageUri: Uri,
|
||||
animatedScale: Float,
|
||||
animatedOffsetX: Float,
|
||||
animatedOffsetY: Float,
|
||||
transformState: ImageTransformState,
|
||||
onImageSizeChanged: (Size) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(imageUri)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = stringResource(R.string.settings_custom_background),
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = modifier
|
||||
.graphicsLayer(
|
||||
scaleX = animatedScale,
|
||||
scaleY = animatedScale,
|
||||
translationX = animatedOffsetX,
|
||||
translationY = animatedOffsetY
|
||||
)
|
||||
.pointerInput(Unit) {
|
||||
detectTransformGestures { _, pan, zoom, _ ->
|
||||
scope.launch {
|
||||
try {
|
||||
val newScale = (transformState.scale * zoom).coerceIn(0.5f, 3f)
|
||||
val maxOffsetX = max(0f, size.width * (newScale - 1) / 2)
|
||||
val maxOffsetY = max(0f, size.height * (newScale - 1) / 2)
|
||||
|
||||
val newOffsetX = if (maxOffsetX > 0) {
|
||||
(transformState.offsetX + pan.x).coerceIn(-maxOffsetX, maxOffsetX)
|
||||
} else 0f
|
||||
|
||||
val newOffsetY = if (maxOffsetY > 0) {
|
||||
(transformState.offsetY + pan.y).coerceIn(-maxOffsetY, maxOffsetY)
|
||||
} else 0f
|
||||
|
||||
transformState.updateTransform(newScale, newOffsetX, newOffsetY)
|
||||
} catch (_: Exception) {
|
||||
transformState.resetToLast()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onSizeChanged { size ->
|
||||
onImageSizeChanged(Size(size.width.toFloat(), size.height.toFloat()))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶部工具栏组件
|
||||
*/
|
||||
@Composable
|
||||
private fun TopToolbar(
|
||||
onDismiss: () -> Unit,
|
||||
onFullscreen: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
// 关闭按钮
|
||||
ActionButton(
|
||||
onClick = onDismiss,
|
||||
icon = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.cancel),
|
||||
backgroundColor = MaterialTheme.colorScheme.error.copy(alpha = 0.9f)
|
||||
)
|
||||
|
||||
// 全屏按钮
|
||||
ActionButton(
|
||||
onClick = onFullscreen,
|
||||
icon = Icons.Default.Fullscreen,
|
||||
contentDescription = stringResource(R.string.reprovision),
|
||||
backgroundColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.9f)
|
||||
)
|
||||
|
||||
// 确认按钮
|
||||
ActionButton(
|
||||
onClick = onConfirm,
|
||||
icon = Icons.Default.Check,
|
||||
contentDescription = stringResource(R.string.confirm),
|
||||
backgroundColor = Color(0xFF4CAF50).copy(alpha = 0.9f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作按钮组件
|
||||
*/
|
||||
@Composable
|
||||
private fun ActionButton(
|
||||
onClick: () -> Unit,
|
||||
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
contentDescription: String,
|
||||
backgroundColor: Color,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var isPressed by remember { mutableStateOf(false) }
|
||||
|
||||
val buttonScale by animateFloatAsState(
|
||||
targetValue = if (isPressed) 0.85f else 1f,
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
stiffness = Spring.StiffnessHigh
|
||||
),
|
||||
label = "ButtonScale"
|
||||
)
|
||||
|
||||
val buttonAlpha by animateFloatAsState(
|
||||
targetValue = if (isPressed) 0.8f else 1f,
|
||||
animationSpec = tween(100),
|
||||
label = "ButtonAlpha"
|
||||
)
|
||||
|
||||
Surface(
|
||||
onClick = {
|
||||
isPressed = true
|
||||
onClick()
|
||||
},
|
||||
modifier = modifier
|
||||
.size(64.dp)
|
||||
.graphicsLayer(
|
||||
scaleX = buttonScale,
|
||||
scaleY = buttonScale,
|
||||
alpha = buttonAlpha
|
||||
),
|
||||
shape = CircleShape,
|
||||
color = backgroundColor,
|
||||
shadowElevation = 8.dp
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(28.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(isPressed) {
|
||||
if (isPressed) {
|
||||
kotlinx.coroutines.delay(150)
|
||||
isPressed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 底部提示卡片组件
|
||||
*/
|
||||
@Composable
|
||||
private fun BottomHintCard(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var isVisible by remember { mutableStateOf(true) }
|
||||
|
||||
val cardAlpha by animateFloatAsState(
|
||||
targetValue = if (isVisible) 1f else 0f,
|
||||
animationSpec = tween(
|
||||
durationMillis = 500,
|
||||
easing = EaseInOutCubic
|
||||
),
|
||||
label = "HintAlpha"
|
||||
)
|
||||
|
||||
val cardTranslationY by animateFloatAsState(
|
||||
targetValue = if (isVisible) 0f else 100f,
|
||||
animationSpec = tween(
|
||||
durationMillis = 500,
|
||||
easing = EaseInOutCubic
|
||||
),
|
||||
label = "HintTranslation"
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
kotlinx.coroutines.delay(4000)
|
||||
isVisible = false
|
||||
}
|
||||
|
||||
Card(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp)
|
||||
.alpha(cardAlpha)
|
||||
.graphicsLayer {
|
||||
translationY = cardTranslationY
|
||||
},
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = Color.Black.copy(alpha = 0.85f)
|
||||
),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 12.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.image_editor_hint),
|
||||
color = Color.White,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.padding(20.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,26 +105,24 @@ class MoreSettingsHandlers(
|
||||
else -> null
|
||||
}
|
||||
context.saveThemeMode(newThemeMode)
|
||||
ThemeConfig.updateTheme(darkMode = newThemeMode)
|
||||
|
||||
when (index) {
|
||||
2 -> { // 深色
|
||||
ThemeConfig.forceDarkMode = true
|
||||
CardConfig.isUserDarkModeEnabled = true
|
||||
CardConfig.isUserLightModeEnabled = false
|
||||
ThemeConfig.updateTheme(darkMode = true)
|
||||
CardConfig.updateThemePreference(darkMode = true, lightMode = false)
|
||||
CardConfig.setThemeDefaults(true)
|
||||
CardConfig.save(context)
|
||||
}
|
||||
1 -> { // 浅色
|
||||
ThemeConfig.forceDarkMode = false
|
||||
CardConfig.isUserLightModeEnabled = true
|
||||
CardConfig.isUserDarkModeEnabled = false
|
||||
ThemeConfig.updateTheme(darkMode = false)
|
||||
CardConfig.updateThemePreference(darkMode = false, lightMode = true)
|
||||
CardConfig.setThemeDefaults(false)
|
||||
CardConfig.save(context)
|
||||
}
|
||||
0 -> { // 跟随系统
|
||||
ThemeConfig.forceDarkMode = null
|
||||
CardConfig.isUserLightModeEnabled = false
|
||||
CardConfig.isUserDarkModeEnabled = false
|
||||
ThemeConfig.updateTheme(darkMode = null)
|
||||
CardConfig.updateThemePreference(darkMode = null, lightMode = null)
|
||||
val isNightModeActive = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
||||
CardConfig.setThemeDefaults(isNightModeActive)
|
||||
CardConfig.save(context)
|
||||
@@ -145,6 +143,7 @@ class MoreSettingsHandlers(
|
||||
ThemeColors.Yellow -> "yellow"
|
||||
else -> "default"
|
||||
})
|
||||
ThemeConfig.updateTheme(theme = theme)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,6 +152,7 @@ class MoreSettingsHandlers(
|
||||
fun handleDynamicColorChange(enabled: Boolean) {
|
||||
state.useDynamicColor = enabled
|
||||
context.saveDynamicColorState(enabled)
|
||||
ThemeConfig.updateTheme(dynamicColor = enabled)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,8 +222,6 @@ class MoreSettingsHandlers(
|
||||
CardConfig.isCustomDimSet = false
|
||||
CardConfig.isCustomBackgroundEnabled = false
|
||||
saveCardConfig(context)
|
||||
|
||||
ThemeConfig.needsResetOnThemeChange = true
|
||||
ThemeConfig.preventBackgroundRefresh = false
|
||||
|
||||
context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE).edit {
|
||||
|
||||
Reference in New Issue
Block a user