UI: split a nested parent settings screen into separate child settings screens

This commit is contained in:
Han Yin 2025-04-11 21:55:04 -07:00
parent 65c09b2b32
commit a7ee3d305f
6 changed files with 533 additions and 318 deletions

View File

@ -3,6 +3,7 @@ package com.example.llama.revamp
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.compose.BackHandler
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.activity.compose.setContent
@ -24,12 +25,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.example.llama.revamp.engine.InferenceEngine
import com.example.llama.revamp.navigation.AppDestinations
import com.example.llama.revamp.navigation.NavigationActions
@ -37,10 +36,10 @@ import com.example.llama.revamp.ui.components.AppNavigationDrawer
import com.example.llama.revamp.ui.components.UnloadModelConfirmationDialog
import com.example.llama.revamp.ui.screens.BenchmarkScreen
import com.example.llama.revamp.ui.screens.ConversationScreen
import com.example.llama.revamp.ui.screens.ModeSelectionScreen
import com.example.llama.revamp.ui.screens.ModelSelectionScreen
import com.example.llama.revamp.ui.screens.SettingsScreen
import com.example.llama.revamp.ui.screens.SettingsTab
import com.example.llama.revamp.ui.screens.ModelsManagementScreen
import com.example.llama.revamp.ui.screens.ModeSelectionScreen
import com.example.llama.revamp.ui.screens.SettingsGeneralScreen
import com.example.llama.revamp.ui.theme.LlamaTheme
import com.example.llama.revamp.util.ViewModelFactoryProvider
import com.example.llama.revamp.viewmodel.MainViewModel
@ -103,7 +102,8 @@ fun AppContent() {
} else {
// Only enable drawer opening by gesture on these screens
currentRoute == AppDestinations.MODEL_SELECTION_ROUTE ||
currentRoute.startsWith(AppDestinations.SETTINGS_ROUTE)
currentRoute == AppDestinations.SETTINGS_GENERAL_ROUTE ||
currentRoute == AppDestinations.MODELS_MANAGEMENT_ROUTE
}
}
}
@ -180,7 +180,8 @@ fun AppContent() {
AppNavigationDrawer(
drawerState = drawerState,
navigationActions = navigationActions,
gesturesEnabled = drawerGesturesEnabled
gesturesEnabled = drawerGesturesEnabled,
currentRoute = currentRoute
) {
NavHost(
navController = navController,
@ -194,7 +195,7 @@ fun AppContent() {
navigationActions.navigateToModeSelection()
},
onManageModelsClicked = {
navigationActions.navigateToSettings(SettingsTab.MODEL_MANAGEMENT.name)
navigationActions.navigateToModelsManagement()
},
onMenuClicked = openDrawer,
drawerState = drawerState,
@ -255,28 +256,23 @@ fun AppContent() {
)
}
// Settings Screen
composable(
route = "${AppDestinations.SETTINGS_ROUTE}/{tab}",
arguments = listOf(
navArgument("tab") {
type = NavType.StringType
defaultValue = SettingsTab.GENERAL.name
}
)
) { backStackEntry ->
val tabName = backStackEntry.arguments?.getString("tab") ?: SettingsTab.GENERAL.name
val tab = try {
SettingsTab.valueOf(tabName)
} catch (e: IllegalArgumentException) {
SettingsTab.GENERAL
}
SettingsScreen(
selectedTab = tab,
// Settings General Screen
composable(AppDestinations.SETTINGS_GENERAL_ROUTE) {
SettingsGeneralScreen(
onBackPressed = { navController.popBackStack() },
drawerState = drawerState,
navigationActions = navigationActions
navigationActions = navigationActions,
onMenuClicked = openDrawer
)
}
// Models Management Screen
composable(AppDestinations.MODELS_MANAGEMENT_ROUTE) {
ModelsManagementScreen(
onBackPressed = { navController.popBackStack() },
drawerState = drawerState,
navigationActions = navigationActions,
onMenuClicked = openDrawer
)
}
}

View File

@ -6,11 +6,15 @@ import androidx.navigation.NavController
* Navigation destinations for the app
*/
object AppDestinations {
// Primary navigation destinations
const val MODEL_SELECTION_ROUTE = "model_selection"
const val MODE_SELECTION_ROUTE = "mode_selection"
const val CONVERSATION_ROUTE = "conversation"
const val BENCHMARK_ROUTE = "benchmark"
const val SETTINGS_ROUTE = "settings"
// Settings destinations (moved from tabs to separate routes)
const val SETTINGS_GENERAL_ROUTE = "settings_general"
const val MODELS_MANAGEMENT_ROUTE = "models_management"
}
/**
@ -37,8 +41,12 @@ class NavigationActions(private val navController: NavController) {
navController.navigate(AppDestinations.BENCHMARK_ROUTE)
}
fun navigateToSettings(tab: String = "GENERAL") {
navController.navigate("${AppDestinations.SETTINGS_ROUTE}/$tab")
fun navigateToSettingsGeneral() {
navController.navigate(AppDestinations.SETTINGS_GENERAL_ROUTE)
}
fun navigateToModelsManagement() {
navController.navigate(AppDestinations.MODELS_MANAGEMENT_ROUTE)
}
fun navigateUp() {

View File

@ -3,18 +3,21 @@ package com.example.llama.revamp.ui.components
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
@ -23,6 +26,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalConfiguration
@ -40,6 +44,7 @@ fun AppNavigationDrawer(
drawerState: DrawerState,
navigationActions: NavigationActions,
gesturesEnabled: Boolean,
currentRoute: String,
content: @Composable () -> Unit
) {
val coroutineScope = rememberCoroutineScope()
@ -63,18 +68,14 @@ fun AppNavigationDrawer(
modifier = Modifier.width(drawerWidth)
) {
DrawerContent(
onHomeClicked = {
currentRoute = currentRoute,
onNavigate = { destination ->
coroutineScope.launch {
drawerState.close()
navigationActions.navigateToModelSelection()
destination()
}
},
onSettingsClicked = {
coroutineScope.launch {
drawerState.close()
navigationActions.navigateToSettings()
}
}
navigationActions = navigationActions
)
}
},
@ -84,8 +85,9 @@ fun AppNavigationDrawer(
@Composable
private fun DrawerContent(
onHomeClicked: () -> Unit,
onSettingsClicked: () -> Unit,
currentRoute: String,
onNavigate: ((Function0<Unit>)) -> Unit,
navigationActions: NavigationActions,
modifier: Modifier = Modifier
) {
Column(
@ -93,30 +95,68 @@ private fun DrawerContent(
.fillMaxSize()
.padding(16.dp)
) {
Text(
text = "Local LLM",
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
// App Header
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
)
.padding(vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Local LLM",
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center
)
Text(
text = "v1.0.0",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
)
}
HorizontalDivider()
Spacer(modifier = Modifier.height(16.dp))
// Navigation Items
// Main Navigation Items
Text(
text = "Navigation",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)
)
DrawerNavigationItem(
icon = Icons.Default.Home,
label = "Home",
onClick = onHomeClicked
isSelected = currentRoute == com.example.llama.revamp.navigation.AppDestinations.MODEL_SELECTION_ROUTE,
onClick = { onNavigate { navigationActions.navigateToModelSelection() } }
)
Spacer(modifier = Modifier.height(24.dp))
// Settings Group
Text(
text = "Settings",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)
)
DrawerNavigationItem(
icon = Icons.Default.Settings,
label = "Settings",
onClick = onSettingsClicked
label = "General Settings",
isSelected = currentRoute == com.example.llama.revamp.navigation.AppDestinations.SETTINGS_GENERAL_ROUTE,
onClick = { onNavigate { navigationActions.navigateToSettingsGeneral() } }
)
DrawerNavigationItem(
icon = Icons.Default.Folder,
label = "Models Management",
isSelected = currentRoute == com.example.llama.revamp.navigation.AppDestinations.MODELS_MANAGEMENT_ROUTE,
onClick = { onNavigate { navigationActions.navigateToModelsManagement() } }
)
}
}
@ -125,28 +165,47 @@ private fun DrawerContent(
private fun DrawerNavigationItem(
icon: ImageVector,
label: String,
isSelected: Boolean,
onClick: () -> Unit
) {
val backgroundColor = if (isSelected) {
MaterialTheme.colorScheme.primaryContainer
} else {
MaterialTheme.colorScheme.surface
}
val contentColor = if (isSelected) {
MaterialTheme.colorScheme.onPrimaryContainer
} else {
MaterialTheme.colorScheme.onSurface
}
Surface(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(vertical = 8.dp),
color = MaterialTheme.colorScheme.surface
.padding(vertical = 4.dp),
color = backgroundColor,
shape = MaterialTheme.shapes.small
) {
Column(
modifier = Modifier.padding(8.dp)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 12.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = label,
tint = MaterialTheme.colorScheme.primary
tint = contentColor,
modifier = Modifier.size(24.dp)
)
Text(
text = label,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(start = 8.dp, top = 4.dp)
style = MaterialTheme.typography.bodyLarge,
color = contentColor,
modifier = Modifier.padding(start = 16.dp)
)
}
}

View File

@ -0,0 +1,216 @@
package com.example.llama.revamp.ui.screens
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DrawerState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.example.llama.revamp.data.model.ModelInfo
import com.example.llama.revamp.navigation.NavigationActions
import com.example.llama.revamp.ui.components.AppScaffold
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/**
* Screen for managing LLM models (view, download, delete)
*/
@Composable
fun ModelsManagementScreen(
onBackPressed: () -> Unit,
drawerState: DrawerState,
navigationActions: NavigationActions,
onMenuClicked: () -> Unit
) {
// For demo purposes, we'll use sample models
val installedModels = remember { ModelInfo.getSampleModels() }
AppScaffold(
title = "Models Management",
navigationActions = navigationActions,
onBackPressed = onBackPressed,
onMenuPressed = onMenuClicked
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(16.dp)
) {
// Summary card
Card(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "Models Storage",
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.height(8.dp))
Row(
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Storage Used: 14.6GB / 32GB",
style = MaterialTheme.typography.bodyMedium
)
Spacer(modifier = Modifier.height(4.dp))
LinearProgressIndicator(
progress = { 0.45f },
modifier = Modifier.fillMaxWidth()
)
}
OutlinedButton(
onClick = { /* Download new model */ },
modifier = Modifier.padding(start = 16.dp)
) {
Text("Add Model")
}
}
}
}
// Installed models list
Text(
text = "Installed Models",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(vertical = 8.dp)
)
LazyColumn {
items(installedModels) { model ->
ModelManagementItem(
model = model,
onInfoClick = { /* Show model details */ },
onDeleteClick = { /* Delete model */ }
)
Spacer(modifier = Modifier.height(8.dp))
}
}
}
}
}
@Composable
fun ModelManagementItem(
model: ModelInfo,
onInfoClick: () -> Unit,
onDeleteClick: () -> Unit
) {
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = model.name,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
Text(
text = "${model.parameters}${model.quantization}${model.formattedSize}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
IconButton(onClick = onInfoClick) {
Icon(
imageVector = Icons.Default.Info,
contentDescription = "Model details",
tint = MaterialTheme.colorScheme.primary
)
}
IconButton(onClick = onDeleteClick) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete model",
tint = MaterialTheme.colorScheme.error
)
}
}
HorizontalDivider(
modifier = Modifier.padding(vertical = 8.dp)
)
Row(
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Location: ${model.path}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
model.lastUsed?.let { lastUsed ->
val dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.getDefault())
Text(
text = "Last used: ${dateFormat.format(Date(lastUsed))}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}
}
}

View File

@ -0,0 +1,195 @@
package com.example.llama.revamp.ui.screens
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Card
import androidx.compose.material3.DrawerState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.llama.revamp.data.preferences.UserPreferences
import com.example.llama.revamp.monitoring.PerformanceMonitor
import com.example.llama.revamp.navigation.NavigationActions
import com.example.llama.revamp.ui.components.AppScaffold
import com.example.llama.revamp.util.ViewModelFactoryProvider
import com.example.llama.revamp.viewmodel.PerformanceViewModel
/**
* Screen for general app settings
*/
@Composable
fun SettingsGeneralScreen(
onBackPressed: () -> Unit,
drawerState: DrawerState,
navigationActions: NavigationActions,
onMenuClicked: () -> Unit
) {
// Create dependencies for PerformanceViewModel
val context = LocalContext.current
val performanceMonitor = remember { PerformanceMonitor(context) }
val userPreferences = remember { UserPreferences(context) }
// Create factory for PerformanceViewModel
val factory = remember { ViewModelFactoryProvider.getPerformanceViewModelFactory(performanceMonitor, userPreferences) }
// Get ViewModel instance with factory
val performanceViewModel: PerformanceViewModel = viewModel(factory = factory)
// Collect state from ViewModel
val isMonitoringEnabled by performanceViewModel.isMonitoringEnabled.collectAsState()
val useFahrenheit by performanceViewModel.useFahrenheitUnit.collectAsState()
AppScaffold(
title = "Settings",
navigationActions = navigationActions,
onBackPressed = onBackPressed,
onMenuPressed = onMenuClicked
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(16.dp)
.verticalScroll(rememberScrollState())
) {
SettingsCategory(title = "Performance Monitoring") {
SettingsSwitch(
title = "Enable Monitoring",
description = "Display memory, battery and temperature information",
checked = isMonitoringEnabled,
onCheckedChange = { performanceViewModel.setMonitoringEnabled(it) }
)
SettingsSwitch(
title = "Use Fahrenheit",
description = "Display temperature in Fahrenheit instead of Celsius",
checked = useFahrenheit,
onCheckedChange = { performanceViewModel.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: Implement theme switching */ }
)
}
SettingsCategory(title = "About") {
Card(
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "Local LLM",
style = MaterialTheme.typography.titleLarge
)
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.",
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
}
}
@Composable
fun SettingsCategory(
title: String,
content: @Composable () -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(bottom = 8.dp)
)
Card(
modifier = Modifier.fillMaxWidth()
) {
Column {
content()
}
}
Spacer(modifier = Modifier.height(16.dp))
}
}
@Composable
fun SettingsSwitch(
title: String,
description: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium
)
Text(
text = description,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Switch(
checked = checked,
onCheckedChange = onCheckedChange
)
}
}
HorizontalDivider()
}

View File

@ -1,259 +0,0 @@
package com.example.llama.revamp.ui.screens
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Card
import androidx.compose.material3.Divider
import androidx.compose.material3.DrawerState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.llama.revamp.data.preferences.UserPreferences
import com.example.llama.revamp.monitoring.PerformanceMonitor
import com.example.llama.revamp.navigation.NavigationActions
import com.example.llama.revamp.ui.components.AppScaffold
import com.example.llama.revamp.util.ViewModelFactoryProvider
import com.example.llama.revamp.viewmodel.PerformanceViewModel
/**
* Tabs for the settings screen.
*/
enum class SettingsTab {
GENERAL,
MODEL_MANAGEMENT,
ADVANCED
}
@Composable
fun SettingsScreen(
selectedTab: SettingsTab = SettingsTab.GENERAL,
onBackPressed: () -> Unit,
drawerState: DrawerState,
navigationActions: NavigationActions
) {
// State for tab selection
var currentTab by remember { mutableStateOf(selectedTab) }
// Create dependencies for PerformanceViewModel
val context = LocalContext.current
val performanceMonitor = remember { PerformanceMonitor(context) }
val userPreferences = remember { UserPreferences(context) }
// Create factory for PerformanceViewModel
val factory = remember { ViewModelFactoryProvider.getPerformanceViewModelFactory(performanceMonitor, userPreferences) }
// Get ViewModel instance with factory
val performanceViewModel: PerformanceViewModel = viewModel(factory = factory)
AppScaffold(
title = "Settings",
drawerState = drawerState,
navigationActions = navigationActions,
onBackPressed = onBackPressed
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
// Tabs for different settings categories
TabRow(selectedTabIndex = currentTab.ordinal) {
Tab(
selected = currentTab == SettingsTab.GENERAL,
onClick = { currentTab = SettingsTab.GENERAL },
text = { Text("General") }
)
Tab(
selected = currentTab == SettingsTab.MODEL_MANAGEMENT,
onClick = { currentTab = SettingsTab.MODEL_MANAGEMENT },
text = { Text("Models") }
)
Tab(
selected = currentTab == SettingsTab.ADVANCED,
onClick = { currentTab = SettingsTab.ADVANCED },
text = { Text("Advanced") }
)
}
// Content for the selected tab
when (currentTab) {
SettingsTab.GENERAL -> GeneralSettings(performanceViewModel)
SettingsTab.MODEL_MANAGEMENT -> ModelManagementSettings()
SettingsTab.ADVANCED -> AdvancedSettings()
}
}
}
}
@Composable
fun GeneralSettings(performanceViewModel: PerformanceViewModel) {
val isMonitoringEnabled by performanceViewModel.isMonitoringEnabled.collectAsState()
val useFahrenheit by performanceViewModel.useFahrenheitUnit.collectAsState()
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.verticalScroll(rememberScrollState())
) {
SettingsCategory(title = "Performance Monitoring") {
SettingsSwitch(
title = "Enable Monitoring",
description = "Display memory, battery and temperature information",
checked = isMonitoringEnabled,
onCheckedChange = { performanceViewModel.setMonitoringEnabled(it) }
)
SettingsSwitch(
title = "Use Fahrenheit",
description = "Display temperature in Fahrenheit instead of Celsius",
checked = useFahrenheit,
onCheckedChange = { performanceViewModel.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: Implement theme switching */ }
)
}
}
}
@Composable
fun ModelManagementSettings() {
// This would be populated with actual functionality in a real implementation
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.verticalScroll(rememberScrollState())
) {
Text(
text = "Model Management",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "This section will allow you to download, delete, and manage LLM models.",
style = MaterialTheme.typography.bodyMedium
)
}
}
@Composable
fun AdvancedSettings() {
// This would be populated with actual functionality in a real implementation
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.verticalScroll(rememberScrollState())
) {
Text(
text = "Advanced Settings",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "This section will contain advanced settings such as memory management, cache configuration, and debugging options.",
style = MaterialTheme.typography.bodyMedium
)
}
}
@Composable
fun SettingsCategory(
title: String,
content: @Composable () -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(bottom = 8.dp)
)
Card(
modifier = Modifier.fillMaxWidth()
) {
Column {
content()
}
}
Spacer(modifier = Modifier.height(16.dp))
}
}
@Composable
fun SettingsSwitch(
title: String,
description: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium
)
Text(
text = description,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Switch(
checked = checked,
onCheckedChange = onCheckedChange
)
}
}
Divider()
}