misc: UI polish

This commit is contained in:
Han Yin 2025-06-27 12:02:44 -07:00
parent d211c4c605
commit 8c6e449ad2
4 changed files with 135 additions and 153 deletions

View File

@ -504,13 +504,6 @@ fun AppContent(
) )
} }
// Settings General Screen
composable(AppDestinations.SETTINGS_GENERAL_ROUTE) {
SettingsGeneralScreen(
viewModel = settingsViewModel
)
}
// Models Management Screen // Models Management Screen
composable(AppDestinations.MODELS_MANAGEMENT_ROUTE) { composable(AppDestinations.MODELS_MANAGEMENT_ROUTE) {
ModelsManagementScreen( ModelsManagementScreen(
@ -518,6 +511,13 @@ fun AppContent(
viewModel = modelsManagementViewModel viewModel = modelsManagementViewModel
) )
} }
// General Settings Screen
composable(AppDestinations.SETTINGS_GENERAL_ROUTE) {
SettingsGeneralScreen(
viewModel = settingsViewModel
)
}
} }
} }
} }

View File

@ -23,16 +23,18 @@ class UserPreferences @Inject constructor (
) { ) {
companion object { companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
// Performance monitoring preferences // Performance monitoring preferences
val PERFORMANCE_MONITORING_ENABLED = booleanPreferencesKey("performance_monitoring_enabled") private const val DATASTORE_SETTINGS = "settings"
val USE_FAHRENHEIT_TEMPERATURE = booleanPreferencesKey("use_fahrenheit_temperature") private val Context.settingsDataStore: DataStore<Preferences>
val MONITORING_INTERVAL_MS = longPreferencesKey("monitoring_interval_ms") by preferencesDataStore(name = DATASTORE_SETTINGS)
val THEME_MODE = intPreferencesKey("theme_mode")
// Default values private val PERFORMANCE_MONITORING_ENABLED = booleanPreferencesKey("performance_monitoring_enabled")
const val DEFAULT_MONITORING_INTERVAL_MS = 5000L private val USE_FAHRENHEIT_TEMPERATURE = booleanPreferencesKey("use_fahrenheit_temperature")
private val MONITORING_INTERVAL_MS = longPreferencesKey("monitoring_interval_ms")
private val THEME_MODE = intPreferencesKey("theme_mode")
// Constants
private const val DEFAULT_MONITORING_INTERVAL_MS = 5000L
// Theme mode values // Theme mode values
const val THEME_MODE_AUTO = 0 const val THEME_MODE_AUTO = 0
@ -44,7 +46,7 @@ class UserPreferences @Inject constructor (
* Gets whether performance monitoring is enabled. * Gets whether performance monitoring is enabled.
*/ */
fun isPerformanceMonitoringEnabled(): Flow<Boolean> { fun isPerformanceMonitoringEnabled(): Flow<Boolean> {
return context.dataStore.data.map { preferences -> return context.settingsDataStore.data.map { preferences ->
preferences[PERFORMANCE_MONITORING_ENABLED] != false preferences[PERFORMANCE_MONITORING_ENABLED] != false
} }
} }
@ -53,7 +55,7 @@ class UserPreferences @Inject constructor (
* Sets whether performance monitoring is enabled. * Sets whether performance monitoring is enabled.
*/ */
suspend fun setPerformanceMonitoringEnabled(enabled: Boolean) { suspend fun setPerformanceMonitoringEnabled(enabled: Boolean) {
context.dataStore.edit { preferences -> context.settingsDataStore.edit { preferences ->
preferences[PERFORMANCE_MONITORING_ENABLED] = enabled preferences[PERFORMANCE_MONITORING_ENABLED] = enabled
} }
} }
@ -62,7 +64,7 @@ class UserPreferences @Inject constructor (
* Gets whether temperature should be displayed in Fahrenheit. * Gets whether temperature should be displayed in Fahrenheit.
*/ */
fun usesFahrenheitTemperature(): Flow<Boolean> { fun usesFahrenheitTemperature(): Flow<Boolean> {
return context.dataStore.data.map { preferences -> return context.settingsDataStore.data.map { preferences ->
preferences[USE_FAHRENHEIT_TEMPERATURE] == true preferences[USE_FAHRENHEIT_TEMPERATURE] == true
} }
} }
@ -71,7 +73,7 @@ class UserPreferences @Inject constructor (
* Sets whether temperature should be displayed in Fahrenheit. * Sets whether temperature should be displayed in Fahrenheit.
*/ */
suspend fun setUseFahrenheitTemperature(useFahrenheit: Boolean) { suspend fun setUseFahrenheitTemperature(useFahrenheit: Boolean) {
context.dataStore.edit { preferences -> context.settingsDataStore.edit { preferences ->
preferences[USE_FAHRENHEIT_TEMPERATURE] = useFahrenheit preferences[USE_FAHRENHEIT_TEMPERATURE] = useFahrenheit
} }
} }
@ -82,7 +84,7 @@ class UserPreferences @Inject constructor (
* TODO-han.yin: replace with Enum value instead of millisecond value * TODO-han.yin: replace with Enum value instead of millisecond value
*/ */
fun getMonitoringInterval(): Flow<Long> { fun getMonitoringInterval(): Flow<Long> {
return context.dataStore.data.map { preferences -> return context.settingsDataStore.data.map { preferences ->
preferences[MONITORING_INTERVAL_MS] ?: DEFAULT_MONITORING_INTERVAL_MS preferences[MONITORING_INTERVAL_MS] ?: DEFAULT_MONITORING_INTERVAL_MS
} }
} }
@ -91,7 +93,7 @@ class UserPreferences @Inject constructor (
* Sets the monitoring interval in milliseconds. * Sets the monitoring interval in milliseconds.
*/ */
suspend fun setMonitoringInterval(intervalMs: Long) { suspend fun setMonitoringInterval(intervalMs: Long) {
context.dataStore.edit { preferences -> context.settingsDataStore.edit { preferences ->
preferences[MONITORING_INTERVAL_MS] = intervalMs preferences[MONITORING_INTERVAL_MS] = intervalMs
} }
} }
@ -100,7 +102,7 @@ class UserPreferences @Inject constructor (
* Gets the current theme mode. * Gets the current theme mode.
*/ */
fun getThemeMode(): Flow<Int> { fun getThemeMode(): Flow<Int> {
return context.dataStore.data.map { preferences -> return context.settingsDataStore.data.map { preferences ->
preferences[THEME_MODE] ?: THEME_MODE_AUTO preferences[THEME_MODE] ?: THEME_MODE_AUTO
} }
} }
@ -109,7 +111,7 @@ class UserPreferences @Inject constructor (
* Sets the theme mode. * Sets the theme mode.
*/ */
suspend fun setThemeMode(mode: Int) { suspend fun setThemeMode(mode: Int) {
context.dataStore.edit { preferences -> context.settingsDataStore.edit { preferences ->
preferences[THEME_MODE] = mode preferences[THEME_MODE] = mode
} }
} }

View File

@ -147,19 +147,19 @@ private fun DrawerContent(
modifier = Modifier.padding(start = 8.dp, bottom = 8.dp) modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)
) )
DrawerNavigationItem(
icon = Icons.Default.Settings,
label = "General Settings",
isSelected = currentRoute == AppDestinations.SETTINGS_GENERAL_ROUTE,
onClick = { onNavigate { navigationActions.navigateToSettingsGeneral() } }
)
DrawerNavigationItem( DrawerNavigationItem(
icon = Icons.Default.Folder, icon = Icons.Default.Folder,
label = "Models", label = "Models",
isSelected = currentRoute == AppDestinations.MODELS_MANAGEMENT_ROUTE, isSelected = currentRoute == AppDestinations.MODELS_MANAGEMENT_ROUTE,
onClick = { onNavigate { navigationActions.navigateToModelsManagement() } } onClick = { onNavigate { navigationActions.navigateToModelsManagement() } }
) )
DrawerNavigationItem(
icon = Icons.Default.Settings,
label = "General Settings",
isSelected = currentRoute == AppDestinations.SETTINGS_GENERAL_ROUTE,
onClick = { onNavigate { navigationActions.navigateToSettingsGeneral() } }
)
} }
} }

View File

@ -55,6 +55,12 @@ fun SettingsGeneralScreen(
onCheckedChange = { viewModel.setMonitoringEnabled(it) } onCheckedChange = { viewModel.setMonitoringEnabled(it) }
) )
Spacer(modifier = Modifier.height(8.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(8.dp))
SettingsSwitch( SettingsSwitch(
title = "Use Fahrenheit", title = "Use Fahrenheit",
description = "Display temperature in Fahrenheit instead of Celsius", description = "Display temperature in Fahrenheit instead of Celsius",
@ -64,115 +70,101 @@ fun SettingsGeneralScreen(
} }
SettingsCategory(title = "Theme") { SettingsCategory(title = "Theme") {
Column( Text(
modifier = Modifier text = "Theme Mode",
.fillMaxWidth() style = MaterialTheme.typography.titleMedium
.padding(16.dp) )
Text(
text = "Follow system setting or override with your choice",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(16.dp))
SingleChoiceSegmentedButtonRow(
modifier = Modifier.fillMaxWidth()
) { ) {
Text( SegmentedButton(
text = "Theme Mode", selected = themeMode == UserPreferences.THEME_MODE_AUTO,
style = MaterialTheme.typography.titleMedium onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_AUTO) },
) shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3)
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( Text("Auto")
selected = themeMode == UserPreferences.THEME_MODE_AUTO, }
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_AUTO) },
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 3)
) {
Text("Auto")
}
SegmentedButton( SegmentedButton(
selected = themeMode == UserPreferences.THEME_MODE_LIGHT, selected = themeMode == UserPreferences.THEME_MODE_LIGHT,
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_LIGHT) }, onClick = { viewModel.setThemeMode(UserPreferences.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 = themeMode == UserPreferences.THEME_MODE_DARK,
onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_DARK) }, onClick = { viewModel.setThemeMode(UserPreferences.THEME_MODE_DARK) },
shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3) shape = SegmentedButtonDefaults.itemShape(index = 2, count = 3)
) { ) {
Text("Dark") Text("Dark")
}
} }
} }
} }
// ARM Features Visualizer with Tier Information description // ARM Features Visualizer with Tier Information description
SettingsCategory(title = "About your device") { SettingsCategory(title = "About your device") {
Column( Text(
modifier = Modifier.padding(16.dp) text = "ARM Capabilities",
) { style = MaterialTheme.typography.titleMedium
Text( )
text = "ARM Capabilities",
style = MaterialTheme.typography.titleLarge
)
Text( Text(
text = "Hardware-accelerated AI features", text = "Hardware-accelerated AI features",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
detectedTier?.let { tier -> detectedTier?.let { tier ->
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
ArmFeaturesVisualizerClickable(detectedTier = detectedTier) ArmFeaturesVisualizerClickable(detectedTier = detectedTier)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Optimization Tier: ${tier.name}",
style = MaterialTheme.typography.bodyLarge
)
Text(
text = tier.description,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
)
}
}
}
SettingsCategory(title = "About this app") {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = APP_NAME,
style = MaterialTheme.typography.titleLarge
)
Text(
text = "Version 1.0.0",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
text = "Local inference for LLM models on your device powered by Arm® technologies.", text = "Optimization Tier: ${tier.name}",
style = MaterialTheme.typography.bodyLarge style = MaterialTheme.typography.titleMedium
)
Text(
text = tier.description,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
) )
} }
} }
SettingsCategory(title = "About this app") {
Text(
text = APP_NAME,
style = MaterialTheme.typography.titleMedium
)
Text(
text = "Version 1.0.0",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Local inference for LLM models on your device powered by Arm® technologies.",
style = MaterialTheme.typography.bodyMedium
)
}
} }
} }
@ -182,20 +174,16 @@ fun SettingsCategory(
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp)
.fillMaxWidth()
.padding(vertical = 8.dp)
) { ) {
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(bottom = 8.dp) modifier = Modifier.padding(bottom = 8.dp)
) )
Card( Card(modifier = Modifier.fillMaxWidth()) {
modifier = Modifier.fillMaxWidth() Column( modifier = Modifier.fillMaxWidth().padding(16.dp)) {
) {
Column {
content() content()
} }
} }
@ -211,36 +199,28 @@ fun SettingsSwitch(
checked: Boolean, checked: Boolean,
onCheckedChange: (Boolean) -> Unit onCheckedChange: (Boolean) -> Unit
) { ) {
Column( Row(
modifier = Modifier verticalAlignment = Alignment.CenterVertically,
.fillMaxWidth() modifier = Modifier.fillMaxWidth()
.padding(16.dp)
) { ) {
Row( Column(
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)
modifier = Modifier.fillMaxWidth()
) { ) {
Column( Text(
modifier = Modifier.weight(1f) text = title,
) { style = MaterialTheme.typography.titleMedium
Text( )
text = title,
style = MaterialTheme.typography.titleMedium
)
Text( Text(
text = description, text = description,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Switch(
checked = checked,
onCheckedChange = onCheckedChange
) )
} }
}
HorizontalDivider() Switch(
checked = checked,
onCheckedChange = onCheckedChange
)
}
} }