UI: fix UI issues in the generic settings screen and navigation drawer

This commit is contained in:
Han Yin 2025-09-02 14:14:21 -07:00
parent 36c3768f52
commit eba09a3d40
6 changed files with 82 additions and 98 deletions

View File

@ -48,6 +48,7 @@ android {
} }
buildFeatures { buildFeatures {
compose = true compose = true
buildConfig = true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.5.1" kotlinCompilerExtensionVersion = "1.5.1"

View File

@ -38,13 +38,6 @@ class UserPreferences @Inject constructor (
// Constants // Constants
private const val DEFAULT_MONITORING_INTERVAL_MS = 5000L private const val DEFAULT_MONITORING_INTERVAL_MS = 5000L
const val COLOR_THEME_MODE_ARM = 0
const val COLOR_THEME_MODE_MATERIAL = 1
const val DARK_THEME_MODE_AUTO = 0
const val DARK_THEME_MODE_LIGHT = 1
const val DARK_THEME_MODE_DARK = 2
} }
/** /**
@ -103,34 +96,53 @@ class UserPreferences @Inject constructor (
/** /**
* Gets the current color theme mode. * Gets the current color theme mode.
*/ */
fun getColorThemeMode(): Flow<Int> = fun getColorThemeMode(): Flow<ColorThemeMode> =
context.settingsDataStore.data.map { preferences -> context.settingsDataStore.data.map { preferences ->
preferences[COLOR_THEME_MODE] ?: COLOR_THEME_MODE_ARM ColorThemeMode.fromInt(preferences[COLOR_THEME_MODE]) ?: ColorThemeMode.ARM
} }
/** /**
* Sets the color theme mode. * Sets the color theme mode.
*/ */
suspend fun setColorThemeMode(mode: Int) = withContext(Dispatchers.IO) { suspend fun setColorThemeMode(mode: ColorThemeMode) = withContext(Dispatchers.IO) {
context.settingsDataStore.edit { preferences -> context.settingsDataStore.edit { preferences ->
preferences[COLOR_THEME_MODE] = mode preferences[COLOR_THEME_MODE] = mode.value
} }
} }
/** /**
* Gets the current dark theme mode. * Gets the current dark theme mode.
*/ */
fun getDarkThemeMode(): Flow<Int> = fun getDarkThemeMode(): Flow<DarkThemeMode> =
context.settingsDataStore.data.map { preferences -> context.settingsDataStore.data.map { preferences ->
preferences[DARK_THEME_MODE] ?: DARK_THEME_MODE_AUTO DarkThemeMode.fromInt(preferences[DARK_THEME_MODE]) ?: DarkThemeMode.AUTO
} }
/** /**
* Sets the dark theme mode. * Sets the dark theme mode.
*/ */
suspend fun setDarkThemeMode(mode: Int) = withContext(Dispatchers.IO) { suspend fun setDarkThemeMode(mode: DarkThemeMode) = withContext(Dispatchers.IO) {
context.settingsDataStore.edit { preferences -> context.settingsDataStore.edit { preferences ->
preferences[DARK_THEME_MODE] = mode preferences[DARK_THEME_MODE] = mode.value
} }
} }
} }
enum class ColorThemeMode(val value: Int, val label: String) {
ARM(0, "Arm®"),
MATERIAL(1, "Material Design");
companion object {
fun fromInt(value: Int?) = entries.find { it.value == value }
}
}
enum class DarkThemeMode(val value: Int, val label: String) {
AUTO(0, "Auto"),
LIGHT(1, "Light"),
DARK(2, "Dark");
companion object {
fun fromInt(value: Int?) = entries.find { it.value == value }
}
}

View File

@ -33,6 +33,7 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.llama.APP_NAME import com.example.llama.APP_NAME
import com.example.llama.BuildConfig
import com.example.llama.navigation.AppDestinations import com.example.llama.navigation.AppDestinations
import com.example.llama.navigation.NavigationActions import com.example.llama.navigation.NavigationActions
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -111,7 +112,7 @@ private fun DrawerContent(
) )
Text( Text(
text = "v1.0.0", text = BuildConfig.VERSION_NAME,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp) modifier = Modifier.padding(top = 4.dp)

View File

@ -31,9 +31,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.net.toUri import androidx.core.net.toUri
import com.example.llama.APP_NAME import com.example.llama.APP_NAME
import com.example.llama.data.source.prefs.UserPreferences import com.example.llama.BuildConfig
import com.example.llama.data.source.prefs.ColorThemeMode
import com.example.llama.data.source.prefs.DarkThemeMode
import com.example.llama.ui.components.ArmFeaturesVisualizer import com.example.llama.ui.components.ArmFeaturesVisualizer
import com.example.llama.viewmodel.SettingsViewModel import com.example.llama.viewmodel.SettingsViewModel
import kotlin.math.sqrt
/** /**
* Screen for general app settings * Screen for general app settings
@ -67,11 +70,7 @@ fun SettingsGeneralScreen(
onCheckedChange = { viewModel.setMonitoringEnabled(it) } onCheckedChange = { viewModel.setMonitoringEnabled(it) }
) )
Spacer(modifier = Modifier.height(8.dp)) HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(8.dp))
SettingsSwitch( SettingsSwitch(
title = "Use Fahrenheit", title = "Use Fahrenheit",
@ -99,25 +98,25 @@ fun SettingsGeneralScreen(
SingleChoiceSegmentedButtonRow( SingleChoiceSegmentedButtonRow(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
SegmentedButton( ColorThemeMode.entries.forEachIndexed { index, mode ->
modifier = Modifier.weight(3f), val weight = sqrt(sqrt(mode.label.length.toFloat()))
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( SegmentedButton(
modifier = Modifier.weight(4f), modifier = Modifier.weight(weight),
selected = colorThemeMode == UserPreferences.COLOR_THEME_MODE_MATERIAL, selected = colorThemeMode == mode,
onClick = { viewModel.setColorThemeMode(UserPreferences.COLOR_THEME_MODE_MATERIAL) }, onClick = { viewModel.setColorThemeMode(mode) },
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2) shape = SegmentedButtonDefaults.itemShape(
) { index = index,
Text("Material Design") count = ColorThemeMode.entries.size
)
) {
Text(mode.label)
}
} }
} }
HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp))
// Dark theme mode // Dark theme mode
Text( Text(
text = "Dark Mode", text = "Dark Mode",
@ -135,28 +134,14 @@ fun SettingsGeneralScreen(
SingleChoiceSegmentedButtonRow( SingleChoiceSegmentedButtonRow(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
SegmentedButton( DarkThemeMode.entries.forEachIndexed { index, mode ->
selected = darkThemeMode == UserPreferences.DARK_THEME_MODE_AUTO, SegmentedButton(
onClick = { viewModel.setDarkThemeMode(UserPreferences.DARK_THEME_MODE_AUTO) }, selected = darkThemeMode == mode,
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3) onClick = { viewModel.setDarkThemeMode(mode) },
) { shape = SegmentedButtonDefaults.itemShape(index = index, count = DarkThemeMode.entries.size)
Text("Auto") ) {
} Text(mode.label)
}
SegmentedButton(
selected = darkThemeMode == UserPreferences.DARK_THEME_MODE_LIGHT,
onClick = { viewModel.setDarkThemeMode(UserPreferences.DARK_THEME_MODE_LIGHT) },
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 3)
) {
Text("Light")
}
SegmentedButton(
selected = darkThemeMode == UserPreferences.DARK_THEME_MODE_DARK,
onClick = { viewModel.setDarkThemeMode(UserPreferences.DARK_THEME_MODE_DARK) },
shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3)
) {
Text("Dark")
} }
} }
} }
@ -169,13 +154,11 @@ fun SettingsGeneralScreen(
style = MaterialTheme.typography.titleMedium style = MaterialTheme.typography.titleMedium
) )
Spacer(modifier = Modifier.height(4.dp))
Text( Text(
text = "Available hardware capabilities on your device are highlighted below:", text = "Available hardware capabilities on your device are highlighted below:",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp, bottom = 8.dp) modifier = Modifier.padding(vertical = 8.dp)
) )
supportedFeatures?.let { supportedFeatures?.let {
@ -186,7 +169,7 @@ fun SettingsGeneralScreen(
text = "Tap a feature above to learn more about how it accelerates Generative AI!", text = "Tap a feature above to learn more about how it accelerates Generative AI!",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp) modifier = Modifier.padding(vertical = 8.dp)
) )
} }
} }
@ -198,9 +181,10 @@ fun SettingsGeneralScreen(
) )
Text( Text(
text = "Version 1.0.0", text = "Version ${BuildConfig.VERSION_NAME}",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(vertical = 4.dp)
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))

View File

@ -14,9 +14,8 @@ import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext 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.ColorThemeMode
import com.example.llama.data.source.prefs.UserPreferences.Companion.COLOR_THEME_MODE_ARM import com.example.llama.data.source.prefs.DarkThemeMode
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
// -------------------- ColorScheme -------------------- // -------------------- ColorScheme --------------------
@ -143,23 +142,21 @@ internal val armDarkColorScheme: ColorScheme = darkColorScheme(
@Composable @Composable
fun LlamaTheme( fun LlamaTheme(
colorThemeMode: Int, colorThemeMode: ColorThemeMode,
darkThemeMode: Int, darkThemeMode: DarkThemeMode,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val darkTheme = when (darkThemeMode) {
UserPreferences.DARK_THEME_MODE_LIGHT -> false
UserPreferences.DARK_THEME_MODE_DARK -> true
else -> isSystemInDarkTheme()
}
val context = LocalContext.current val context = LocalContext.current
val darkTheme = when (darkThemeMode) {
DarkThemeMode.AUTO -> isSystemInDarkTheme()
DarkThemeMode.LIGHT -> false
DarkThemeMode.DARK -> true
}
val colorScheme = when(colorThemeMode) { val colorScheme = when(colorThemeMode) {
COLOR_THEME_MODE_ARM -> if (darkTheme) armDarkColorScheme else armLightColorScheme ColorThemeMode.ARM -> if (darkTheme) armDarkColorScheme else armLightColorScheme
COLOR_THEME_MODE_MATERIAL -> ColorThemeMode.MATERIAL ->
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
else -> error("Unexpected color theme $colorThemeMode")
} }
val view = LocalView.current val view = LocalView.current

View File

@ -5,6 +5,8 @@ import android.llama.cpp.TierDetection
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.llama.data.repo.ModelRepository import com.example.llama.data.repo.ModelRepository
import com.example.llama.data.source.prefs.ColorThemeMode
import com.example.llama.data.source.prefs.DarkThemeMode
import com.example.llama.data.source.prefs.UserPreferences import com.example.llama.data.source.prefs.UserPreferences
import com.example.llama.monitoring.BatteryMetrics import com.example.llama.monitoring.BatteryMetrics
import com.example.llama.monitoring.MemoryMetrics import com.example.llama.monitoring.MemoryMetrics
@ -58,11 +60,11 @@ class SettingsViewModel @Inject constructor(
val monitoringInterval: StateFlow<Long> = _monitoringInterval.asStateFlow() val monitoringInterval: StateFlow<Long> = _monitoringInterval.asStateFlow()
// User preferences: themes // User preferences: themes
private val _colorThemeMode = MutableStateFlow(UserPreferences.COLOR_THEME_MODE_ARM) private val _colorThemeMode = MutableStateFlow(ColorThemeMode.ARM)
val colorThemeMode: StateFlow<Int> = _colorThemeMode.asStateFlow() val colorThemeMode: StateFlow<ColorThemeMode> = _colorThemeMode.asStateFlow()
private val _darkThemeMode = MutableStateFlow(UserPreferences.DARK_THEME_MODE_AUTO) private val _darkThemeMode = MutableStateFlow(DarkThemeMode.AUTO)
val darkThemeMode: StateFlow<Int> = _darkThemeMode.asStateFlow() val darkThemeMode: StateFlow<DarkThemeMode> = _darkThemeMode.asStateFlow()
val detectedTier: LLamaTier? val detectedTier: LLamaTier?
get() = tierDetection.detectedTier get() = tierDetection.detectedTier
@ -80,19 +82,6 @@ class SettingsViewModel @Inject constructor(
if (_isMonitoringEnabled.value) { if (_isMonitoringEnabled.value) {
startMonitoring() startMonitoring()
} }
// viewModelScope.launch {
// launch {
// userPreferences.getColorThemeMode().collect { mode ->
// _colorThemeMode.value = mode
// }
// }
// launch {
// userPreferences.getDarkThemeMode().collect { mode ->
// _darkThemeMode.value = mode
// }
// }
// }
} }
} }
@ -171,7 +160,7 @@ class SettingsViewModel @Inject constructor(
/** /**
* Sets the color theme mode. * Sets the color theme mode.
*/ */
fun setColorThemeMode(mode: Int) { fun setColorThemeMode(mode: ColorThemeMode) {
viewModelScope.launch { viewModelScope.launch {
userPreferences.setColorThemeMode(mode) userPreferences.setColorThemeMode(mode)
_colorThemeMode.value = mode _colorThemeMode.value = mode
@ -181,7 +170,7 @@ class SettingsViewModel @Inject constructor(
/** /**
* Sets the dark theme mode. * Sets the dark theme mode.
*/ */
fun setDarkThemeMode(mode: Int) { fun setDarkThemeMode(mode: DarkThemeMode) {
viewModelScope.launch { viewModelScope.launch {
userPreferences.setDarkThemeMode(mode) userPreferences.setDarkThemeMode(mode)
_darkThemeMode.value = mode _darkThemeMode.value = mode