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