[WIP] UI: add Arm color palette from Philip.Watson3

This commit is contained in:
Han Yin 2025-09-01 11:46:18 -07:00
parent d27933fffe
commit 6fb4a94cc3
6 changed files with 350 additions and 121 deletions

View File

@ -67,9 +67,10 @@ class MainActivity : ComponentActivity() {
setContent { setContent {
val settingsViewModel: SettingsViewModel = hiltViewModel() val settingsViewModel: SettingsViewModel = hiltViewModel()
val themeMode by settingsViewModel.themeMode.collectAsState() val colorThemeMode by settingsViewModel.colorThemeMode.collectAsState()
val darkThemeMode by settingsViewModel.darkThemeMode.collectAsState()
LlamaTheme(themeMode) { LlamaTheme(colorThemeMode = colorThemeMode, darkThemeMode = darkThemeMode) {
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background color = MaterialTheme.colorScheme.background

View File

@ -33,15 +33,18 @@ class UserPreferences @Inject constructor (
private val PERFORMANCE_MONITORING_ENABLED = booleanPreferencesKey("performance_monitoring_enabled") private val PERFORMANCE_MONITORING_ENABLED = booleanPreferencesKey("performance_monitoring_enabled")
private val USE_FAHRENHEIT_TEMPERATURE = booleanPreferencesKey("use_fahrenheit_temperature") private val USE_FAHRENHEIT_TEMPERATURE = booleanPreferencesKey("use_fahrenheit_temperature")
private val MONITORING_INTERVAL_MS = longPreferencesKey("monitoring_interval_ms") private val MONITORING_INTERVAL_MS = longPreferencesKey("monitoring_interval_ms")
private val THEME_MODE = intPreferencesKey("theme_mode") private val COLOR_THEME_MODE = intPreferencesKey("color_theme_mode")
private val DARK_THEME_MODE = intPreferencesKey("dark_theme_mode")
// Constants // Constants
private const val DEFAULT_MONITORING_INTERVAL_MS = 5000L private const val DEFAULT_MONITORING_INTERVAL_MS = 5000L
// Theme mode values const val COLOR_THEME_MODE_ARM = 0
const val THEME_MODE_AUTO = 0 const val COLOR_THEME_MODE_MATERIAL = 1
const val THEME_MODE_LIGHT = 1
const val THEME_MODE_DARK = 2 const val DARK_THEME_MODE_AUTO = 0
const val DARK_THEME_MODE_LIGHT = 1
const val DARK_THEME_MODE_DARK = 2
} }
/** /**
@ -98,19 +101,36 @@ class UserPreferences @Inject constructor (
} }
/** /**
* Gets the current theme mode. * Gets the current color theme mode.
*/ */
fun getThemeMode(): Flow<Int> = fun getColorThemeMode(): Flow<Int> =
context.settingsDataStore.data.map { preferences -> context.settingsDataStore.data.map { preferences ->
preferences[THEME_MODE] ?: THEME_MODE_AUTO preferences[COLOR_THEME_MODE] ?: COLOR_THEME_MODE_ARM
} }
/** /**
* Sets the theme mode. * Sets the color theme mode.
*/ */
suspend fun setThemeMode(mode: Int) = withContext(Dispatchers.IO) { suspend fun setColorThemeMode(mode: Int) = withContext(Dispatchers.IO) {
context.settingsDataStore.edit { preferences -> context.settingsDataStore.edit { preferences ->
preferences[THEME_MODE] = mode preferences[COLOR_THEME_MODE] = mode
}
}
/**
* Gets the current dark theme mode.
*/
fun getDarkThemeMode(): Flow<Int> =
context.settingsDataStore.data.map { preferences ->
preferences[DARK_THEME_MODE] ?: DARK_THEME_MODE_AUTO
}
/**
* Sets the dark theme mode.
*/
suspend fun setDarkThemeMode(mode: Int) = withContext(Dispatchers.IO) {
context.settingsDataStore.edit { preferences ->
preferences[DARK_THEME_MODE] = mode
} }
} }
} }

View File

@ -45,7 +45,8 @@ fun SettingsGeneralScreen(
// Collect state from ViewModel // Collect state from ViewModel
val isMonitoringEnabled by viewModel.isMonitoringEnabled.collectAsState() val isMonitoringEnabled by viewModel.isMonitoringEnabled.collectAsState()
val useFahrenheit by viewModel.useFahrenheitUnit.collectAsState() val useFahrenheit by viewModel.useFahrenheitUnit.collectAsState()
val themeMode by viewModel.themeMode.collectAsState() val colorThemeMode by viewModel.colorThemeMode.collectAsState()
val darkThemeMode by viewModel.darkThemeMode.collectAsState()
val detectedTier = viewModel.detectedTier val detectedTier = viewModel.detectedTier
val supportedFeatures = remember(detectedTier) { val supportedFeatures = remember(detectedTier) {
@ -80,9 +81,44 @@ fun SettingsGeneralScreen(
) )
} }
SettingsCategory(title = "Theme") { SettingsCategory(title = "Styling") {
// Color theme mode
Text( Text(
text = "Theme Mode", text = "Color Theme",
style = MaterialTheme.typography.titleMedium
)
Text(
text = "Arm® or follow your system dynamic colors",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(16.dp))
SingleChoiceSegmentedButtonRow(
modifier = Modifier.fillMaxWidth()
) {
SegmentedButton(
selected = colorThemeMode == UserPreferences.COLOR_THEME_MODE_ARM,
onClick = { viewModel.setColorThemeMode(UserPreferences.COLOR_THEME_MODE_ARM) },
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2)
) {
Text("Arm")
}
SegmentedButton(
selected = colorThemeMode == UserPreferences.COLOR_THEME_MODE_MATERIAL,
onClick = { viewModel.setColorThemeMode(UserPreferences.COLOR_THEME_MODE_MATERIAL) },
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2)
) {
Text("Material")
}
}
// Dark theme mode
Text(
text = "Dark Mode",
style = MaterialTheme.typography.titleMedium style = MaterialTheme.typography.titleMedium
) )
@ -98,24 +134,24 @@ fun SettingsGeneralScreen(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
SegmentedButton( SegmentedButton(
selected = themeMode == UserPreferences.THEME_MODE_AUTO, selected = darkThemeMode == UserPreferences.DARK_THEME_MODE_AUTO,
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_AUTO) }, onClick = { viewModel.setDarkThemeMode(UserPreferences.DARK_THEME_MODE_AUTO) },
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3) shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3)
) { ) {
Text("Auto") Text("Auto")
} }
SegmentedButton( SegmentedButton(
selected = themeMode == UserPreferences.THEME_MODE_LIGHT, selected = darkThemeMode == UserPreferences.DARK_THEME_MODE_LIGHT,
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_LIGHT) }, onClick = { viewModel.setDarkThemeMode(UserPreferences.DARK_THEME_MODE_LIGHT) },
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 3) shape = SegmentedButtonDefaults.itemShape(index = 1, count = 3)
) { ) {
Text("Light") Text("Light")
} }
SegmentedButton( SegmentedButton(
selected = themeMode == UserPreferences.THEME_MODE_DARK, selected = darkThemeMode == UserPreferences.DARK_THEME_MODE_DARK,
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_DARK) }, onClick = { viewModel.setDarkThemeMode(UserPreferences.DARK_THEME_MODE_DARK) },
shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3) shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3)
) { ) {
Text("Dark") Text("Dark")

View File

@ -1,66 +1,151 @@
package com.example.llama.ui.theme package com.example.llama.ui.theme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
// Light Theme Colors
val md_theme_light_primary = Color(0xFF4527A0)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFE8DDFF)
val md_theme_light_onPrimaryContainer = Color(0xFF22005D)
val md_theme_light_secondary = Color(0xFF625B71)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFE8DEF8)
val md_theme_light_onSecondaryContainer = Color(0xFF1E192B)
val md_theme_light_tertiary = Color(0xFF7D5260)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFFFD9E3)
val md_theme_light_onTertiaryContainer = Color(0xFF31101D)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF1C1B1E)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF1C1B1E)
val md_theme_light_surfaceVariant = Color(0xFFE7E0EB)
val md_theme_light_onSurfaceVariant = Color(0xFF49454E)
val md_theme_light_outline = Color(0xFF7A757F)
val md_theme_light_inverseOnSurface = Color(0xFFF4EFF4)
val md_theme_light_inverseSurface = Color(0xFF313033)
val md_theme_light_inversePrimary = Color(0xFFCFBCFF)
val md_theme_light_surfaceTint = Color(0xFF6750A4)
val md_theme_light_outlineVariant = Color(0xFFCAC4CF)
val md_theme_light_scrim = Color(0xFF000000)
// Dark Theme Colors // --- Light Theme Colors ---
val md_theme_dark_primary = Color(0xFFCFBCFF) val md_theme_light_primary = Color(0xFF0747C9)
val md_theme_dark_onPrimary = Color(0xFF381E72) val md_theme_light_onPrimary = Color(0xFFCFCFCF)
val md_theme_dark_primaryContainer = Color(0xFF4F378A) val md_theme_light_primaryContainer = Color(0xFFD0DEF6)
val md_theme_dark_onPrimaryContainer = Color(0xFFE8DDFF) val md_theme_light_onPrimaryContainer = Color(0xFF1F2227)
val md_theme_dark_secondary = Color(0xFFCBC2DB) val md_theme_light_inversePrimary = Color(0xFFD0DEF6)
val md_theme_dark_onSecondary = Color(0xFF332D41) // NOT-USED: val md_theme_light_primaryDim = Color(0xFF031E59)
val md_theme_dark_secondaryContainer = Color(0xFF4A4458) // NOT-USED: val md_theme_light_onPrimaryDim = Color(0xFFCFCFCF)
val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8)
val md_theme_dark_tertiary = Color(0xFFEFB8C8) val md_theme_light_secondary = Color(0xFFCFCFCF)
val md_theme_dark_onTertiary = Color(0xFF4A2532) val md_theme_light_onSecondary = Color(0xFF1F2227)
val md_theme_dark_tertiaryContainer = Color(0xFF633B48) val md_theme_light_secondaryContainer = Color(0xFFDFE1E7)
val md_theme_dark_onTertiaryContainer = Color(0xFFFFD9E3) val md_theme_light_onSecondaryContainer = Color(0xFF1F2227)
val md_theme_dark_error = Color(0xFFFFB4AB) // NOT-USED: val md_theme_light_secondaryDim = Color(0xFF93B7F8)
val md_theme_dark_errorContainer = Color(0xFF93000A) // NOT-USED: val md_theme_light_onSecondaryDim = Color(0xFF1F2227)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) val md_theme_light_tertiary = Color(0xFF5613CD)
val md_theme_dark_background = Color(0xFF1C1B1E) val md_theme_light_onTertiary = Color(0xFFCFCFCF)
val md_theme_dark_onBackground = Color(0xFFE6E1E6) val md_theme_light_tertiaryContainer = Color(0xFFD7D6F7)
val md_theme_dark_surface = Color(0xFF1C1B1E) val md_theme_light_onTertiaryContainer = Color(0xFF1F2227)
val md_theme_dark_onSurface = Color(0xFFE6E1E6) // NOT-USED: val md_theme_light_tertiaryDim = Color(0xFF220857)
val md_theme_dark_surfaceVariant = Color(0xFF49454E) // NOT-USED: val md_theme_light_onTertiaryDim = Color(0xFFCFCFCF)
val md_theme_dark_onSurfaceVariant = Color(0xFFCAC4CF)
val md_theme_dark_outline = Color(0xFF948F99) val md_theme_light_background = Color(0xFFCFCFCF)
val md_theme_dark_inverseOnSurface = Color(0xFF1C1B1E) val md_theme_light_onBackground = Color(0xFF1F2227)
val md_theme_dark_inverseSurface = Color(0xFFE6E1E6) val md_theme_light_surface = Color(0xFFCFCFCF)
val md_theme_dark_inversePrimary = Color(0xFF6750A4) val md_theme_light_onSurface = Color(0xFF1F2227)
val md_theme_dark_surfaceTint = Color(0xFFCFBCFF) val md_theme_light_surfaceVariant = Color(0xFFD0DEF6)
val md_theme_dark_outlineVariant = Color(0xFF49454E) val md_theme_light_onSurfaceVariant = Color(0xFF1F2227)
val md_theme_light_surfaceTint = Color(0xFFDFE1E7)
val md_theme_light_inverseSurface = Color(0xFF737A8B)
val md_theme_light_inverseOnSurface = Color(0xFFCFCFCF)
val md_theme_light_error = Color(0xFF8F0D11)
val md_theme_light_onError = Color(0xFFCFCFCF)
val md_theme_light_errorContainer = Color(0xFFF5C8C3)
val md_theme_light_onErrorContainer = Color(0xFF1F2227)
// NOT-USED: val md_theme_light_errorDim = Color(0xFF390506)
// NOT-USED: val md_theme_light_onErrorDim = Color(0xFFCFCFCF)
val md_theme_light_outline = Color(0xFFDFE1E7)
val md_theme_light_outlineVariant = Color(0xFF9BA1B2)
val md_theme_light_scrim = Color(0xFF000000)
// NOT-USED: val md_theme_light_shadow = Color(0x99030304)
// TODO: md_theme_light_surfaceBright
val md_theme_light_surfaceContainer = Color(0xFFF0F2F4)
// NOT-USED: val md_theme_light_onSurfaceContainer = Color(0xFF1F2227)
val md_theme_light_surfaceContainerHigh = Color(0xFFDFE1E7)
// NOT-USED: val md_theme_light_onSurfaceContainerHigh = Color(0xFF1F2227)
// TODO: md_theme_light_surfaceContainerHighest
val md_theme_light_surfaceContainerLow = Color(0xFFE9EBEF)
// NOT-USED: val md_theme_light_onSurfaceContainerLow = Color(0xFF1F2227)
// TODO: md_theme_light_surfaceContainerLowest
// TODO: md_theme_light_surfaceDim
val md_theme_light_primaryFixed = Color(0xFF0747C9)
val md_theme_light_primaryFixedDim = Color(0xFF04359B)
val md_theme_light_onPrimaryFixed = Color(0xFFCFCFCF)
val md_theme_light_onPrimaryFixedVariant = Color(0xFF2F333B)
val md_theme_light_secondaryFixed = Color(0xFFCFCFCF)
val md_theme_light_secondaryFixedDim = Color(0xFFE9EBEF)
val md_theme_light_onSecondaryFixed = Color(0xFF1F2227)
val md_theme_light_onSecondaryFixedVariant = Color(0xFF2F333B)
val md_theme_light_tertiaryFixed = Color(0xFF5613CD)
val md_theme_light_tertiaryFixedDim = Color(0xFF220857)
val md_theme_light_onTertiaryFixed = Color(0xFFCFCFCF)
val md_theme_light_onTertiaryFixedVariant = Color(0xFFDFE1E7)
// --- Dark Theme Colors ---
val md_theme_dark_primary = Color(0xFF0747C9)
val md_theme_dark_onPrimary = Color(0xFFCFCFCF)
val md_theme_dark_primaryContainer = Color(0xFF020D2B)
val md_theme_dark_onPrimaryContainer = Color(0xFFCFCFCF)
val md_theme_dark_inversePrimary = Color(0xFF020D2B)
// NOT-USED: val md_theme_dark_primaryDim = Color(0xFF031E59)
// NOT-USED: val md_theme_dark_onPrimaryDim = Color(0xFFCFCFCF)
val md_theme_dark_secondary = Color(0xFF1F2227)
val md_theme_dark_onSecondary = Color(0xFFCFCFCF)
val md_theme_dark_secondaryContainer = Color(0xFF2F333B)
val md_theme_dark_onSecondaryContainer = Color(0xFFCFCFCF)
// NOT-USED: val md_theme_dark_secondaryDim = Color(0xFF031E59)
// NOT-USED: val md_theme_dark_onSecondaryDim = Color(0xFFCFCFCF)
val md_theme_dark_tertiary = Color(0xFF5613CD)
val md_theme_dark_onTertiary = Color(0xFFCFCFCF)
val md_theme_dark_tertiaryContainer = Color(0xFF0F0429)
val md_theme_dark_onTertiaryContainer = Color(0xFFCFCFCF)
// NOT-USED: val md_theme_dark_tertiaryDim = Color(0xFF220857)
// NOT-USED: val md_theme_dark_onTertiaryDim = Color(0xFFCFCFCF)
val md_theme_dark_background = Color(0xFF0F1216)
val md_theme_dark_onBackground = Color(0xFFCFCFCF)
val md_theme_dark_surface = Color(0xFF0F1216)
val md_theme_dark_onSurface = Color(0xFFCFCFCF)
val md_theme_dark_surfaceVariant = Color(0xFF020D2B)
val md_theme_dark_onSurfaceVariant = Color(0xFFCFCFCF)
val md_theme_dark_surfaceTint = Color(0xFF2F333B)
val md_theme_dark_inverseSurface = Color(0xFF8A92A5)
val md_theme_dark_inverseOnSurface = Color(0xFFCFCFCF)
val md_theme_dark_error = Color(0xFF8F0D11)
val md_theme_dark_onError = Color(0xFFCFCFCF)
val md_theme_dark_errorContainer = Color(0xFF180203)
val md_theme_dark_onErrorContainer = Color(0xFFCFCFCF)
// NOT-USED: val md_theme_dark_errorDim = Color(0xFF390506)
// NOT-USED: val md_theme_dark_onErrorDim = Color(0xFFCFCFCF)
val md_theme_dark_outline = Color(0xFF40454F)
val md_theme_dark_outlineVariant = Color(0xFF8A92A5)
val md_theme_dark_scrim = Color(0xFF000000) val md_theme_dark_scrim = Color(0xFF000000)
// NOT-USED: val md_theme_dark_shadow = Color(0xFF2F333B)
// TODO: md_theme_dark_surfaceBright
val md_theme_dark_surfaceContainer = Color(0xFF030304)
// NOT-USED: val md_theme_dark_onSurfaceContainer = Color(0xFFFCFCFC)
val md_theme_dark_surfaceContainerLow = Color(0xFF1F2227)
// NOT-USED: val md_theme_dark_onSurfaceContainerLow = Color(0xFFFCFCFC)
// TODO: md_theme_dark_surfaceContainerHighest
val md_theme_dark_surfaceContainerHigh = Color(0xFF2F333B)
// NOT-USED: val md_theme_dark_onSurfaceContainerHigh = Color(0xFFCFCFCF)
// TODO: md_theme_dark_surfaceContainerLowest
// TODO: md_theme_dark_surfaceDim
val md_theme_dark_primaryFixed = Color(0xFF0747C9)
val md_theme_dark_primaryFixedDim = Color(0xFF04359B)
val md_theme_dark_onPrimaryFixed = Color(0xFFCFCFCF)
val md_theme_dark_onPrimaryFixedVariant = Color(0xFFDFE1E7)
val md_theme_dark_secondaryFixed = Color(0xFF1F2227)
val md_theme_dark_secondaryFixedDim = Color(0xFF40454F)
val md_theme_dark_onSecondaryFixed = Color(0xFFCFCFCF)
val md_theme_dark_onSecondaryFixedVariant = Color(0xFFDFE1E7)
val md_theme_dark_tertiaryFixed = Color(0xFF5613CD)
val md_theme_dark_tertiaryFixedDim = Color(0xFF220857)
val md_theme_dark_onTertiaryFixed = Color(0xFFCFCFCF)
val md_theme_dark_onTertiaryFixedVariant = Color(0xFFDFE1E7)

View File

@ -2,6 +2,7 @@ package com.example.llama.ui.theme
import android.app.Activity import android.app.Activity
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicDarkColorScheme
@ -14,87 +15,152 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import com.example.llama.data.source.prefs.UserPreferences import com.example.llama.data.source.prefs.UserPreferences
import com.example.llama.data.source.prefs.UserPreferences.Companion.COLOR_THEME_MODE_ARM
import com.example.llama.data.source.prefs.UserPreferences.Companion.COLOR_THEME_MODE_MATERIAL
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
// TODO-han.yin: support more / custom color palettes? // -------------------- ColorScheme --------------------
private val LightColorScheme = lightColorScheme( internal val armLightColorScheme: ColorScheme = lightColorScheme(
primary = md_theme_light_primary, primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary, onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer, primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer, onPrimaryContainer = md_theme_light_onPrimaryContainer,
inversePrimary = md_theme_light_inversePrimary,
secondary = md_theme_light_secondary, secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary, onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer, secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer, onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary, tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary, onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer, tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer, onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background, background = md_theme_light_background,
onBackground = md_theme_light_onBackground, onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface, surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface, onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant, surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant, onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint, surfaceTint = md_theme_light_surfaceTint,
inverseSurface = md_theme_light_inverseSurface,
inverseOnSurface = md_theme_light_inverseOnSurface,
error = md_theme_light_error,
onError = md_theme_light_onError,
errorContainer = md_theme_light_errorContainer,
onErrorContainer = md_theme_light_onErrorContainer,
outline = md_theme_light_outline,
outlineVariant = md_theme_light_outlineVariant, outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim, scrim = md_theme_light_scrim,
// TODO: surfaceBright = md_theme_light_surfaceBright,
surfaceContainer = md_theme_light_surfaceContainer,
surfaceContainerHigh = md_theme_light_surfaceContainerHigh,
// TODO: surfaceContainerHighest = md_theme_light_surfaceContainerHighest,
surfaceContainerLow = md_theme_light_surfaceContainerLow,
// TODO: surfaceContainerLowest = md_theme_light_surfaceContainerLowest,
// TODO: surfaceDim = md_theme_light_surfaceDim,
primaryFixed = md_theme_light_primaryFixed,
primaryFixedDim = md_theme_light_primaryFixedDim,
onPrimaryFixed = md_theme_light_onPrimaryFixed,
onPrimaryFixedVariant = md_theme_light_onPrimaryFixedVariant,
secondaryFixed = md_theme_light_secondaryFixed,
secondaryFixedDim = md_theme_light_secondaryFixedDim,
onSecondaryFixed = md_theme_light_onSecondaryFixed,
onSecondaryFixedVariant = md_theme_light_onSecondaryFixedVariant,
tertiaryFixed = md_theme_light_tertiaryFixed,
tertiaryFixedDim = md_theme_light_tertiaryFixedDim,
onTertiaryFixed = md_theme_light_onTertiaryFixed,
onTertiaryFixedVariant = md_theme_light_onTertiaryFixedVariant,
) )
private val DarkColorScheme = darkColorScheme(
internal val armDarkColorScheme: ColorScheme = darkColorScheme(
primary = md_theme_dark_primary, primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary, onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer, primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer, onPrimaryContainer = md_theme_dark_onPrimaryContainer,
inversePrimary = md_theme_dark_inversePrimary,
secondary = md_theme_dark_secondary, secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary, onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer, secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer, onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary, tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary, onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer, tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer, onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background, background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground, onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface, surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface, onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant, surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant, onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint, surfaceTint = md_theme_dark_surfaceTint,
inverseSurface = md_theme_dark_inverseSurface,
inverseOnSurface = md_theme_dark_inverseOnSurface,
error = md_theme_dark_error,
onError = md_theme_dark_onError,
errorContainer = md_theme_dark_errorContainer,
onErrorContainer = md_theme_dark_onErrorContainer,
outline = md_theme_dark_outline,
outlineVariant = md_theme_dark_outlineVariant, outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim, scrim = md_theme_dark_scrim,
// TODO: surfaceBright = md_theme_dark_surfaceBright,
surfaceContainer = md_theme_dark_surfaceContainer,
surfaceContainerHigh = md_theme_dark_surfaceContainerHigh,
// TODO: surfaceContainerHighest = md_theme_dark_surfaceContainerHighest,
surfaceContainerLow = md_theme_dark_surfaceContainerLow,
// TODO: surfaceContainerLowest = md_theme_dark_surfaceContainerLowest,
// TODO: surfaceDim = md_theme_dark_surfaceDim,
primaryFixed = md_theme_dark_primaryFixed,
primaryFixedDim = md_theme_dark_primaryFixedDim,
onPrimaryFixed = md_theme_dark_onPrimaryFixed,
onPrimaryFixedVariant = md_theme_dark_onPrimaryFixedVariant,
secondaryFixed = md_theme_dark_secondaryFixed,
secondaryFixedDim = md_theme_dark_secondaryFixedDim,
onSecondaryFixed = md_theme_dark_onSecondaryFixed,
onSecondaryFixedVariant = md_theme_dark_onSecondaryFixedVariant,
tertiaryFixed = md_theme_dark_tertiaryFixed,
tertiaryFixedDim = md_theme_dark_tertiaryFixedDim,
onTertiaryFixed = md_theme_dark_onTertiaryFixed,
onTertiaryFixedVariant = md_theme_dark_onTertiaryFixedVariant,
) )
@Composable @Composable
fun LlamaTheme( fun LlamaTheme(
themeMode: Int = UserPreferences.THEME_MODE_AUTO, colorThemeMode: Int,
darkThemeMode: Int,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val darkTheme = when (themeMode) { val darkTheme = when (darkThemeMode) {
UserPreferences.THEME_MODE_LIGHT -> false UserPreferences.DARK_THEME_MODE_LIGHT -> false
UserPreferences.THEME_MODE_DARK -> true UserPreferences.DARK_THEME_MODE_DARK -> true
else -> isSystemInDarkTheme() else -> isSystemInDarkTheme()
} }
val context = LocalContext.current val context = LocalContext.current
val colorScheme =
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) val colorScheme = when(colorThemeMode) {
COLOR_THEME_MODE_ARM -> if (darkTheme) armDarkColorScheme else armLightColorScheme
COLOR_THEME_MODE_MATERIAL ->
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
else -> error("Unexpected color theme $colorThemeMode")
}
val view = LocalView.current val view = LocalView.current
if (!view.isInEditMode) { if (!view.isInEditMode) {

View File

@ -44,11 +44,10 @@ class SettingsViewModel @Inject constructor(
private val _batteryInfo = MutableStateFlow(BatteryMetrics(0, false)) private val _batteryInfo = MutableStateFlow(BatteryMetrics(0, false))
val batteryInfo: StateFlow<BatteryMetrics> = _batteryInfo.asStateFlow() val batteryInfo: StateFlow<BatteryMetrics> = _batteryInfo.asStateFlow()
// Temperature information // User preferences: monitoring
private val _temperatureMetrics = MutableStateFlow(TemperatureMetrics(0f, TemperatureWarningLevel.NORMAL)) private val _temperatureMetrics = MutableStateFlow(TemperatureMetrics(0f, TemperatureWarningLevel.NORMAL))
val temperatureMetrics: StateFlow<TemperatureMetrics> = _temperatureMetrics.asStateFlow() val temperatureMetrics: StateFlow<TemperatureMetrics> = _temperatureMetrics.asStateFlow()
// User preferences
private val _isMonitoringEnabled = MutableStateFlow(true) private val _isMonitoringEnabled = MutableStateFlow(true)
val isMonitoringEnabled: StateFlow<Boolean> = _isMonitoringEnabled.asStateFlow() val isMonitoringEnabled: StateFlow<Boolean> = _isMonitoringEnabled.asStateFlow()
@ -58,8 +57,12 @@ class SettingsViewModel @Inject constructor(
private val _monitoringInterval = MutableStateFlow(5000L) private val _monitoringInterval = MutableStateFlow(5000L)
val monitoringInterval: StateFlow<Long> = _monitoringInterval.asStateFlow() val monitoringInterval: StateFlow<Long> = _monitoringInterval.asStateFlow()
private val _themeMode = MutableStateFlow(UserPreferences.THEME_MODE_AUTO) // User preferences: themes
val themeMode: StateFlow<Int> = _themeMode.asStateFlow() private val _colorThemeMode = MutableStateFlow(UserPreferences.COLOR_THEME_MODE_ARM)
val colorThemeMode: StateFlow<Int> = _colorThemeMode.asStateFlow()
private val _darkThemeMode = MutableStateFlow(UserPreferences.DARK_THEME_MODE_AUTO)
val darkThemeMode: StateFlow<Int> = _darkThemeMode.asStateFlow()
val detectedTier: LLamaTier? val detectedTier: LLamaTier?
get() = tierDetection.detectedTier get() = tierDetection.detectedTier
@ -70,18 +73,26 @@ class SettingsViewModel @Inject constructor(
_isMonitoringEnabled.value = userPreferences.isPerformanceMonitoringEnabled().first() _isMonitoringEnabled.value = userPreferences.isPerformanceMonitoringEnabled().first()
_useFahrenheitUnit.value = userPreferences.usesFahrenheitTemperature().first() _useFahrenheitUnit.value = userPreferences.usesFahrenheitTemperature().first()
_monitoringInterval.value = userPreferences.getMonitoringInterval().first() _monitoringInterval.value = userPreferences.getMonitoringInterval().first()
_themeMode.value = userPreferences.getThemeMode().first() _colorThemeMode.value = userPreferences.getColorThemeMode().first()
_darkThemeMode.value = userPreferences.getDarkThemeMode().first()
// Start monitoring if enabled // Start monitoring if enabled
if (_isMonitoringEnabled.value) { if (_isMonitoringEnabled.value) {
startMonitoring() startMonitoring()
} }
viewModelScope.launch { // viewModelScope.launch {
userPreferences.getThemeMode().collect { mode -> // launch {
_themeMode.value = mode // userPreferences.getColorThemeMode().collect { mode ->
} // _colorThemeMode.value = mode
} // }
// }
// launch {
// userPreferences.getDarkThemeMode().collect { mode ->
// _darkThemeMode.value = mode
// }
// }
// }
} }
} }
@ -158,12 +169,22 @@ class SettingsViewModel @Inject constructor(
} }
/** /**
* Sets the theme mode. * Sets the color theme mode.
*/ */
fun setThemeMode(mode: Int) { fun setColorThemeMode(mode: Int) {
viewModelScope.launch { viewModelScope.launch {
userPreferences.setThemeMode(mode) userPreferences.setColorThemeMode(mode)
_themeMode.value = mode _colorThemeMode.value = mode
}
}
/**
* Sets the dark theme mode.
*/
fun setDarkThemeMode(mode: Int) {
viewModelScope.launch {
userPreferences.setDarkThemeMode(mode)
_darkThemeMode.value = mode
} }
} }
} }