android: all dialogs in the Final 2026 look (28dp card, 22/800 title, 50% scrim), login fields as form fields

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Opus 5.5 committed 2026-10-12 11:01:25 +07:00
1 parent ffa38d5f28
commit 9c96c36fc6
8 files changed
+102 -33

No files matched your search

@@ -25,7 +25,7 @@ import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.AlertDialog
import org.strongswan.android.ui.compose.design.ShxAlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.getValue
@@ -175,7 +175,7 @@ class TrustedCertificateImportActivity : AppCompatActivity()
@androidx.compose.runtime.Composable
private fun ConfirmImportUi(certificate: X509Certificate)
{
AlertDialog(
ShxAlertDialog(
onDismissRequest = { finish() },
title = { Text(stringResource(R.string.import_certificate)) },
text = { Text(certificate.getSubjectDN().toString()) },
@@ -31,7 +31,7 @@ import androidx.compose.material.icons.rounded.Remove
import androidx.compose.material.icons.rounded.Public
import androidx.compose.material.icons.rounded.Speed
import androidx.compose.material.icons.rounded.Timer
import androidx.compose.material3.AlertDialog
import org.strongswan.android.ui.compose.design.ShxAlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -252,7 +252,7 @@ fun ConnectivityCheckScreen()
if (showIntervalPicker)
{
val options = listOf(15, 30, 60, 300)
AlertDialog(
ShxAlertDialog(
onDismissRequest = { showIntervalPicker = false },
title = { Text(stringResource(R.string.health_check_interval)) },
text = {
@@ -71,7 +71,7 @@ import androidx.compose.material.icons.rounded.Share
import androidx.compose.material.icons.rounded.SignalCellularAlt
import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material.icons.rounded.Wifi
import androidx.compose.material3.AlertDialog
import org.strongswan.android.ui.compose.design.ShxAlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
@@ -425,7 +425,7 @@ fun HomeScreen(
}
pendingDelete?.let { profile ->
AlertDialog(
ShxAlertDialog(
onDismissRequest = { pendingDelete = null },
title = { Text(stringResource(R.string.delete_profile)) },
text = { Text(profile.name ?: "") },
@@ -45,7 +45,7 @@ import androidx.compose.material.icons.rounded.Security
import androidx.compose.material.icons.rounded.SystemUpdateAlt
import androidx.compose.material.icons.rounded.VerifiedUser
import androidx.compose.material.icons.rounded.Wifi
import androidx.compose.material3.AlertDialog
import org.strongswan.android.ui.compose.design.ShxAlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
@@ -551,7 +551,7 @@ fun SettingsScreen(profiles: List<VpnProfile>, showTitle: Boolean = true)
{
val options = listOf(Constants.PREF_DEFAULT_VPN_PROFILE_MRU to context.getString(R.string.pref_default_vpn_profile_mru)) +
profiles.map { it.getUUID().toString() to (it.name ?: "") }
AlertDialog(
ShxAlertDialog(
onDismissRequest = { showDefaultPicker = false },
title = { Text(stringResource(R.string.pref_default_vpn_profile)) },
text = {
@@ -586,7 +586,7 @@ fun SettingsScreen(profiles: List<VpnProfile>, showTitle: Boolean = true)
if (showCrl)
{
val files = remember(showCrl) { context.fileList().filter { it.startsWith("crl-") } }
AlertDialog(
ShxAlertDialog(
onDismissRequest = { showCrl = false },
title = { Text(stringResource(R.string.clear_crl_cache_title)) },
text = {
@@ -620,7 +620,7 @@ fun SettingsScreen(profiles: List<VpnProfile>, showTitle: Boolean = true)
{
val info = (updateState as UpdateState.Available).info
val notes = info.notes["ru"] ?: info.notes["en"] ?: info.notes.values.firstOrNull() ?: ""
AlertDialog(
ShxAlertDialog(
onDismissRequest = { showUpdateConfirm = false },
title = { Text(stringResource(R.string.update_confirm_title, info.versionName)) },
text = { if (notes.isNotEmpty()) { Text(notes) } },
@@ -905,7 +905,7 @@ private fun SettingsRoot(onOpen: (SettingsPage) -> Unit, updateState: UpdateStat
}
if (showKillSwitchDialog)
{
AlertDialog(
ShxAlertDialog(
onDismissRequest = { showKillSwitchDialog = false },
title = { Text(stringResource(R.string.kill_switch_dialog_title)) },
text = { Text(stringResource(R.string.kill_switch_dialog_text)) },
@@ -974,7 +974,7 @@ private fun KillSwitchRow()
}
if (showKillSwitchDialog)
{
AlertDialog(
ShxAlertDialog(
onDismissRequest = { showKillSwitchDialog = false },
title = { Text(stringResource(R.string.kill_switch_dialog_title)) },
text = { Text(stringResource(R.string.kill_switch_dialog_text)) },
@@ -37,7 +37,6 @@ import androidx.compose.material.icons.outlined.AltRoute
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.Storage
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -38,7 +38,7 @@ import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material.icons.rounded.Language
import androidx.compose.material.icons.rounded.Wifi
import androidx.compose.material3.AlertDialog
import org.strongswan.android.ui.compose.design.ShxAlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -302,7 +302,7 @@ fun TrustedNetworksScreen(
if (showProfilePicker)
{
AlertDialog(
ShxAlertDialog(
onDismissRequest = { showProfilePicker = false },
title = { Text(stringResource(R.string.auto_connect_profile)) },
text = {
@@ -339,7 +339,7 @@ fun TrustedNetworksScreen(
if (showAddSsid)
{
var ssid by remember { mutableStateOf("") }
AlertDialog(
ShxAlertDialog(
onDismissRequest = { showAddSsid = false },
title = { Text(stringResource(R.string.auto_connect_add_network)) },
text = {
@@ -372,7 +372,7 @@ fun TrustedNetworksScreen(
}
pendingDeleteSsid?.let { ssid ->
AlertDialog(
ShxAlertDialog(
onDismissRequest = { pendingDeleteSsid = null },
title = { Text(stringResource(R.string.auto_connect_remove_network)) },
text = { Text(ssid) },
@@ -22,7 +22,8 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Visibility
import androidx.compose.material.icons.rounded.VisibilityOff
import androidx.compose.material3.AlertDialog
import org.strongswan.android.ui.compose.design.ShxAlertDialog
import org.strongswan.android.ui.compose.design.ShxFormField
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -142,7 +143,7 @@ private fun cancelDialog()
@Composable
private fun ConfirmationUi(dialog: VpnDialog.Confirmation)
{
AlertDialog(
ShxAlertDialog(
onDismissRequest = ::cancelDialog,
title = { Text(dialog.title) },
text = { Text(stringResource(dialog.message)) },
@@ -189,31 +190,28 @@ private fun LoginUi(dialog: VpnDialog.Login)
var password by rememberSaveable { mutableStateOf("") }
var showPassword by rememberSaveable { mutableStateOf(false) }
AlertDialog(
ShxAlertDialog(
onDismissRequest = ::cancelDialog,
title = { Text(stringResource(R.string.login_title)) },
text = {
Column(verticalArrangement = Arrangement.spacedBy(ShxSpacing.md)) {
OutlinedTextField(
ShxFormField(
label = stringResource(R.string.login_username),
value = username,
onValueChange = { username = it },
label = { Text(stringResource(R.string.login_username)) },
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
enabled = false,
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
)
OutlinedTextField(
ShxFormField(
label = stringResource(R.string.login_password),
value = password,
onValueChange = { password = it },
label = { Text(stringResource(R.string.login_password)) },
singleLine = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done,
),
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
trailing = {
IconButton(onClick = { showPassword = !showPassword }) {
Icon(
if (showPassword) Icons.Rounded.Visibility else Icons.Rounded.VisibilityOff,
@@ -223,7 +221,6 @@ private fun LoginUi(dialog: VpnDialog.Login)
)
}
},
modifier = Modifier.fillMaxWidth(),
)
}
},
@@ -244,7 +241,7 @@ private fun LoginUi(dialog: VpnDialog.Login)
@Composable
private fun CertificateRequiredUi(dialog: VpnDialog.CertificateRequired)
{
AlertDialog(
ShxAlertDialog(
onDismissRequest = ::cancelDialog,
title = { Text(stringResource(R.string.certificate_required_title)) },
text = { Text(stringResource(R.string.certificate_required_text)) },
@@ -265,7 +262,7 @@ private fun CertificateRequiredUi(dialog: VpnDialog.CertificateRequired)
@Composable
private fun PowerWhitelistUi()
{
AlertDialog(
ShxAlertDialog(
onDismissRequest = ::cancelDialog,
title = { Text(stringResource(R.string.power_whitelist_title)) },
text = { Text(stringResource(R.string.power_whitelist_text)) },
@@ -281,7 +278,7 @@ private fun PowerWhitelistUi()
@Composable
private fun NotSupportedUi(dialog: VpnDialog.NotSupported)
{
AlertDialog(
ShxAlertDialog(
onDismissRequest = ::cancelDialog,
title = { Text(stringResource(R.string.vpn_not_supported_title)) },
text = { Text(stringResource(dialog.message)) },
@@ -757,6 +757,7 @@ fun ShxDialog(
val c = LocalShxColors.current
androidx.compose.ui.window.Dialog(onDismissRequest = onDismiss)
{
DialogScrim()
Column(
verticalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(28.dp)).background(c.background).padding(24.dp),
@@ -802,3 +803,75 @@ fun ShxDialog(
}
}
}
/**
* Drop-in for the Material AlertDialog in the mockup look (as [ShxDialog]):
* 28dp card on the page colour, 22/800 title, 14sp soft text, buttons at the end.
*/
@Composable
fun ShxAlertDialog(
onDismissRequest: () -> Unit,
confirmButton: @Composable () -> Unit,
modifier: Modifier = Modifier,
dismissButton: (@Composable () -> Unit)? = null,
icon: (@Composable () -> Unit)? = null,
title: (@Composable () -> Unit)? = null,
text: (@Composable () -> Unit)? = null,
)
{
val c = LocalShxColors.current
androidx.compose.ui.window.Dialog(onDismissRequest = onDismissRequest)
{
DialogScrim()
Column(
verticalArrangement = Arrangement.spacedBy(14.dp),
modifier = modifier.fillMaxWidth().clip(RoundedCornerShape(28.dp)).background(c.background).padding(24.dp),
)
{
icon?.let {
androidx.compose.runtime.CompositionLocalProvider(androidx.compose.material3.LocalContentColor provides c.onAccentContainer) { it() }
}
title?.let {
androidx.compose.runtime.CompositionLocalProvider(androidx.compose.material3.LocalContentColor provides c.text) {
androidx.compose.material3.ProvideTextStyle(ShxType.pageTitle) { it() }
}
}
text?.let {
androidx.compose.runtime.CompositionLocalProvider(androidx.compose.material3.LocalContentColor provides c.textSoft) {
androidx.compose.material3.ProvideTextStyle(ShxType.note.copy(fontSize = 14.sp, lineHeight = 21.sp, fontWeight = FontWeight.Normal)) {
Box(Modifier.weight(1f, fill = false)) { it() }
}
}
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
)
{
androidx.compose.material3.ProvideTextStyle(ShxType.button.copy(fontWeight = FontWeight.Bold))
{
dismissButton?.invoke()
confirmButton()
}
}
}
}
}
/**
* The 50% scrim of the mockup dialogs. Set on the dialog window itself:
* hosts with a translucent theme (VpnProfileControlActivity) turn the
* platform dim off.
*/
@Composable
private fun DialogScrim()
{
val view = androidx.compose.ui.platform.LocalView.current
androidx.compose.runtime.SideEffect {
(view.parent as? androidx.compose.ui.window.DialogWindowProvider)?.window?.let {
it.addFlags(android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setDimAmount(0.5f)
}
}
}