UI: split a nested parent settings screen into separate child settings screens
This commit is contained in:
parent
65c09b2b32
commit
a7ee3d305f
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
Loading…
Reference in New Issue