diff --git a/examples/llama.android/app/src/main/java/com/example/llama/revamp/MainActivity.kt b/examples/llama.android/app/src/main/java/com/example/llama/revamp/MainActivity.kt index de0a6fd899..12cb777f44 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/revamp/MainActivity.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/revamp/MainActivity.kt @@ -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 ) } } diff --git a/examples/llama.android/app/src/main/java/com/example/llama/revamp/navigation/AppDestinations.kt b/examples/llama.android/app/src/main/java/com/example/llama/revamp/navigation/AppDestinations.kt index 9b26514db4..7a53afa95f 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/revamp/navigation/AppDestinations.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/revamp/navigation/AppDestinations.kt @@ -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() { diff --git a/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/components/NavigationDrawer.kt b/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/components/NavigationDrawer.kt index 43ccd21916..5bf6858e94 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/components/NavigationDrawer.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/components/NavigationDrawer.kt @@ -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, + 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) ) } } diff --git a/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/ModelsManagementScreen.kt b/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/ModelsManagementScreen.kt new file mode 100644 index 0000000000..8b2bbe9fcd --- /dev/null +++ b/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/ModelsManagementScreen.kt @@ -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 + ) + } + } + } + } + } +} diff --git a/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/SettingsGeneralScreen.kt b/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/SettingsGeneralScreen.kt new file mode 100644 index 0000000000..16fea75eff --- /dev/null +++ b/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/SettingsGeneralScreen.kt @@ -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() +} diff --git a/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/SettingsScreen.kt b/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/SettingsScreen.kt deleted file mode 100644 index 2552c11dcd..0000000000 --- a/examples/llama.android/app/src/main/java/com/example/llama/revamp/ui/screens/SettingsScreen.kt +++ /dev/null @@ -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() -}