UI: extract a reusable InfoAlertDialog
This commit is contained in:
parent
a4459b22d1
commit
0c6ce7b9a3
|
|
@ -3,10 +3,12 @@ package com.example.llama.ui.components
|
|||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -18,18 +20,95 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
|
||||
data class InfoAction(
|
||||
val label: String,
|
||||
val icon: ImageVector,
|
||||
val label: String,
|
||||
val onAction: () -> Unit
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun InfoAlertDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
isCritical: Boolean = false,
|
||||
allowDismiss: Boolean = false,
|
||||
onDismiss: () -> Unit = {},
|
||||
icon: ImageVector,
|
||||
title: String,
|
||||
message: String? = null,
|
||||
action: InfoAction? = null,
|
||||
confirmButton: @Composable () -> Unit = {},
|
||||
) {
|
||||
AlertDialog(
|
||||
modifier = modifier,
|
||||
onDismissRequest = onDismiss,
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = allowDismiss,
|
||||
dismissOnClickOutside = allowDismiss,
|
||||
),
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(64.dp),
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
message?.let {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = it,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
action?.let {
|
||||
Button(
|
||||
modifier = Modifier.padding(top = 24.dp),
|
||||
onClick = it.onAction,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = it.icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(it.label)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
containerColor = if (isCritical) MaterialTheme.colorScheme.errorContainer else AlertDialogDefaults.containerColor,
|
||||
iconContentColor = if (isCritical) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
|
||||
titleContentColor = if (isCritical) MaterialTheme.colorScheme.onErrorContainer else AlertDialogDefaults.titleContentColor,
|
||||
textContentColor = if (isCritical) MaterialTheme.colorScheme.onErrorContainer else AlertDialogDefaults.textContentColor,
|
||||
confirmButton = confirmButton,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InfoView(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
icon: ImageVector,
|
||||
title: String,
|
||||
message: String? = null,
|
||||
action: InfoAction? = null
|
||||
) {
|
||||
|
|
@ -52,8 +131,8 @@ fun InfoView(
|
|||
@Composable
|
||||
fun InfoView(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
icon: @Composable () -> Unit,
|
||||
title: String,
|
||||
message: String? = null,
|
||||
action: InfoAction? = null
|
||||
) {
|
||||
|
|
@ -64,9 +143,8 @@ fun InfoView(
|
|||
) {
|
||||
icon()
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
textAlign = TextAlign.Center,
|
||||
|
|
@ -74,9 +152,8 @@ fun InfoView(
|
|||
)
|
||||
|
||||
message?.let {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = it,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
|
|
@ -85,16 +162,17 @@ fun InfoView(
|
|||
}
|
||||
|
||||
action?.let {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Button(onClick = action.onAction) {
|
||||
Button(
|
||||
modifier = Modifier.padding(top = 24.dp),
|
||||
onClick = it.onAction,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = action.icon,
|
||||
imageVector = it.icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(action.label)
|
||||
Text(it.label)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||
import androidx.compose.material.icons.automirrored.filled.Help
|
||||
import androidx.compose.material.icons.automirrored.outlined.ContactSupport
|
||||
import androidx.compose.material.icons.filled.ArrowForward
|
||||
import androidx.compose.material.icons.filled.Attribution
|
||||
import androidx.compose.material.icons.filled.Celebration
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
|
|
@ -31,11 +30,11 @@ import androidx.compose.material.icons.filled.Favorite
|
|||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Today
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -64,6 +63,7 @@ import androidx.core.net.toUri
|
|||
import com.example.llama.data.model.ModelInfo
|
||||
import com.example.llama.data.source.remote.HuggingFaceModel
|
||||
import com.example.llama.ui.components.InfoAction
|
||||
import com.example.llama.ui.components.InfoAlertDialog
|
||||
import com.example.llama.ui.components.InfoView
|
||||
import com.example.llama.ui.components.ModelCardFullExpandable
|
||||
import com.example.llama.ui.scaffold.ScaffoldEvent
|
||||
|
|
@ -626,25 +626,17 @@ private fun DownloadHuggingFaceDispatchedDialog(
|
|||
modelId: String,
|
||||
onConfirm: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = false,
|
||||
dismissOnClickOutside = false
|
||||
),
|
||||
title = {},
|
||||
text = {
|
||||
InfoView(
|
||||
title = "Download has started",
|
||||
icon = Icons.Default.Download,
|
||||
message = "Your Android system download manager has started downloading the model: $modelId.\n\n"
|
||||
+ "You can track its progress in your notification drawer.\n"
|
||||
+ "Feel free to stay on this screen, or come back to import it after complete.",
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(onClick = onConfirm) { Text("Okay") }
|
||||
},
|
||||
InfoAlertDialog(
|
||||
title = "Download has started",
|
||||
icon = Icons.Default.Download,
|
||||
message = "Your Android system download manager has started downloading the model: $modelId.\n\n"
|
||||
+ "You can track its progress in your notification drawer.\n"
|
||||
+ "Feel free to stay on this screen, or come back to import it after complete.",
|
||||
action = InfoAction(
|
||||
icon = Icons.AutoMirrored.Default.ArrowForward,
|
||||
label = "Okay",
|
||||
onAction = onConfirm
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -652,26 +644,15 @@ private fun DownloadHuggingFaceDispatchedDialog(
|
|||
private fun FirstModelImportSuccessDialog(
|
||||
onConfirm: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
// Prevent dismissal via back button during deletion
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = false,
|
||||
dismissOnClickOutside = false
|
||||
),
|
||||
onDismissRequest = {},
|
||||
text = {
|
||||
InfoView(
|
||||
title = "Congratulations",
|
||||
icon = Icons.Default.Celebration,
|
||||
message = "You have just installed your first Large Language Model!",
|
||||
action = InfoAction(
|
||||
label = "Check it out",
|
||||
icon = Icons.AutoMirrored.Default.ArrowForward,
|
||||
onAction = onConfirm
|
||||
)
|
||||
)
|
||||
},
|
||||
confirmButton = {}
|
||||
InfoAlertDialog(
|
||||
title = "Congratulations",
|
||||
icon = Icons.Default.Celebration,
|
||||
message = "You have just installed your first Large Language Model!",
|
||||
action = InfoAction(
|
||||
icon = Icons.AutoMirrored.Default.ArrowForward,
|
||||
label = "Check it out",
|
||||
onAction = onConfirm
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -765,21 +746,16 @@ private fun ErrorDialog(
|
|||
)
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
text = {
|
||||
InfoView(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
title = title,
|
||||
icon = Icons.Default.Error,
|
||||
message = message,
|
||||
action = action
|
||||
)
|
||||
},
|
||||
InfoAlertDialog(
|
||||
isCritical = true,
|
||||
title = title,
|
||||
allowDismiss = true,
|
||||
onDismiss = onDismiss,
|
||||
icon = Icons.Default.Error,
|
||||
message = message,
|
||||
action = action,
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("OK")
|
||||
}
|
||||
FilledTonalButton(onClick = onDismiss) { Text("Dismiss") }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@ package com.example.llama.ui.screens
|
|||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -20,6 +23,8 @@ import androidx.compose.runtime.derivedStateOf
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.llama.data.model.ModelInfo
|
||||
import com.example.llama.ui.components.InfoView
|
||||
|
|
@ -149,12 +154,26 @@ private fun RamErrorDialog(
|
|||
val availableRam = formatFileByteSize(ramError.availableRam)
|
||||
|
||||
AlertDialog(
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Warning,
|
||||
contentDescription = "Warning icon",
|
||||
modifier = Modifier.size(64.dp),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = "Insufficient RAM",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
},
|
||||
text = {
|
||||
InfoView(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
title = "Insufficient RAM",
|
||||
icon = Icons.Default.Warning,
|
||||
message = "You are trying to run a $requiredRam size model, " +
|
||||
Text(
|
||||
"You are trying to run a $requiredRam size model, " +
|
||||
"but currently there's only $availableRam memory available!",
|
||||
)
|
||||
},
|
||||
|
|
@ -162,17 +181,19 @@ private fun RamErrorDialog(
|
|||
titleContentColor = MaterialTheme.colorScheme.onErrorContainer,
|
||||
textContentColor = MaterialTheme.colorScheme.onErrorContainer,
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("Cancel")
|
||||
TextButton(
|
||||
onClick = onConfirm,
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.error
|
||||
)
|
||||
) {
|
||||
Text("Proceed")
|
||||
}
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
TextButton(onClick = onConfirm) {
|
||||
Text(
|
||||
text = "Proceed",
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
FilledTonalButton(onClick = onDismiss) {
|
||||
Text("Cancel")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue