UI: polish UI for ModelsManagementScreen; inject ModelsManagementVieModel
This commit is contained in:
parent
6e82bb37d3
commit
eebc05b559
|
|
@ -73,6 +73,7 @@ fun StorageAppScaffold(
|
||||||
storageTotal: Float,
|
storageTotal: Float,
|
||||||
onNavigateBack: (() -> Unit)? = null,
|
onNavigateBack: (() -> Unit)? = null,
|
||||||
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
|
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
|
||||||
|
bottomBar: @Composable () -> Unit = {},
|
||||||
content: @Composable (PaddingValues) -> Unit
|
content: @Composable (PaddingValues) -> Unit
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
|
@ -87,6 +88,7 @@ fun StorageAppScaffold(
|
||||||
snackbarHost = {
|
snackbarHost = {
|
||||||
SnackbarHost(hostState = snackbarHostState)
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
},
|
},
|
||||||
|
bottomBar = bottomBar,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.example.llama.revamp.ui.screens
|
package com.example.llama.revamp.ui.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
|
@ -7,35 +8,52 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.Sort
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.ClearAll
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.DeleteSweep
|
||||||
|
import androidx.compose.material.icons.filled.FilterAlt
|
||||||
|
import androidx.compose.material.icons.filled.FolderOpen
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
|
import androidx.compose.material.icons.filled.SelectAll
|
||||||
|
import androidx.compose.material3.BottomAppBar
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.example.llama.revamp.data.model.ModelInfo
|
import com.example.llama.revamp.data.model.ModelInfo
|
||||||
import com.example.llama.revamp.ui.components.StorageAppScaffold
|
import com.example.llama.revamp.ui.components.StorageAppScaffold
|
||||||
|
import com.example.llama.revamp.viewmodel.ModelSortOrder
|
||||||
|
import com.example.llama.revamp.viewmodel.ModelsManagementViewModel
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import com.example.llama.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Screen for managing LLM models (view, download, delete)
|
* Screen for managing LLM models (view, download, delete)
|
||||||
|
|
@ -43,132 +61,317 @@ import java.util.Locale
|
||||||
@Composable
|
@Composable
|
||||||
fun ModelsManagementScreen(
|
fun ModelsManagementScreen(
|
||||||
onBackPressed: () -> Unit,
|
onBackPressed: () -> Unit,
|
||||||
|
viewModel: ModelsManagementViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
// For demo purposes, we'll use sample models
|
// For demo purposes, we'll use sample models
|
||||||
val installedModels = remember { ModelInfo.getSampleModels() }
|
val models by viewModel.availableModels.collectAsState()
|
||||||
|
val storageMetrics by viewModel.storageMetrics.collectAsState()
|
||||||
|
|
||||||
// Edit mode for models' batch deletion
|
// UI states
|
||||||
var isEditMode by remember { mutableStateOf(false) }
|
var isMultiSelectionMode by remember { mutableStateOf(false) }
|
||||||
|
val selectedModels = remember { mutableStateMapOf<String, ModelInfo>() }
|
||||||
|
var showSortMenu by remember { mutableStateOf(false) }
|
||||||
|
var showAddModelMenu by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Calculate storage info
|
val exitSelectionMode = {
|
||||||
val storageUsed = 14.6f // This would be calculated from actual models
|
isMultiSelectionMode = false
|
||||||
val storageTotal = 32.0f // This would be from device storage info
|
selectedModels.clear()
|
||||||
|
}
|
||||||
|
|
||||||
StorageAppScaffold(
|
StorageAppScaffold(
|
||||||
title = "Models Management",
|
title = "Models Management",
|
||||||
storageUsed = storageUsed,
|
storageUsed = storageMetrics.usedGB,
|
||||||
storageTotal = storageTotal,
|
storageTotal = storageMetrics.totalGB,
|
||||||
onNavigateBack = onBackPressed,
|
onNavigateBack = onBackPressed,
|
||||||
) { paddingValues ->
|
bottomBar = {
|
||||||
Column(
|
BottomAppBar(
|
||||||
modifier = Modifier
|
actions = {
|
||||||
.fillMaxSize()
|
if (isMultiSelectionMode) {
|
||||||
.padding(paddingValues)
|
// Multi-selection mode actions
|
||||||
.padding(16.dp)
|
IconButton(onClick = {
|
||||||
) {
|
// Select all
|
||||||
// Summary card
|
selectedModels.putAll(models.map { it.id to it })
|
||||||
Card(
|
}) {
|
||||||
modifier = Modifier
|
Icon(
|
||||||
.fillMaxWidth()
|
imageVector = Icons.Default.SelectAll,
|
||||||
.padding(bottom = 16.dp)
|
contentDescription = "Select all"
|
||||||
) {
|
|
||||||
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(
|
IconButton(onClick = {
|
||||||
onClick = { /* Download new model */ },
|
// Deselect all
|
||||||
modifier = Modifier.padding(start = 16.dp)
|
selectedModels.clear()
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.ClearAll,
|
||||||
|
contentDescription = "Deselect all"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
// Delete selected
|
||||||
|
if (selectedModels.isNotEmpty()) {
|
||||||
|
viewModel.deleteModels(selectedModels)
|
||||||
|
exitSelectionMode()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = selectedModels.isNotEmpty()
|
||||||
) {
|
) {
|
||||||
Text("Add Model")
|
Icon(
|
||||||
|
imageVector = Icons.Default.Delete,
|
||||||
|
contentDescription = "Delete selected",
|
||||||
|
tint = if (selectedModels.isNotEmpty())
|
||||||
|
MaterialTheme.colorScheme.error
|
||||||
|
else
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.38f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default mode actions
|
||||||
|
IconButton(onClick = { showSortMenu = true }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.Sort,
|
||||||
|
contentDescription = "Sort models"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort dropdown menu
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = showSortMenu,
|
||||||
|
onDismissRequest = { showSortMenu = false }
|
||||||
|
) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Name (A-Z)") },
|
||||||
|
onClick = {
|
||||||
|
viewModel.setSortOrder(ModelSortOrder.NAME_ASC)
|
||||||
|
showSortMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Name (Z-A)") },
|
||||||
|
onClick = {
|
||||||
|
viewModel.setSortOrder(ModelSortOrder.NAME_DESC)
|
||||||
|
showSortMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Size (Largest first)") },
|
||||||
|
onClick = {
|
||||||
|
viewModel.setSortOrder(ModelSortOrder.SIZE_DESC)
|
||||||
|
showSortMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Size (Smallest first)") },
|
||||||
|
onClick = {
|
||||||
|
viewModel.setSortOrder(ModelSortOrder.SIZE_ASC)
|
||||||
|
showSortMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Last used") },
|
||||||
|
onClick = {
|
||||||
|
viewModel.setSortOrder(ModelSortOrder.LAST_USED)
|
||||||
|
showSortMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(onClick = { /* Filter action - stub for now */ }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.FilterAlt,
|
||||||
|
contentDescription = "Filter models"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(onClick = {
|
||||||
|
isMultiSelectionMode = true
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.DeleteSweep,
|
||||||
|
contentDescription = "Delete models"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
floatingActionButton = {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
if (isMultiSelectionMode) {
|
||||||
|
exitSelectionMode()
|
||||||
|
} else {
|
||||||
|
showAddModelMenu = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
containerColor = MaterialTheme.colorScheme.primaryContainer
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (isMultiSelectionMode) Icons.Default.Close else Icons.Default.Add,
|
||||||
|
contentDescription = if (isMultiSelectionMode) "Exit selection mode" else "Add model"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Installed models list
|
// Add model dropdown menu
|
||||||
Text(
|
DropdownMenu(
|
||||||
text = "Installed Models",
|
expanded = showAddModelMenu,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
onDismissRequest = { showAddModelMenu = false }
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Import local model") },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.FolderOpen,
|
||||||
|
contentDescription = "Import a local model on the device"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
viewModel.importLocalModel()
|
||||||
|
showAddModelMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Download from HuggingFace") },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.logo_huggingface),
|
||||||
|
contentDescription = "Browse and download a model from HuggingFace",
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
tint = Color.Unspecified,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
viewModel.importFromHuggingFace()
|
||||||
|
showAddModelMenu = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
},
|
||||||
LazyColumn {
|
) { paddingValues ->
|
||||||
items(installedModels) { model ->
|
// Main content
|
||||||
ModelManagementItem(
|
ModelList(
|
||||||
model = model,
|
models = models,
|
||||||
onInfoClick = { /* Show model details */ },
|
isMultiSelectionMode = isMultiSelectionMode,
|
||||||
onDeleteClick = { /* Delete model */ }
|
selectedModels = selectedModels,
|
||||||
)
|
onModelClick = { modelId ->
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
if (isMultiSelectionMode) {
|
||||||
|
// Toggle selection
|
||||||
|
if (selectedModels.contains(modelId)) {
|
||||||
|
selectedModels.remove(modelId)
|
||||||
|
} else {
|
||||||
|
selectedModels.put(modelId, models.first { it.id == modelId } )
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// View model details
|
||||||
|
viewModel.viewModelDetails(modelId)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onModelInfoClick = { modelId ->
|
||||||
|
viewModel.viewModelDetails(modelId)
|
||||||
|
},
|
||||||
|
onModelDeleteClick = { modelId ->
|
||||||
|
viewModel.deleteModel(modelId)
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(paddingValues)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ModelList(
|
||||||
|
models: List<ModelInfo>,
|
||||||
|
isMultiSelectionMode: Boolean,
|
||||||
|
selectedModels: Map<String, ModelInfo>,
|
||||||
|
onModelClick: (String) -> Unit,
|
||||||
|
onModelInfoClick: (String) -> Unit,
|
||||||
|
onModelDeleteClick: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = models,
|
||||||
|
key = { it.id }
|
||||||
|
) { model ->
|
||||||
|
ModelItem(
|
||||||
|
model = model,
|
||||||
|
isMultiSelectionMode = isMultiSelectionMode,
|
||||||
|
isSelected = selectedModels.contains(model.id),
|
||||||
|
onClick = { onModelClick(model.id) },
|
||||||
|
onInfoClick = { onModelInfoClick(model.id) },
|
||||||
|
onDeleteClick = { onModelDeleteClick(model.id) }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ModelManagementItem(
|
private fun ModelItem(
|
||||||
model: ModelInfo,
|
model: ModelInfo,
|
||||||
|
isMultiSelectionMode: Boolean,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
onInfoClick: () -> Unit,
|
onInfoClick: () -> Unit,
|
||||||
onDeleteClick: () -> Unit
|
onDeleteClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
// Model item implementation with selection support
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
|
.fillMaxWidth()
|
||||||
|
.clickable(onClick = onClick),
|
||||||
|
colors = if (isSelected && isMultiSelectionMode)
|
||||||
|
CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
|
||||||
|
else
|
||||||
|
CardDefaults.cardColors()
|
||||||
) {
|
) {
|
||||||
Column(
|
Row(
|
||||||
modifier = Modifier.padding(16.dp)
|
modifier = Modifier.padding(16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Row(
|
// Show checkbox in selection mode
|
||||||
verticalAlignment = Alignment.CenterVertically
|
if (isMultiSelectionMode) {
|
||||||
) {
|
Checkbox(
|
||||||
Column(
|
checked = isSelected,
|
||||||
modifier = Modifier.weight(1f)
|
onCheckedChange = { onClick() },
|
||||||
) {
|
modifier = Modifier.padding(end = 8.dp)
|
||||||
Text(
|
)
|
||||||
text = model.name,
|
}
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
|
|
||||||
|
// Model info
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
Text(
|
||||||
|
text = model.name,
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "${model.parameters} • ${model.quantization} • ${model.formattedSize}",
|
||||||
|
style = MaterialTheme.typography.bodySmall
|
||||||
|
)
|
||||||
|
|
||||||
|
model.lastUsed?.let { lastUsed ->
|
||||||
|
val dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.getDefault())
|
||||||
Text(
|
Text(
|
||||||
text = "${model.parameters} • ${model.quantization} • ${model.formattedSize}",
|
text = "Last used: ${dateFormat.format(Date(lastUsed))}",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only show action buttons in non-selection mode
|
||||||
|
if (!isMultiSelectionMode) {
|
||||||
IconButton(onClick = onInfoClick) {
|
IconButton(onClick = onInfoClick) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Info,
|
imageVector = Icons.Default.Info,
|
||||||
contentDescription = "Model details",
|
contentDescription = "Model details"
|
||||||
tint = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,35 +383,6 @@ fun ModelManagementItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,10 @@ class ModelsManagementViewModel @Inject constructor(
|
||||||
// TODO-han.yin: Stub for now. Would open file picker and import model
|
// TODO-han.yin: Stub for now. Would open file picker and import model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun importFromHuggingFace() {
|
||||||
|
// TODO-han.yin: Stub for now. Would need to investigate HuggingFace APIs
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateStorageMetrics() {
|
private fun updateStorageMetrics() {
|
||||||
// Recalculate storage metrics after model changes
|
// Recalculate storage metrics after model changes
|
||||||
// TODO-han.yin: Stub for now. Would query actual storage
|
// TODO-han.yin: Stub for now. Would query actual storage
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="95dp"
|
||||||
|
android:height="88dp"
|
||||||
|
android:viewportWidth="95"
|
||||||
|
android:viewportHeight="88">
|
||||||
|
<path
|
||||||
|
android:pathData="M47.21,76.5a34.75,34.75 0,1 0,0 -69.5,34.75 34.75,0 0,0 0,69.5Z"
|
||||||
|
android:fillColor="#FFD21E"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M81.96,41.75a34.75,34.75 0,1 0,-69.5 0,34.75 34.75,0 0,0 69.5,0ZM8.46,41.75a38.75,38.75 0,1 1,77.5 0,38.75 38.75,0 0,1 -77.5,0Z"
|
||||||
|
android:fillColor="#FF9D0B"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M58.5,32.3c1.28,0.44 1.78,3.06 3.07,2.38a5,5 0,1 0,-6.76 -2.07c0.61,1.15 2.55,-0.72 3.7,-0.32ZM34.95,32.3c-1.28,0.44 -1.79,3.06 -3.07,2.38a5,5 0,1 1,6.76 -2.07c-0.61,1.15 -2.56,-0.72 -3.7,-0.32Z"
|
||||||
|
android:fillColor="#3A3B45"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M46.96,56.29c9.83,0 13,-8.76 13,-13.26 0,-2.34 -1.57,-1.6 -4.09,-0.36 -2.33,1.15 -5.46,2.74 -8.9,2.74 -7.19,0 -13,-6.88 -13,-2.38s3.16,13.26 13,13.26Z"
|
||||||
|
android:fillColor="#FF323D"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M39.43,54a8.7,8.7 0,0 1,5.3 -4.49c0.4,-0.12 0.81,0.57 1.24,1.28 0.4,0.68 0.82,1.37 1.24,1.37 0.45,0 0.9,-0.68 1.33,-1.35 0.45,-0.7 0.89,-1.38 1.32,-1.25a8.61,8.61 0,0 1,5 4.17c3.73,-2.94 5.1,-7.74 5.1,-10.7 0,-2.34 -1.57,-1.6 -4.09,-0.36l-0.14,0.07c-2.31,1.15 -5.39,2.67 -8.77,2.67s-6.45,-1.52 -8.77,-2.67c-2.6,-1.29 -4.23,-2.1 -4.23,0.29 0,3.05 1.46,8.06 5.47,10.97Z"
|
||||||
|
android:fillColor="#3A3B45"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M70.71,37a3.25,3.25 0,1 0,0 -6.5,3.25 3.25,0 0,0 0,6.5ZM24.21,37a3.25,3.25 0,1 0,0 -6.5,3.25 3.25,0 0,0 0,6.5ZM17.52,48c-1.62,0 -3.06,0.66 -4.07,1.87a5.97,5.97 0,0 0,-1.33 3.76,7.1 7.1,0 0,0 -1.94,-0.3c-1.55,0 -2.95,0.59 -3.94,1.66a5.8,5.8 0,0 0,-0.8 7,5.3 5.3,0 0,0 -1.79,2.82c-0.24,0.9 -0.48,2.8 0.8,4.74a5.22,5.22 0,0 0,-0.37 5.02c1.02,2.32 3.57,4.14 8.52,6.1 3.07,1.22 5.89,2 5.91,2.01a44.33,44.33 0,0 0,10.93 1.6c5.86,0 10.05,-1.8 12.46,-5.34 3.88,-5.69 3.33,-10.9 -1.7,-15.92 -2.77,-2.78 -4.62,-6.87 -5,-7.77 -0.78,-2.66 -2.84,-5.62 -6.25,-5.62a5.7,5.7 0,0 0,-4.6 2.46c-1,-1.26 -1.98,-2.25 -2.86,-2.82A7.4,7.4 0,0 0,17.52 48ZM17.52,52c0.51,0 1.14,0.22 1.82,0.65 2.14,1.36 6.25,8.43 7.76,11.18 0.5,0.92 1.37,1.31 2.14,1.31 1.55,0 2.75,-1.53 0.15,-3.48 -3.92,-2.93 -2.55,-7.72 -0.68,-8.01 0.08,-0.02 0.17,-0.02 0.24,-0.02 1.7,0 2.45,2.93 2.45,2.93s2.2,5.52 5.98,9.3c3.77,3.77 3.97,6.8 1.22,10.83 -1.88,2.75 -5.47,3.58 -9.16,3.58 -3.81,0 -7.73,-0.9 -9.92,-1.46 -0.11,-0.03 -13.45,-3.8 -11.76,-7 0.28,-0.54 0.75,-0.76 1.34,-0.76 2.38,0 6.7,3.54 8.57,3.54 0.41,0 0.7,-0.17 0.83,-0.6 0.79,-2.85 -12.06,-4.05 -10.98,-8.17 0.2,-0.73 0.71,-1.02 1.44,-1.02 3.14,0 10.2,5.53 11.68,5.53 0.11,0 0.2,-0.03 0.24,-0.1 0.74,-1.2 0.33,-2.04 -4.9,-5.2 -5.21,-3.16 -8.88,-5.06 -6.8,-7.33 0.24,-0.26 0.58,-0.38 1,-0.38 3.17,0 10.66,6.82 10.66,6.82s2.02,2.1 3.25,2.1c0.28,0 0.52,-0.1 0.68,-0.38 0.86,-1.46 -8.06,-8.22 -8.56,-11.01 -0.34,-1.9 0.24,-2.85 1.31,-2.85Z"
|
||||||
|
android:fillColor="#FF9D0B"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M38.6,76.69c2.75,-4.04 2.55,-7.07 -1.22,-10.84 -3.78,-3.77 -5.98,-9.3 -5.98,-9.3s-0.82,-3.2 -2.69,-2.9c-1.87,0.3 -3.24,5.08 0.68,8.01 3.91,2.93 -0.78,4.92 -2.29,2.17 -1.5,-2.75 -5.62,-9.82 -7.76,-11.18 -2.13,-1.35 -3.63,-0.6 -3.13,2.2 0.5,2.79 9.43,9.55 8.56,11 -0.87,1.47 -3.93,-1.71 -3.93,-1.71s-9.57,-8.71 -11.66,-6.44c-2.08,2.27 1.59,4.17 6.8,7.33 5.23,3.16 5.64,4 4.9,5.2 -0.75,1.2 -12.28,-8.53 -13.36,-4.4 -1.08,4.11 11.77,5.3 10.98,8.15 -0.8,2.85 -9.06,-5.38 -10.74,-2.18 -1.7,3.21 11.65,6.98 11.76,7.01 4.3,1.12 15.25,3.49 19.08,-2.12Z"
|
||||||
|
android:fillColor="#FFD21E"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M77.4,48c1.62,0 3.07,0.66 4.07,1.87a5.97,5.97 0,0 1,1.33 3.76,7.1 7.1,0 0,1 1.95,-0.3c1.55,0 2.95,0.59 3.94,1.66a5.8,5.8 0,0 1,0.8 7,5.3 5.3,0 0,1 1.78,2.82c0.24,0.9 0.48,2.8 -0.8,4.74a5.22,5.22 0,0 1,0.37 5.02c-1.02,2.32 -3.57,4.14 -8.51,6.1 -3.08,1.22 -5.9,2 -5.92,2.01a44.33,44.33 0,0 1,-10.93 1.6c-5.86,0 -10.05,-1.8 -12.46,-5.34 -3.88,-5.69 -3.33,-10.9 1.7,-15.92 2.78,-2.78 4.63,-6.87 5.01,-7.77 0.78,-2.66 2.83,-5.62 6.24,-5.62a5.7,5.7 0,0 1,4.6 2.46c1,-1.26 1.98,-2.25 2.87,-2.82A7.4,7.4 0,0 1,77.4 48ZM77.4,52c-0.51,0 -1.13,0.22 -1.82,0.65 -2.13,1.36 -6.25,8.43 -7.76,11.18a2.43,2.43 0,0 1,-2.14 1.31c-1.54,0 -2.75,-1.53 -0.14,-3.48 3.91,-2.93 2.54,-7.72 0.67,-8.01a1.54,1.54 0,0 0,-0.24 -0.02c-1.7,0 -2.45,2.93 -2.45,2.93s-2.2,5.52 -5.97,9.3c-3.78,3.77 -3.98,6.8 -1.22,10.83 1.87,2.75 5.47,3.58 9.15,3.58 3.82,0 7.73,-0.9 9.93,-1.46 0.1,-0.03 13.45,-3.8 11.76,-7 -0.29,-0.54 -0.75,-0.76 -1.34,-0.76 -2.38,0 -6.71,3.54 -8.57,3.54 -0.42,0 -0.71,-0.17 -0.83,-0.6 -0.8,-2.85 12.05,-4.05 10.97,-8.17 -0.19,-0.73 -0.7,-1.02 -1.44,-1.02 -3.14,0 -10.2,5.53 -11.68,5.53 -0.1,0 -0.19,-0.03 -0.23,-0.1 -0.74,-1.2 -0.34,-2.04 4.88,-5.2 5.23,-3.16 8.9,-5.06 6.8,-7.33 -0.23,-0.26 -0.57,-0.38 -0.98,-0.38 -3.18,0 -10.67,6.82 -10.67,6.82s-2.02,2.1 -3.24,2.1a0.74,0.74 0,0 1,-0.68 -0.38c-0.87,-1.46 8.05,-8.22 8.55,-11.01 0.34,-1.9 -0.24,-2.85 -1.31,-2.85Z"
|
||||||
|
android:fillColor="#FF9D0B"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M56.33,76.69c-2.75,-4.04 -2.56,-7.07 1.22,-10.84 3.77,-3.77 5.97,-9.3 5.97,-9.3s0.82,-3.2 2.7,-2.9c1.86,0.3 3.23,5.08 -0.68,8.01 -3.92,2.93 0.78,4.92 2.28,2.17 1.51,-2.75 5.63,-9.82 7.76,-11.18 2.13,-1.35 3.64,-0.6 3.13,2.2 -0.5,2.79 -9.42,9.55 -8.55,11 0.86,1.47 3.92,-1.71 3.92,-1.71s9.58,-8.71 11.66,-6.44c2.08,2.27 -1.58,4.17 -6.8,7.33 -5.23,3.16 -5.63,4 -4.9,5.2 0.75,1.2 12.28,-8.53 13.36,-4.4 1.08,4.11 -11.76,5.3 -10.97,8.15 0.8,2.85 9.05,-5.38 10.74,-2.18 1.69,3.21 -11.65,6.98 -11.76,7.01 -4.31,1.12 -15.26,3.49 -19.08,-2.12Z"
|
||||||
|
android:fillColor="#FFD21E"/>
|
||||||
|
</vector>
|
||||||
Loading…
Reference in New Issue