UI: finally support theme modes; remove hardcoded color schemes, default to dynamic color scheme implementation
This commit is contained in:
parent
a8dc825aef
commit
2b3ba770dd
|
|
@ -50,7 +50,7 @@ import com.example.llama.revamp.viewmodel.MainViewModel
|
|||
import com.example.llama.revamp.viewmodel.ModelLoadingViewModel
|
||||
import com.example.llama.revamp.viewmodel.ModelSelectionViewModel
|
||||
import com.example.llama.revamp.viewmodel.ModelsManagementViewModel
|
||||
import com.example.llama.revamp.viewmodel.PerformanceViewModel
|
||||
import com.example.llama.revamp.viewmodel.SettingsViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -58,13 +58,17 @@ import kotlinx.coroutines.launch
|
|||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
LlamaTheme {
|
||||
val settingsViewModel: SettingsViewModel = hiltViewModel()
|
||||
val themeMode by settingsViewModel.themeMode.collectAsState()
|
||||
|
||||
LlamaTheme(themeMode) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
AppContent()
|
||||
AppContent(settingsViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,8 +77,8 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
@Composable
|
||||
fun AppContent(
|
||||
settingsViewModel: SettingsViewModel,
|
||||
mainViewModel: MainViewModel = hiltViewModel(),
|
||||
performanceViewModel: PerformanceViewModel = hiltViewModel(),
|
||||
modelSelectionViewModel: ModelSelectionViewModel = hiltViewModel(),
|
||||
modelLoadingViewModel: ModelLoadingViewModel = hiltViewModel(),
|
||||
benchmarkViewModel: BenchmarkViewModel = hiltViewModel(),
|
||||
|
|
@ -88,10 +92,10 @@ fun AppContent(
|
|||
val engineState by mainViewModel.engineState.collectAsState()
|
||||
|
||||
// Metric states for scaffolds
|
||||
val memoryUsage by performanceViewModel.memoryUsage.collectAsState()
|
||||
val temperatureInfo by performanceViewModel.temperatureMetrics.collectAsState()
|
||||
val useFahrenheit by performanceViewModel.useFahrenheitUnit.collectAsState()
|
||||
val storageMetrics by performanceViewModel.storageMetrics.collectAsState()
|
||||
val memoryUsage by settingsViewModel.memoryUsage.collectAsState()
|
||||
val temperatureInfo by settingsViewModel.temperatureMetrics.collectAsState()
|
||||
val useFahrenheit by settingsViewModel.useFahrenheitUnit.collectAsState()
|
||||
val storageMetrics by settingsViewModel.storageMetrics.collectAsState()
|
||||
|
||||
// Navigation
|
||||
val navController = rememberNavController()
|
||||
|
|
@ -293,9 +297,6 @@ fun AppContent(
|
|||
// Model Selection Screen
|
||||
composable(AppDestinations.MODEL_SELECTION_ROUTE) {
|
||||
ModelSelectionScreen(
|
||||
onNavigateBack = {
|
||||
navigationActions.navigateUp()
|
||||
},
|
||||
onModelConfirmed = { modelInfo ->
|
||||
navigationActions.navigateToModelLoading()
|
||||
},
|
||||
|
|
@ -371,7 +372,9 @@ fun AppContent(
|
|||
|
||||
// Settings General Screen
|
||||
composable(AppDestinations.SETTINGS_GENERAL_ROUTE) {
|
||||
SettingsGeneralScreen()
|
||||
SettingsGeneralScreen(
|
||||
viewModel = settingsViewModel
|
||||
)
|
||||
}
|
||||
|
||||
// Models Management Screen
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.datastore.core.DataStore
|
|||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.longPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
|
|
@ -28,9 +29,15 @@ class UserPreferences @Inject constructor (
|
|||
val PERFORMANCE_MONITORING_ENABLED = booleanPreferencesKey("performance_monitoring_enabled")
|
||||
val USE_FAHRENHEIT_TEMPERATURE = booleanPreferencesKey("use_fahrenheit_temperature")
|
||||
val MONITORING_INTERVAL_MS = longPreferencesKey("monitoring_interval_ms")
|
||||
val THEME_MODE = intPreferencesKey("theme_mode")
|
||||
|
||||
// Default values
|
||||
const val DEFAULT_MONITORING_INTERVAL_MS = 5000L
|
||||
|
||||
// Theme mode values
|
||||
const val THEME_MODE_AUTO = 0
|
||||
const val THEME_MODE_LIGHT = 1
|
||||
const val THEME_MODE_DARK = 2
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -38,7 +45,7 @@ class UserPreferences @Inject constructor (
|
|||
*/
|
||||
fun isPerformanceMonitoringEnabled(): Flow<Boolean> {
|
||||
return context.dataStore.data.map { preferences ->
|
||||
preferences[PERFORMANCE_MONITORING_ENABLED] ?: true
|
||||
preferences[PERFORMANCE_MONITORING_ENABLED] != false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +63,7 @@ class UserPreferences @Inject constructor (
|
|||
*/
|
||||
fun usesFahrenheitTemperature(): Flow<Boolean> {
|
||||
return context.dataStore.data.map { preferences ->
|
||||
preferences[USE_FAHRENHEIT_TEMPERATURE] ?: false
|
||||
preferences[USE_FAHRENHEIT_TEMPERATURE] == true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,4 +95,22 @@ class UserPreferences @Inject constructor (
|
|||
preferences[MONITORING_INTERVAL_MS] = intervalMs
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current theme mode.
|
||||
*/
|
||||
fun getThemeMode(): Flow<Int> {
|
||||
return context.dataStore.data.map { preferences ->
|
||||
preferences[THEME_MODE] ?: THEME_MODE_AUTO
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme mode.
|
||||
*/
|
||||
suspend fun setThemeMode(mode: Int) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[THEME_MODE] = mode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ import androidx.compose.foundation.verticalScroll
|
|||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -20,20 +23,21 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.example.llama.revamp.APP_NAME
|
||||
import com.example.llama.revamp.viewmodel.PerformanceViewModel
|
||||
import com.example.llama.revamp.data.preferences.UserPreferences
|
||||
import com.example.llama.revamp.viewmodel.SettingsViewModel
|
||||
|
||||
/**
|
||||
* Screen for general app settings
|
||||
*/
|
||||
@Composable
|
||||
fun SettingsGeneralScreen(
|
||||
performanceViewModel: PerformanceViewModel = hiltViewModel(),
|
||||
viewModel: SettingsViewModel,
|
||||
) {
|
||||
// Collect state from ViewModel
|
||||
val isMonitoringEnabled by performanceViewModel.isMonitoringEnabled.collectAsState()
|
||||
val useFahrenheit by performanceViewModel.useFahrenheitUnit.collectAsState()
|
||||
val isMonitoringEnabled by viewModel.isMonitoringEnabled.collectAsState()
|
||||
val useFahrenheit by viewModel.useFahrenheitUnit.collectAsState()
|
||||
val themeMode by viewModel.themeMode.collectAsState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -44,28 +48,66 @@ fun SettingsGeneralScreen(
|
|||
SettingsCategory(title = "Performance Monitoring") {
|
||||
SettingsSwitch(
|
||||
title = "Enable Monitoring",
|
||||
description = "Display memory, battery and temperature information",
|
||||
description = "Display memory, battery and temperature info",
|
||||
checked = isMonitoringEnabled,
|
||||
onCheckedChange = { performanceViewModel.setMonitoringEnabled(it) }
|
||||
onCheckedChange = { viewModel.setMonitoringEnabled(it) }
|
||||
)
|
||||
|
||||
SettingsSwitch(
|
||||
title = "Use Fahrenheit",
|
||||
description = "Display temperature in Fahrenheit instead of Celsius",
|
||||
checked = useFahrenheit,
|
||||
onCheckedChange = { performanceViewModel.setUseFahrenheitUnit(it) }
|
||||
onCheckedChange = { viewModel.setUseFahrenheitUnit(it) }
|
||||
)
|
||||
}
|
||||
|
||||
SettingsCategory(title = "Theme") {
|
||||
SettingsSwitch(
|
||||
title = "Dark Theme",
|
||||
description = "Use dark theme throughout the app",
|
||||
checked = true, // This would be connected to theme state in a real app
|
||||
onCheckedChange = {
|
||||
/* TODO-hyin: Implement theme switching between Auto, Light and Dark */
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Theme Mode",
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Follow system setting or override with your choice",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
SingleChoiceSegmentedButtonRow(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
SegmentedButton(
|
||||
selected = themeMode == UserPreferences.THEME_MODE_AUTO,
|
||||
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_AUTO) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3)
|
||||
) {
|
||||
Text("Auto")
|
||||
}
|
||||
|
||||
SegmentedButton(
|
||||
selected = themeMode == UserPreferences.THEME_MODE_LIGHT,
|
||||
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_LIGHT) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 3)
|
||||
) {
|
||||
Text("Light")
|
||||
}
|
||||
|
||||
SegmentedButton(
|
||||
selected = themeMode == UserPreferences.THEME_MODE_DARK,
|
||||
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_DARK) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3)
|
||||
) {
|
||||
Text("Dark")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCategory(title = "About") {
|
||||
|
|
|
|||
|
|
@ -64,8 +64,3 @@ val md_theme_dark_inversePrimary = Color(0xFF6750A4)
|
|||
val md_theme_dark_surfaceTint = Color(0xFFCFBCFF)
|
||||
val md_theme_dark_outlineVariant = Color(0xFF49454E)
|
||||
val md_theme_dark_scrim = Color(0xFF000000)
|
||||
|
||||
// Additional app-specific colors
|
||||
val CriticalColor = Color(0xFFFF0000)
|
||||
val WarningColor = Color(0xFFFFA000)
|
||||
val SuccessColor = Color(0xFF388E3C)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package com.example.llama.revamp.ui.theme
|
||||
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
|
|
@ -15,8 +13,10 @@ import androidx.compose.ui.graphics.toArgb
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.example.llama.revamp.data.preferences.UserPreferences
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
// TODO-han.yin: support more / custom color palettes?
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = md_theme_light_primary,
|
||||
onPrimary = md_theme_light_onPrimary,
|
||||
|
|
@ -83,19 +83,18 @@ private val DarkColorScheme = darkColorScheme(
|
|||
|
||||
@Composable
|
||||
fun LlamaTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = false,
|
||||
themeMode: Int = UserPreferences.THEME_MODE_AUTO,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val darkTheme = when (themeMode) {
|
||||
UserPreferences.THEME_MODE_LIGHT -> false
|
||||
UserPreferences.THEME_MODE_DARK -> true
|
||||
else -> isSystemInDarkTheme()
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
val colorScheme =
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import javax.inject.Inject
|
|||
* ViewModel that manages performance monitoring for the app.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class PerformanceViewModel @Inject constructor(
|
||||
class SettingsViewModel @Inject constructor(
|
||||
private val userPreferences: UserPreferences,
|
||||
private val performanceMonitor: PerformanceMonitor,
|
||||
private val modelRepository: ModelRepository,
|
||||
|
|
@ -54,17 +54,27 @@ class PerformanceViewModel @Inject constructor(
|
|||
private val _monitoringInterval = MutableStateFlow(5000L)
|
||||
val monitoringInterval: StateFlow<Long> = _monitoringInterval.asStateFlow()
|
||||
|
||||
private val _themeMode = MutableStateFlow(UserPreferences.THEME_MODE_AUTO)
|
||||
val themeMode: StateFlow<Int> = _themeMode.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
// Load user preferences
|
||||
_isMonitoringEnabled.value = userPreferences.isPerformanceMonitoringEnabled().first()
|
||||
_useFahrenheitUnit.value = userPreferences.usesFahrenheitTemperature().first()
|
||||
_monitoringInterval.value = userPreferences.getMonitoringInterval().first()
|
||||
_themeMode.value = userPreferences.getThemeMode().first()
|
||||
|
||||
// Start monitoring if enabled
|
||||
if (_isMonitoringEnabled.value) {
|
||||
startMonitoring()
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
userPreferences.getThemeMode().collect { mode ->
|
||||
_themeMode.value = mode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,4 +154,14 @@ class PerformanceViewModel @Inject constructor(
|
|||
private fun isMonitoringActive(): Boolean {
|
||||
return _isMonitoringEnabled.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme mode.
|
||||
*/
|
||||
fun setThemeMode(mode: Int) {
|
||||
viewModelScope.launch {
|
||||
userPreferences.setThemeMode(mode)
|
||||
_themeMode.value = mode
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue