misc: UI polish
This commit is contained in:
parent
d211c4c605
commit
8c6e449ad2
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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() } }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue