android: spacing tokens - migrate padding/spacing literals and ScreenPadding to ShxSpacing

This commit is contained in:
Denozordec committed 2026-10-10 00:13:08 +07:00
1 parent 22e4082825
commit 0e5e28b4da
21 files changed
+141 -140

No files matched your search

@@ -51,6 +51,7 @@ import org.strongswan.android.data.LogContentProvider
import org.strongswan.android.logic.CharonVpnService import org.strongswan.android.logic.CharonVpnService
import org.strongswan.android.logic.DiagLog import org.strongswan.android.logic.DiagLog
import org.strongswan.android.ui.compose.SecondaryScaffold import org.strongswan.android.ui.compose.SecondaryScaffold
import org.strongswan.android.ui.compose.theme.ShxSpacing
import org.strongswan.android.ui.compose.theme.ShxTheme import org.strongswan.android.ui.compose.theme.ShxTheme
import java.io.BufferedReader import java.io.BufferedReader
import java.io.File import java.io.File
@@ -194,7 +195,7 @@ private fun LogScreen(onBack: () -> Unit, onSend: () -> Unit, viewModel: LogView
{ {
Text( Text(
stringResource(R.string.empty_log), stringResource(R.string.empty_log),
modifier = Modifier.padding(padding).padding(24.dp), modifier = Modifier.padding(padding).padding(ShxSpacing.xl),
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
) )
} }
@@ -202,7 +203,7 @@ private fun LogScreen(onBack: () -> Unit, onSend: () -> Unit, viewModel: LogView
{ {
LazyColumn( LazyColumn(
state = listState, state = listState,
modifier = Modifier.fillMaxSize().padding(padding).padding(horizontal = 12.dp), modifier = Modifier.fillMaxSize().padding(padding).padding(horizontal = ShxSpacing.md),
) { ) {
items(lines.size) { index -> items(lines.size) { index ->
val line = lines[index] val line = lines[index]
@@ -65,7 +65,7 @@ import org.strongswan.android.logic.imc.RemediationInstruction
import org.strongswan.android.ui.compose.components.ShxCard import org.strongswan.android.ui.compose.components.ShxCard
import org.strongswan.android.ui.compose.components.ShxEmptyState import org.strongswan.android.ui.compose.components.ShxEmptyState
import org.strongswan.android.ui.compose.components.ShxSectionDivider import org.strongswan.android.ui.compose.components.ShxSectionDivider
import org.strongswan.android.ui.compose.theme.ScreenPadding import org.strongswan.android.ui.compose.theme.ShxSpacing
import org.strongswan.android.ui.compose.theme.ShxTheme import org.strongswan.android.ui.compose.theme.ShxTheme
/** /**
@@ -167,7 +167,7 @@ private fun RemediationUi(
ShxEmptyState( ShxEmptyState(
icon = Icons.Rounded.CheckCircle, icon = Icons.Rounded.CheckCircle,
message = stringResource(R.string.remediation_instructions_title), message = stringResource(R.string.remediation_instructions_title),
modifier = Modifier.padding(ScreenPadding), modifier = Modifier.padding(ShxSpacing.lg),
) )
} }
else else
@@ -179,19 +179,19 @@ private fun RemediationUi(
itemsIndexed(instructions) { index, item -> itemsIndexed(instructions) { index, item ->
ShxCard( ShxCard(
modifier = Modifier modifier = Modifier
.padding(horizontal = ScreenPadding, vertical = 6.dp) .padding(horizontal = ShxSpacing.lg, vertical = 6.dp)
.animateItem(), .animateItem(),
) { ) {
Column( Column(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { onSelect(item) } .clickable { onSelect(item) }
.padding(16.dp), .padding(ShxSpacing.lg),
) { ) {
Text(item.getTitle(), style = MaterialTheme.typography.titleMedium) Text(item.getTitle(), style = MaterialTheme.typography.titleMedium)
if (!item.getDescription().isNullOrEmpty()) if (!item.getDescription().isNullOrEmpty())
{ {
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(ShxSpacing.xs))
Text( Text(
item.getDescription(), item.getDescription(),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
@@ -210,12 +210,12 @@ private fun RemediationUi(
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(horizontal = ScreenPadding), .padding(horizontal = ShxSpacing.lg),
) { ) {
Text( Text(
instruction.getDescription(), instruction.getDescription(),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(top = 8.dp), modifier = Modifier.padding(top = ShxSpacing.sm),
) )
if (!instruction.getHeader().isNullOrEmpty()) if (!instruction.getHeader().isNullOrEmpty())
{ {
@@ -223,15 +223,15 @@ private fun RemediationUi(
instruction.getHeader(), instruction.getHeader(),
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 20.dp, bottom = 8.dp), modifier = Modifier.padding(top = 20.dp, bottom = ShxSpacing.sm),
) )
ShxCard { ShxCard {
Column(Modifier.padding(vertical = 4.dp)) { Column(Modifier.padding(vertical = ShxSpacing.xs)) {
instruction.getItems().forEachIndexed { index, item -> instruction.getItems().forEachIndexed { index, item ->
Text( Text(
item, item,
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp), modifier = Modifier.padding(horizontal = ShxSpacing.lg, vertical = 10.dp),
) )
if (index < instruction.getItems().lastIndex) if (index < instruction.getItems().lastIndex)
{ {
@@ -241,7 +241,7 @@ private fun RemediationUi(
} }
} }
} }
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(ShxSpacing.xl))
} }
} }
} }
@@ -52,6 +52,7 @@ import org.strongswan.android.R
import org.strongswan.android.data.VpnProfileDataSource import org.strongswan.android.data.VpnProfileDataSource
import org.strongswan.android.ui.adapter.SelectedApplicationEntry import org.strongswan.android.ui.adapter.SelectedApplicationEntry
import org.strongswan.android.ui.compose.SecondaryScaffold import org.strongswan.android.ui.compose.SecondaryScaffold
import org.strongswan.android.ui.compose.theme.ShxSpacing
import org.strongswan.android.ui.compose.theme.ShxTheme import org.strongswan.android.ui.compose.theme.ShxTheme
import java.util.TreeSet import java.util.TreeSet
@@ -161,11 +162,11 @@ private fun SelectedAppsScreen(
onValueChange = { query = it }, onValueChange = { query = it },
label = { Text(stringResource(R.string.search)) }, label = { Text(stringResource(R.string.search)) },
singleLine = true, singleLine = true,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp).fillMaxWidth(), modifier = Modifier.padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.sm).fillMaxWidth(),
) )
if (loading) if (loading)
{ {
LoadingIndicator(Modifier.padding(24.dp).size(32.dp)) LoadingIndicator(Modifier.padding(ShxSpacing.xl).size(32.dp))
} }
else else
{ {
@@ -61,7 +61,7 @@ import org.strongswan.android.logic.connectivity.HealthCheckManager
import org.strongswan.android.logic.connectivity.HealthConfig import org.strongswan.android.logic.connectivity.HealthConfig
import org.strongswan.android.logic.connectivity.ProbeTarget import org.strongswan.android.logic.connectivity.ProbeTarget
import org.strongswan.android.logic.connectivity.ServerCheckMode import org.strongswan.android.logic.connectivity.ServerCheckMode
import org.strongswan.android.ui.compose.theme.ScreenPadding import org.strongswan.android.ui.compose.theme.ShxSpacing
/** /**
* Connectivity check settings: what to probe, how often, and what to do when * Connectivity check settings: what to probe, how often, and what to do when
@@ -86,7 +86,7 @@ fun ConnectivityCheckScreen()
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(bottom = 24.dp), .padding(bottom = ShxSpacing.xl),
) )
{ {
ShxSection(title = stringResource(R.string.settings_section_general)) ShxSection(title = stringResource(R.string.settings_section_general))
@@ -153,14 +153,14 @@ fun ConnectivityCheckScreen()
ShxSection(title = stringResource(R.string.health_check_server_down_title)) ShxSection(title = stringResource(R.string.health_check_server_down_title))
{ {
Column(Modifier.padding(horizontal = 16.dp, vertical = 10.dp)) Column(Modifier.padding(horizontal = ShxSpacing.lg, vertical = 10.dp))
{ {
Text( Text(
stringResource(R.string.health_check_mode), stringResource(R.string.health_check_mode),
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
color = if (config.enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant, color = if (config.enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant,
) )
SingleChoiceSegmentedButtonRow(Modifier.fillMaxWidth().padding(top = 8.dp)) SingleChoiceSegmentedButtonRow(Modifier.fillMaxWidth().padding(top = ShxSpacing.sm))
{ {
ServerCheckMode.entries.forEachIndexed { index, mode -> ServerCheckMode.entries.forEachIndexed { index, mode ->
SegmentedButton( SegmentedButton(
@@ -270,7 +270,7 @@ fun ConnectivityCheckScreen()
) )
{ {
RadioButton(selected = config.intervalSec == seconds, onClick = null) RadioButton(selected = config.intervalSec == seconds, onClick = null)
Text(intervalLabel(seconds, false), modifier = Modifier.padding(start = 12.dp)) Text(intervalLabel(seconds, false), modifier = Modifier.padding(start = ShxSpacing.md))
} }
} }
} }
@@ -331,7 +331,7 @@ private fun AttemptsRow(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 10.dp), .padding(horizontal = ShxSpacing.lg, vertical = 10.dp),
) )
{ {
Text( Text(
@@ -346,7 +346,7 @@ private fun AttemptsRow(
Text( Text(
text = attempts.toString(), text = attempts.toString(),
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 8.dp), modifier = Modifier.padding(horizontal = ShxSpacing.sm),
) )
IconButton(onClick = onIncrement, enabled = enabled && attempts < 5) { IconButton(onClick = onIncrement, enabled = enabled && attempts < 5) {
Icon(Icons.Rounded.Add, contentDescription = stringResource(R.string.health_check_attempt_plus)) Icon(Icons.Rounded.Add, contentDescription = stringResource(R.string.health_check_attempt_plus))
@@ -54,7 +54,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.strongswan.android.R import org.strongswan.android.R
import org.strongswan.android.ui.compose.components.ShxCard import org.strongswan.android.ui.compose.components.ShxCard
import org.strongswan.android.ui.compose.theme.ScreenPadding import org.strongswan.android.ui.compose.theme.ShxSpacing
import org.strongswan.android.ui.compose.theme.ShxTheme import org.strongswan.android.ui.compose.theme.ShxTheme
/** /**
@@ -149,8 +149,8 @@ fun ImportScreen(
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(horizontal = ScreenPadding) .padding(horizontal = ShxSpacing.lg)
.padding(bottom = 24.dp), .padding(bottom = ShxSpacing.xl),
) )
{ {
if (model.existsWarning != null) if (model.existsWarning != null)
@@ -161,7 +161,7 @@ fun ImportScreen(
content = MaterialTheme.colorScheme.onErrorContainer, content = MaterialTheme.colorScheme.onErrorContainer,
) )
} }
ShxCard(modifier = Modifier.padding(top = 12.dp)) ShxCard(modifier = Modifier.padding(top = ShxSpacing.md))
{ {
ImportInfoRow(Icons.Rounded.Language, stringResource(R.string.profile_name_label_simple), model.name) ImportInfoRow(Icons.Rounded.Language, stringResource(R.string.profile_name_label_simple), model.name)
ImportInfoRow(Icons.Rounded.Dns, stringResource(R.string.profile_gateway_label), model.gateway) ImportInfoRow(Icons.Rounded.Dns, stringResource(R.string.profile_gateway_label), model.gateway)
@@ -174,10 +174,10 @@ fun ImportScreen(
stringResource(R.string.profile_username_label).uppercase().replace(":", ""), stringResource(R.string.profile_username_label).uppercase().replace(":", ""),
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(start = 16.dp, top = 20.dp, bottom = 8.dp), modifier = Modifier.padding(start = ShxSpacing.lg, top = 20.dp, bottom = ShxSpacing.sm),
) )
ShxCard { ShxCard {
Column(Modifier.padding(16.dp)) { Column(Modifier.padding(ShxSpacing.lg)) {
OutlinedTextField( OutlinedTextField(
value = model.username, value = model.username,
onValueChange = { model.username = it }, onValueChange = { model.username = it },
@@ -187,7 +187,7 @@ fun ImportScreen(
isError = model.usernameError, isError = model.usernameError,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) )
Spacer(Modifier.height(12.dp)) Spacer(Modifier.height(ShxSpacing.md))
OutlinedTextField( OutlinedTextField(
value = model.password, value = model.password,
onValueChange = { model.password = it }, onValueChange = { model.password = it },
@@ -205,7 +205,7 @@ fun ImportScreen(
text = stringResource(R.string.profile_import_shared_secret), text = stringResource(R.string.profile_import_shared_secret),
container = MaterialTheme.colorScheme.surfaceContainerHigh, container = MaterialTheme.colorScheme.surfaceContainerHigh,
content = MaterialTheme.colorScheme.onSurfaceVariant, content = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 8.dp), modifier = Modifier.padding(top = ShxSpacing.sm),
) )
} }
} }
@@ -216,7 +216,7 @@ fun ImportScreen(
stringResource(R.string.profile_user_certificate_label).uppercase().replace(":", ""), stringResource(R.string.profile_user_certificate_label).uppercase().replace(":", ""),
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(start = 16.dp, top = 20.dp, bottom = 8.dp), modifier = Modifier.padding(start = ShxSpacing.lg, top = 20.dp, bottom = ShxSpacing.sm),
) )
ShxCard { ShxCard {
Column { Column {
@@ -225,7 +225,7 @@ fun ImportScreen(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onSelectUserCert) .clickable(onClick = onSelectUserCert)
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) { ) {
Column(Modifier.weight(1f)) { Column(Modifier.weight(1f)) {
Text( Text(
@@ -248,9 +248,9 @@ fun ImportScreen(
} }
if (model.showImportUserCert) if (model.showImportUserCert)
{ {
TextButton(onClick = onImportPkcs12, modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)) { TextButton(onClick = onImportPkcs12, modifier = Modifier.padding(start = ShxSpacing.sm, bottom = ShxSpacing.sm)) {
Icon(Icons.Rounded.FileDownload, contentDescription = null, modifier = Modifier.size(18.dp)) Icon(Icons.Rounded.FileDownload, contentDescription = null, modifier = Modifier.size(18.dp))
Spacer(Modifier.size(8.dp)) Spacer(Modifier.size(ShxSpacing.sm))
Text(stringResource(R.string.profile_cert_import)) Text(stringResource(R.string.profile_cert_import))
} }
} }
@@ -264,7 +264,7 @@ fun ImportScreen(
stringResource(R.string.profile_ca_label).uppercase().replace(":", ""), stringResource(R.string.profile_ca_label).uppercase().replace(":", ""),
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(start = 16.dp, top = 20.dp, bottom = 8.dp), modifier = Modifier.padding(start = ShxSpacing.lg, top = 20.dp, bottom = ShxSpacing.sm),
) )
ShxCard { ShxCard {
ImportInfoRow(Icons.Rounded.Dns, model.remoteCertPrimary ?: "", model.remoteCertSecondary ?: "", divider = false) ImportInfoRow(Icons.Rounded.Dns, model.remoteCertPrimary ?: "", model.remoteCertSecondary ?: "", divider = false)
@@ -288,12 +288,12 @@ private fun ImportBanner(
contentColor = content, contentColor = content,
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 8.dp), .padding(top = ShxSpacing.sm),
) { ) {
Text( Text(
text, text,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp), modifier = Modifier.padding(horizontal = ShxSpacing.lg, vertical = 10.dp),
) )
} }
} }
@@ -306,7 +306,7 @@ private fun ImportInfoRow(icon: ImageVector, label: String, value: String, divid
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.md),
) { ) {
Icon( Icon(
icon, icon,
@@ -314,7 +314,7 @@ private fun ImportInfoRow(icon: ImageVector, label: String, value: String, divid
tint = MaterialTheme.colorScheme.onSurfaceVariant, tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(20.dp), modifier = Modifier.size(20.dp),
) )
Column(Modifier.padding(start = 16.dp)) { Column(Modifier.padding(start = ShxSpacing.lg)) {
Text( Text(
label, label,
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
@@ -41,6 +41,7 @@ import androidx.compose.ui.unit.dp
import org.strongswan.android.R import org.strongswan.android.R
import org.strongswan.android.logic.routing.RuleSet import org.strongswan.android.logic.routing.RuleSet
import org.strongswan.android.logic.routing.RoutingPresets import org.strongswan.android.logic.routing.RoutingPresets
import org.strongswan.android.ui.compose.theme.ShxSpacing
/** /**
* Searchable list of the built-in presets shown inside the rule set editor * Searchable list of the built-in presets shown inside the rule set editor
@@ -71,7 +72,7 @@ fun PresetPickerContent(
{ {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 4.dp), modifier = Modifier.padding(start = ShxSpacing.md, end = ShxSpacing.md, bottom = ShxSpacing.xs),
) )
{ {
IconButton(onClick = onBack) IconButton(onClick = onBack)
@@ -103,7 +104,7 @@ fun PresetPickerContent(
}, },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 24.dp), .padding(horizontal = ShxSpacing.xl),
shape = MaterialTheme.shapes.medium, shape = MaterialTheme.shapes.medium,
) )
@@ -111,7 +112,7 @@ fun PresetPickerContent(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(max = 460.dp) .heightIn(max = 460.dp)
.padding(top = 4.dp), .padding(top = ShxSpacing.xs),
) )
{ {
items(filtered, key = { it.name }) items(filtered, key = { it.name })
@@ -123,7 +124,7 @@ fun PresetPickerContent(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { onToggle(preset) } .clickable { onToggle(preset) }
.padding(horizontal = 24.dp, vertical = 6.dp), .padding(horizontal = ShxSpacing.xl, vertical = 6.dp),
) )
{ {
Column( Column(
@@ -151,7 +152,7 @@ fun PresetPickerContent(
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp), .padding(horizontal = ShxSpacing.xl, vertical = ShxSpacing.lg),
) )
} }
} }
@@ -91,6 +91,7 @@ import org.strongswan.android.data.VpnType
import org.strongswan.android.data.VpnType.VpnTypeFeature import org.strongswan.android.data.VpnType.VpnTypeFeature
import org.strongswan.android.ui.SelectedApplicationsActivity import org.strongswan.android.ui.SelectedApplicationsActivity
import org.strongswan.android.ui.TrustedCertificatesActivity import org.strongswan.android.ui.TrustedCertificatesActivity
import org.strongswan.android.ui.compose.theme.ShxSpacing
import org.strongswan.android.utils.Constants import org.strongswan.android.utils.Constants
import org.strongswan.android.utils.IPRangeSet import org.strongswan.android.utils.IPRangeSet
import org.strongswan.android.utils.Utils import org.strongswan.android.utils.Utils
@@ -356,8 +357,8 @@ fun ProfileFormScreen(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 20.dp, vertical = 12.dp), .padding(horizontal = 20.dp, vertical = ShxSpacing.md),
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(ShxSpacing.md),
) { ) {
OutlinedButton( OutlinedButton(
onClick = onCancel, onClick = onCancel,
@@ -516,7 +517,7 @@ fun ProfileFormScreen(
enter = expandVertically() + fadeIn(), enter = expandVertically() + fadeIn(),
exit = shrinkVertically() + fadeOut(), exit = shrinkVertically() + fadeOut(),
) { ) {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Column(verticalArrangement = Arrangement.spacedBy(ShxSpacing.md)) {
OutlinedTextField(state.remoteId, { onChange(state.copy(remoteId = it)) }, OutlinedTextField(state.remoteId, { onChange(state.copy(remoteId = it)) },
label = { Text(stringResource(R.string.profile_remote_id_label)) }, label = { Text(stringResource(R.string.profile_remote_id_label)) },
supportingText = { Text(stringResource(R.string.profile_remote_id_hint)) }, supportingText = { Text(stringResource(R.string.profile_remote_id_hint)) },
@@ -38,6 +38,7 @@ import androidx.compose.ui.unit.dp
import org.strongswan.android.R import org.strongswan.android.R
import org.strongswan.android.data.VpnProfile import org.strongswan.android.data.VpnProfile
import org.strongswan.android.ui.compose.theme.LocalShxStatusColors import org.strongswan.android.ui.compose.theme.LocalShxStatusColors
import org.strongswan.android.ui.compose.theme.ShxSpacing
@Composable @Composable
fun ProfileAvatar(icon: ImageVector, connected: Boolean, modifier: Modifier = Modifier) fun ProfileAvatar(icon: ImageVector, connected: Boolean, modifier: Modifier = Modifier)
@@ -81,13 +82,13 @@ fun ShxProfileRow(
onLongClick = onLongClick, onLongClick = onLongClick,
onLongClickLabel = onLongClickLabel, onLongClickLabel = onLongClickLabel,
) )
.padding(horizontal = 16.dp, vertical = 12.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.md),
) { ) {
ProfileAvatar(icon = leadingIcon, connected = active) ProfileAvatar(icon = leadingIcon, connected = active)
Column( Column(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.padding(horizontal = 12.dp), .padding(horizontal = ShxSpacing.md),
) { ) {
Text( Text(
text = profile.name ?: "", text = profile.name ?: "",
@@ -128,7 +129,7 @@ fun ShxDivider(modifier: Modifier = Modifier, indent: Boolean = true)
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f), color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f),
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(start = if (indent) 68.dp else 0.dp, end = if (indent) 16.dp else 0.dp) .padding(start = if (indent) 68.dp else 0.dp, end = if (indent) ShxSpacing.lg else 0.dp)
.height(1.dp), .height(1.dp),
) {} ) {}
} }
@@ -61,7 +61,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.strongswan.android.R import org.strongswan.android.R
import org.strongswan.android.data.VpnProfile import org.strongswan.android.data.VpnProfile
@@ -69,7 +68,6 @@ import org.strongswan.android.logic.VpnStateService.State
import org.strongswan.android.ui.compose.components.ShxCard import org.strongswan.android.ui.compose.components.ShxCard
import org.strongswan.android.ui.compose.components.ShxEmptyState import org.strongswan.android.ui.compose.components.ShxEmptyState
import org.strongswan.android.ui.compose.theme.LocalShxStatusColors import org.strongswan.android.ui.compose.theme.LocalShxStatusColors
import org.strongswan.android.ui.compose.theme.ScreenPadding
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -136,7 +134,7 @@ fun ProfilesScreen(
SearchBar( SearchBar(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = ScreenPadding, vertical = 4.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.xs),
inputField = { inputField = {
SearchBarDefaults.InputField( SearchBarDefaults.InputField(
query = query, query = query,
@@ -170,7 +168,7 @@ fun ProfilesScreen(
color = MaterialTheme.colorScheme.surfaceContainerLow, color = MaterialTheme.colorScheme.surfaceContainerLow,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = ScreenPadding, vertical = 12.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.md),
) { ) {
LazyColumn { LazyColumn {
itemsIndexed(filtered, key = { _, p -> p.getUUID().toString() }) { index, profile -> itemsIndexed(filtered, key = { _, p -> p.getUUID().toString() }) { index, profile ->
@@ -275,7 +273,7 @@ private fun EmptyProfilesState(query: String, onAdd: () -> Unit)
ShxCard( ShxCard(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = ScreenPadding, vertical = 12.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.md),
) )
{ {
if (query.isBlank()) if (query.isBlank())
@@ -36,7 +36,7 @@ import kotlinx.coroutines.launch
import org.strongswan.android.R import org.strongswan.android.R
import org.strongswan.android.logic.CharonVpnService import org.strongswan.android.logic.CharonVpnService
import org.strongswan.android.logic.routing.RouteCheck import org.strongswan.android.logic.routing.RouteCheck
import org.strongswan.android.ui.compose.theme.ScreenPadding import org.strongswan.android.ui.compose.theme.ShxSpacing
/** Diagnostic: how a domain resolves and which route its addresses take. */ /** Diagnostic: how a domain resolves and which route its addresses take. */
@Composable @Composable
@@ -52,7 +52,7 @@ fun RouteCheckScreen()
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(horizontal = ScreenPadding, vertical = 12.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.md),
) )
{ {
Text( Text(
@@ -98,7 +98,7 @@ fun RouteCheckScreen()
@Composable @Composable
private fun RouteCheckResult(report: RouteCheck.Report) private fun RouteCheckResult(report: RouteCheck.Report)
{ {
Column(modifier = Modifier.padding(top = 12.dp)) Column(modifier = Modifier.padding(top = ShxSpacing.md))
{ {
if (!report.vpnNetworkFound) if (!report.vpnNetworkFound)
{ {
@@ -141,7 +141,7 @@ private fun RouteCheckResult(report: RouteCheck.Report)
) )
} }
} }
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(ShxSpacing.sm))
} }
@Composable @Composable
@@ -150,7 +150,7 @@ private fun RouteCheckRow(label: String, ips: List<String>)
Text( Text(
text = label, text = label,
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
modifier = Modifier.padding(top = 8.dp), modifier = Modifier.padding(top = ShxSpacing.sm),
) )
Text( Text(
text = ips.joinToString(", ").ifEmpty { stringResource(R.string.route_check_none) }, text = ips.joinToString(", ").ifEmpty { stringResource(R.string.route_check_none) },
@@ -124,7 +124,6 @@ import org.strongswan.android.ui.compose.components.ShxIconBadge
import org.strongswan.android.ui.compose.components.ShxSection import org.strongswan.android.ui.compose.components.ShxSection
import org.strongswan.android.ui.compose.components.ShxSectionDivider import org.strongswan.android.ui.compose.components.ShxSectionDivider
import org.strongswan.android.ui.compose.theme.LocalShxStatusColors import org.strongswan.android.ui.compose.theme.LocalShxStatusColors
import org.strongswan.android.ui.compose.theme.ScreenPadding
import org.strongswan.android.ui.compose.theme.ShxSpacing import org.strongswan.android.ui.compose.theme.ShxSpacing
import org.strongswan.android.ui.compose.routing.RoutingHeroCard import org.strongswan.android.ui.compose.routing.RoutingHeroCard
import org.strongswan.android.ui.compose.routing.RuToggleRow import org.strongswan.android.ui.compose.routing.RuToggleRow
@@ -236,7 +235,7 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(bottom = 24.dp), .padding(bottom = ShxSpacing.xl),
) )
{ {
TopAppBar( TopAppBar(
@@ -261,7 +260,7 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
setCount = config.ruleSets.count { it.enabled }, setCount = config.ruleSets.count { it.enabled },
appCount = excludedApps, appCount = excludedApps,
onToggle = { manager.setGlobalEnabled(it) }, onToggle = { manager.setGlobalEnabled(it) },
modifier = Modifier.padding(horizontal = ScreenPadding), modifier = Modifier.padding(horizontal = ShxSpacing.lg),
) )
/* kill switch + excluded apps: excluded apps are added to the VPN's disallowed list /* kill switch + excluded apps: excluded apps are added to the VPN's disallowed list
@@ -273,7 +272,7 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
text = stringResource(R.string.routing_killswitch_note, excludedApps), text = stringResource(R.string.routing_killswitch_note, excludedApps),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = ScreenPadding + ShxSpacing.xs, vertical = ShxSpacing.sm), modifier = Modifier.padding(horizontal = ShxSpacing.lg + ShxSpacing.xs, vertical = ShxSpacing.sm),
) )
} }
@@ -302,16 +301,16 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
text = stringResource(R.string.dead_targets_summary), text = stringResource(R.string.dead_targets_summary),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp), modifier = Modifier.padding(horizontal = ShxSpacing.lg, vertical = 6.dp),
) )
deadTargets.forEachIndexed { index, target -> deadTargets.forEachIndexed { index, target ->
ShxSectionDivider() ShxSectionDivider()
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(ShxSpacing.md),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 10.dp), .padding(horizontal = ShxSpacing.lg, vertical = 10.dp),
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -400,7 +399,7 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { advancedOpen = !advancedOpen } .clickable { advancedOpen = !advancedOpen }
.padding(horizontal = ScreenPadding + 16.dp, vertical = 16.dp), .padding(horizontal = ShxSpacing.lg + ShxSpacing.lg, vertical = ShxSpacing.lg),
) )
{ {
Text( Text(
@@ -433,7 +432,7 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
LinearWavyProgressIndicator( LinearWavyProgressIndicator(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 4.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.xs),
) )
} }
GeoipChipsRow(state = ruState) GeoipChipsRow(state = ruState)
@@ -451,7 +450,7 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
text = stringResource(R.string.routing_force_summary), text = stringResource(R.string.routing_force_summary),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp), modifier = Modifier.padding(horizontal = ShxSpacing.lg, vertical = 6.dp),
) )
} }
else else
@@ -68,6 +68,7 @@ import org.strongswan.android.R
import org.strongswan.android.logic.routing.RuleSet import org.strongswan.android.logic.routing.RuleSet
import org.strongswan.android.logic.routing.RoutingPresets import org.strongswan.android.logic.routing.RoutingPresets
import org.strongswan.android.ui.compose.components.ShxIconBadge import org.strongswan.android.ui.compose.components.ShxIconBadge
import org.strongswan.android.ui.compose.theme.ShxSpacing
/** /**
* Editor of one rule set as a modal bottom sheet. The preset picker is a * Editor of one rule set as a modal bottom sheet. The preset picker is a
@@ -161,12 +162,12 @@ private fun EditorContent(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(bottom = 24.dp), .padding(bottom = ShxSpacing.xl),
) )
{ {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 24.dp, end = 12.dp, bottom = 8.dp), modifier = Modifier.padding(start = ShxSpacing.xl, end = ShxSpacing.md, bottom = ShxSpacing.sm),
) )
{ {
Text( Text(
@@ -188,7 +189,7 @@ private fun EditorContent(
singleLine = true, singleLine = true,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 24.dp) .padding(horizontal = ShxSpacing.xl)
.focusRequester(nameFocusRequester), .focusRequester(nameFocusRequester),
shape = MaterialTheme.shapes.medium, shape = MaterialTheme.shapes.medium,
) )
@@ -232,7 +233,7 @@ private fun EditorContent(
shape = MaterialTheme.shapes.medium, shape = MaterialTheme.shapes.medium,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 12.dp), .padding(horizontal = ShxSpacing.xl, vertical = ShxSpacing.md),
) )
{ {
Icon( Icon(
@@ -240,7 +241,7 @@ private fun EditorContent(
contentDescription = null, contentDescription = null,
modifier = Modifier.size(18.dp), modifier = Modifier.size(18.dp),
) )
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(ShxSpacing.sm))
Text(stringResource(R.string.routing_delete)) Text(stringResource(R.string.routing_delete))
} }
} }
@@ -262,14 +263,14 @@ private fun ChipGroup(
text = label, text = label,
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(start = 24.dp, top = 16.dp), modifier = Modifier.padding(start = ShxSpacing.xl, top = ShxSpacing.lg),
) )
FlowRow( FlowRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 4.dp), .padding(horizontal = ShxSpacing.xl, vertical = ShxSpacing.xs),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(ShxSpacing.sm),
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(ShxSpacing.xs),
) )
{ {
values.forEach { value -> values.forEach { value ->
@@ -294,11 +295,11 @@ private fun EditorRow(icon: ImageVector, title: String, subtitle: String, onClic
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(horizontal = 24.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.xl, vertical = 14.dp),
) )
{ {
ShxIconBadge(icon) ShxIconBadge(icon)
Spacer(Modifier.width(16.dp)) Spacer(Modifier.width(ShxSpacing.lg))
Column(Modifier.weight(1f)) Column(Modifier.weight(1f))
{ {
Text(title, style = MaterialTheme.typography.bodyLarge) Text(title, style = MaterialTheme.typography.bodyLarge)
@@ -346,7 +347,7 @@ private fun AddChipField(
}, },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 4.dp), .padding(horizontal = ShxSpacing.xl, vertical = ShxSpacing.xs),
shape = MaterialTheme.shapes.medium, shape = MaterialTheme.shapes.medium,
) )
} }
@@ -526,7 +526,7 @@ fun SettingsScreen(profiles: List<VpnProfile>, showTitle: Boolean = true)
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 16.dp), .padding(top = ShxSpacing.lg),
textAlign = androidx.compose.ui.text.style.TextAlign.Center, textAlign = androidx.compose.ui.text.style.TextAlign.Center,
) )
} }
@@ -560,7 +560,7 @@ fun SettingsScreen(profiles: List<VpnProfile>, showTitle: Boolean = true)
) )
{ {
RadioButton(selected = defaultUuid == value, onClick = null) RadioButton(selected = defaultUuid == value, onClick = null)
Text(label, modifier = Modifier.padding(start = 12.dp)) Text(label, modifier = Modifier.padding(start = ShxSpacing.md))
} }
} }
} }
@@ -66,7 +66,6 @@ import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import org.strongswan.android.R import org.strongswan.android.R
@@ -76,6 +75,7 @@ import org.strongswan.android.logic.StrongSwanApplication
import org.strongswan.android.logic.TrustedCertificateManager import org.strongswan.android.logic.TrustedCertificateManager
import org.strongswan.android.ui.LogActivity import org.strongswan.android.ui.LogActivity
import org.strongswan.android.ui.VpnProfileDetailActivity import org.strongswan.android.ui.VpnProfileDetailActivity
import org.strongswan.android.ui.compose.theme.ShxSpacing
import org.strongswan.android.ui.compose.theme.ShxTheme import org.strongswan.android.ui.compose.theme.ShxTheme
fun ComponentActivity.installShxUi() fun ComponentActivity.installShxUi()
@@ -209,13 +209,13 @@ private fun ShareProfileDialog(profile: VpnProfile, onDismiss: () -> Unit, onCon
text = { text = {
Column { Column {
Text(stringResource(R.string.share_profile_password_warning), style = MaterialTheme.typography.bodyMedium) Text(stringResource(R.string.share_profile_password_warning), style = MaterialTheme.typography.bodyMedium)
Spacer(Modifier.height(12.dp)) Spacer(Modifier.height(ShxSpacing.md))
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { includePassword = !includePassword }, modifier = Modifier.clickable { includePassword = !includePassword },
) { ) {
Checkbox(checked = includePassword, onCheckedChange = { includePassword = it }) Checkbox(checked = includePassword, onCheckedChange = { includePassword = it })
Spacer(Modifier.width(4.dp)) Spacer(Modifier.width(ShxSpacing.xs))
Text(stringResource(R.string.share_profile_include_password)) Text(stringResource(R.string.share_profile_include_password))
} }
} }
@@ -69,7 +69,7 @@ import org.strongswan.android.ui.compose.components.ShxSettingRow
import org.strongswan.android.ui.compose.components.ShxSettingToggleRow import org.strongswan.android.ui.compose.components.ShxSettingToggleRow
import org.strongswan.android.data.VpnProfile import org.strongswan.android.data.VpnProfile
import org.strongswan.android.logic.autoconnect.AutoConnectManager import org.strongswan.android.logic.autoconnect.AutoConnectManager
import org.strongswan.android.ui.compose.theme.ScreenPadding import org.strongswan.android.ui.compose.theme.ShxSpacing
/** /**
* Trusted networks settings: when enabled, the VPN is started on cellular and untrusted * Trusted networks settings: when enabled, the VPN is started on cellular and untrusted
@@ -159,7 +159,7 @@ fun TrustedNetworksScreen(
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(bottom = 24.dp), .padding(bottom = ShxSpacing.xl),
) )
{ {
ShxSection(title = stringResource(R.string.settings_section_general)) ShxSection(title = stringResource(R.string.settings_section_general))
@@ -190,19 +190,19 @@ fun TrustedNetworksScreen(
if (enabled && !hasBackgroundLocation) if (enabled && !hasBackgroundLocation)
{ {
Spacer(Modifier.size(12.dp)) Spacer(Modifier.size(ShxSpacing.md))
Surface( Surface(
shape = MaterialTheme.shapes.large, shape = MaterialTheme.shapes.large,
color = MaterialTheme.colorScheme.errorContainer, color = MaterialTheme.colorScheme.errorContainer,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = ScreenPadding) .padding(horizontal = ShxSpacing.lg)
.clickable { requestSsidPermissions() }, .clickable { requestSsidPermissions() },
) )
{ {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), modifier = Modifier.padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.md),
) )
{ {
Text( Text(
@@ -226,7 +226,7 @@ fun TrustedNetworksScreen(
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.sm),
) )
if (config.trustedSsids.isEmpty()) if (config.trustedSsids.isEmpty())
{ {
@@ -236,7 +236,7 @@ fun TrustedNetworksScreen(
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
} }
else else
@@ -251,7 +251,7 @@ fun TrustedNetworksScreen(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp), .padding(horizontal = ShxSpacing.lg),
) )
{ {
Icon( Icon(
@@ -265,7 +265,7 @@ fun TrustedNetworksScreen(
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
IconButton(onClick = { pendingDeleteSsid = ssid }) { IconButton(onClick = { pendingDeleteSsid = ssid }) {
Icon( Icon(
@@ -309,7 +309,7 @@ fun TrustedNetworksScreen(
selected = config.profileUuid == profile.getUUID().toString(), selected = config.profileUuid == profile.getUUID().toString(),
onClick = null, onClick = null,
) )
Text(profile.name ?: "", modifier = Modifier.padding(start = 12.dp)) Text(profile.name ?: "", modifier = Modifier.padding(start = ShxSpacing.md))
} }
} }
} }
@@ -389,7 +389,7 @@ private fun TrustedAddNetworkRow(
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Center, horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Center,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 6.dp), .padding(horizontal = ShxSpacing.lg, vertical = 6.dp),
) )
{ {
if (currentSsid != null) if (currentSsid != null)
@@ -397,7 +397,7 @@ private fun TrustedAddNetworkRow(
TextButton(onClick = { onAddCurrent(currentSsid) }) TextButton(onClick = { onAddCurrent(currentSsid) })
{ {
Icon(Icons.Rounded.Add, contentDescription = null, modifier = Modifier.size(18.dp)) Icon(Icons.Rounded.Add, contentDescription = null, modifier = Modifier.size(18.dp))
Spacer(Modifier.size(8.dp)) Spacer(Modifier.size(ShxSpacing.sm))
Text(stringResource(R.string.auto_connect_add_current, currentSsid)) Text(stringResource(R.string.auto_connect_add_current, currentSsid))
} }
} }
@@ -406,7 +406,7 @@ private fun TrustedAddNetworkRow(
TextButton(onClick = onAddManual) TextButton(onClick = onAddManual)
{ {
Icon(Icons.Rounded.Add, contentDescription = null, modifier = Modifier.size(18.dp)) Icon(Icons.Rounded.Add, contentDescription = null, modifier = Modifier.size(18.dp))
Spacer(Modifier.size(8.dp)) Spacer(Modifier.size(ShxSpacing.sm))
Text(stringResource(R.string.auto_connect_add_network)) Text(stringResource(R.string.auto_connect_add_network))
} }
} }
@@ -40,12 +40,12 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import org.strongswan.android.R import org.strongswan.android.R
import org.strongswan.android.ui.compose.theme.ShxSpacing
import org.strongswan.android.ui.compose.theme.ShxTheme import org.strongswan.android.ui.compose.theme.ShxTheme
/** /**
@@ -147,7 +147,7 @@ private fun ConfirmationUi(dialog: VpnDialog.Confirmation)
title = { Text(dialog.title) }, title = { Text(dialog.title) },
text = { Text(stringResource(dialog.message)) }, text = { Text(stringResource(dialog.message)) },
confirmButton = { confirmButton = {
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(ShxSpacing.xs)) {
if (dialog.reconnect) if (dialog.reconnect)
{ {
TextButton(onClick = { TextButton(onClick = {
@@ -193,7 +193,7 @@ private fun LoginUi(dialog: VpnDialog.Login)
onDismissRequest = ::cancelDialog, onDismissRequest = ::cancelDialog,
title = { Text(stringResource(R.string.login_title)) }, title = { Text(stringResource(R.string.login_title)) },
text = { text = {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Column(verticalArrangement = Arrangement.spacedBy(ShxSpacing.md)) {
OutlinedTextField( OutlinedTextField(
value = username, value = username,
onValueChange = { username = it }, onValueChange = { username = it },
@@ -40,7 +40,7 @@ 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.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.strongswan.android.ui.compose.theme.ScreenPadding import org.strongswan.android.ui.compose.theme.ShxSpacing
/** Uppercase section header + grouped card, the standard grouped-list building block. */ /** Uppercase section header + grouped card, the standard grouped-list building block. */
@Composable @Composable
@@ -52,9 +52,9 @@ fun ShxSection(title: String, content: @Composable () -> Unit)
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(start = ScreenPadding + 16.dp, top = 20.dp, bottom = 8.dp), .padding(start = ShxSpacing.lg + ShxSpacing.lg, top = 20.dp, bottom = ShxSpacing.sm),
) )
ShxCard(modifier = Modifier.fillMaxWidth().padding(horizontal = ScreenPadding)) { ShxCard(modifier = Modifier.fillMaxWidth().padding(horizontal = ShxSpacing.lg)) {
Column { content() } Column { content() }
} }
} }
@@ -83,7 +83,7 @@ fun ShxIconBadge(icon: ImageVector, tint: Color = MaterialTheme.colorScheme.prim
contentDescription = null, contentDescription = null,
tint = tint, tint = tint,
modifier = Modifier modifier = Modifier
.padding(8.dp) .padding(ShxSpacing.sm)
.size(20.dp), .size(20.dp),
) )
} }
@@ -95,7 +95,7 @@ private fun RowScope.RowLabels(title: String, subtitle: String?, enabled: Boolea
Column( Column(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.padding(horizontal = 16.dp), .padding(horizontal = ShxSpacing.lg),
) )
{ {
Text( Text(
@@ -129,7 +129,7 @@ fun ShxSettingRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(enabled = enabled, onClick = onClick) .clickable(enabled = enabled, onClick = onClick)
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
{ {
ShxIconBadge(icon = icon, tint = if (enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant) ShxIconBadge(icon = icon, tint = if (enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant)
@@ -158,7 +158,7 @@ fun ShxSettingToggleRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(enabled = enabled) { onCheckedChange(!checked) } .clickable(enabled = enabled) { onCheckedChange(!checked) }
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
{ {
ShxIconBadge(icon = icon, tint = if (enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant) ShxIconBadge(icon = icon, tint = if (enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant)
@@ -198,7 +198,7 @@ fun ShxEmptyState(
Column( Column(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(24.dp), .padding(ShxSpacing.xl),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(0.dp), verticalArrangement = Arrangement.spacedBy(0.dp),
) )
@@ -225,7 +225,7 @@ fun ShxEmptyState(
) )
if (actionLabel != null && onAction != null) if (actionLabel != null && onAction != null)
{ {
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(ShxSpacing.lg))
OutlinedButton(onClick = onAction, shape = MaterialTheme.shapes.medium) { Text(actionLabel) } OutlinedButton(onClick = onAction, shape = MaterialTheme.shapes.medium) { Text(actionLabel) }
} }
} }
@@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.strongswan.android.ui.compose.theme.LocalShxStatusColors import org.strongswan.android.ui.compose.theme.LocalShxStatusColors
import org.strongswan.android.ui.compose.theme.ShxSpacing
/** Small rounded label with explicit colors, e.g. "Connected". */ /** Small rounded label with explicit colors, e.g. "Connected". */
@Composable @Composable
@@ -42,7 +43,7 @@ fun ShxStatusPill(
Text( Text(
text = text, text = text,
style = style, style = style,
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp), modifier = Modifier.padding(horizontal = 10.dp, vertical = ShxSpacing.xs),
) )
} }
} }
@@ -124,7 +124,6 @@ import org.strongswan.android.ui.compose.components.ShxIconBadge
import org.strongswan.android.ui.compose.components.ShxSection import org.strongswan.android.ui.compose.components.ShxSection
import org.strongswan.android.ui.compose.components.ShxSectionDivider import org.strongswan.android.ui.compose.components.ShxSectionDivider
import org.strongswan.android.ui.compose.theme.LocalShxStatusColors import org.strongswan.android.ui.compose.theme.LocalShxStatusColors
import org.strongswan.android.ui.compose.theme.ScreenPadding
import org.strongswan.android.ui.compose.theme.ShxSpacing import org.strongswan.android.ui.compose.theme.ShxSpacing
/** Geoip toggle row with a refresh/update slot in front of the switch. */ /** Geoip toggle row with a refresh/update slot in front of the switch. */
@@ -143,11 +142,11 @@ internal fun RuToggleRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { onCheckedChange(!checked) } .clickable { onCheckedChange(!checked) }
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
{ {
ShxIconBadge(Icons.Rounded.Block) ShxIconBadge(Icons.Rounded.Block)
Spacer(Modifier.width(16.dp)) Spacer(Modifier.width(ShxSpacing.lg))
Column(Modifier.weight(1f)) Column(Modifier.weight(1f))
{ {
Text(title, style = MaterialTheme.typography.bodyLarge) Text(title, style = MaterialTheme.typography.bodyLarge)
@@ -160,7 +159,7 @@ internal fun RuToggleRow(
if (updating) if (updating)
{ {
LoadingIndicator(Modifier.size(14.dp)) LoadingIndicator(Modifier.size(14.dp))
Spacer(Modifier.width(12.dp)) Spacer(Modifier.width(ShxSpacing.md))
} }
else else
{ {
@@ -186,7 +185,7 @@ internal fun RuModeSelector(mode: String, onModeChange: (String) -> Unit)
SingleChoiceSegmentedButtonRow( SingleChoiceSegmentedButtonRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 10.dp), .padding(horizontal = ShxSpacing.lg, vertical = 10.dp),
) )
{ {
SegmentedButton( SegmentedButton(
@@ -218,7 +217,7 @@ internal fun RuDetailsDisclosure(mode: String)
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { expanded = !expanded } .clickable { expanded = !expanded }
.padding(horizontal = 16.dp, vertical = 10.dp), .padding(horizontal = ShxSpacing.lg, vertical = 10.dp),
) )
{ {
Text( Text(
@@ -226,7 +225,7 @@ internal fun RuDetailsDisclosure(mode: String)
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
) )
Spacer(Modifier.width(4.dp)) Spacer(Modifier.width(ShxSpacing.xs))
Icon( Icon(
if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore, if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
contentDescription = null, contentDescription = null,
@@ -242,7 +241,7 @@ internal fun RuDetailsDisclosure(mode: String)
fadeOut(tween(90)), fadeOut(tween(90)),
) )
{ {
Column(Modifier.padding(start = 16.dp, end = 16.dp, bottom = 12.dp)) Column(Modifier.padding(start = ShxSpacing.lg, end = ShxSpacing.lg, bottom = ShxSpacing.md))
{ {
Text( Text(
text = stringResource( text = stringResource(
@@ -268,7 +267,7 @@ internal fun RuDetailsDisclosure(mode: String)
text = stringResource(R.string.rusplit_beta_note), text = stringResource(R.string.rusplit_beta_note),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp), modifier = Modifier.padding(top = ShxSpacing.xs),
) )
} }
} }
@@ -284,9 +283,9 @@ internal fun GeoipChipsRow(state: RuIpListState)
FlowRow( FlowRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 4.dp), .padding(start = ShxSpacing.lg, end = ShxSpacing.lg, bottom = ShxSpacing.xs),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(ShxSpacing.sm),
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(ShxSpacing.xs),
) )
{ {
InfoChip(text = stringResource(R.string.routing_geoip_ipv4, state.ipv4Ranges)) InfoChip(text = stringResource(R.string.routing_geoip_ipv4, state.ipv4Ranges))
@@ -341,14 +340,14 @@ internal fun ForcePrefixRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onEdit) .clickable(onClick = onEdit)
.padding(horizontal = 16.dp, vertical = 8.dp), .padding(horizontal = ShxSpacing.lg, vertical = ShxSpacing.sm),
) )
{ {
ShxIconBadge( ShxIconBadge(
Icons.Rounded.Lan, Icons.Rounded.Lan,
tint = if (entry.enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant, tint = if (entry.enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant,
) )
Spacer(Modifier.width(16.dp)) Spacer(Modifier.width(ShxSpacing.lg))
Text( Text(
entry.prefix, entry.prefix,
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
@@ -378,7 +377,7 @@ internal fun AddForcePrefixRow(onClick: () -> Unit)
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
{ {
Icon( Icon(
@@ -387,7 +386,7 @@ internal fun AddForcePrefixRow(onClick: () -> Unit)
tint = MaterialTheme.colorScheme.primary, tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(22.dp), modifier = Modifier.size(22.dp),
) )
Spacer(Modifier.width(16.dp)) Spacer(Modifier.width(ShxSpacing.lg))
Text( Text(
stringResource(R.string.routing_force_add), stringResource(R.string.routing_force_add),
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
@@ -124,7 +124,6 @@ import org.strongswan.android.ui.compose.components.ShxIconBadge
import org.strongswan.android.ui.compose.components.ShxSection import org.strongswan.android.ui.compose.components.ShxSection
import org.strongswan.android.ui.compose.components.ShxSectionDivider import org.strongswan.android.ui.compose.components.ShxSectionDivider
import org.strongswan.android.ui.compose.theme.LocalShxStatusColors import org.strongswan.android.ui.compose.theme.LocalShxStatusColors
import org.strongswan.android.ui.compose.theme.ScreenPadding
import org.strongswan.android.ui.compose.theme.ShxSpacing import org.strongswan.android.ui.compose.theme.ShxSpacing
/** Hero card: master switch plus a live summary of what the rules currently affect. */ /** Hero card: master switch plus a live summary of what the rules currently affect. */
@@ -147,7 +146,7 @@ internal fun RoutingHeroCard(
{ {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 16.dp, top = 14.dp, bottom = 14.dp, end = 8.dp), modifier = Modifier.padding(start = ShxSpacing.lg, top = 14.dp, bottom = 14.dp, end = ShxSpacing.sm),
) )
{ {
Icon( Icon(
@@ -155,7 +154,7 @@ internal fun RoutingHeroCard(
contentDescription = null, contentDescription = null,
tint = if (enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant, tint = if (enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant,
) )
Spacer(Modifier.width(16.dp)) Spacer(Modifier.width(ShxSpacing.lg))
Column(Modifier.weight(1f)) Column(Modifier.weight(1f))
{ {
Text( Text(
@@ -184,7 +183,7 @@ internal fun RoutingHeroCard(
) )
} }
Switch(checked = enabled, onCheckedChange = onToggle) Switch(checked = enabled, onCheckedChange = onToggle)
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(ShxSpacing.sm))
} }
} }
} }
@@ -198,11 +197,11 @@ internal fun RfActionRow(onClick: () -> Unit)
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
{ {
ShxIconBadge(Icons.Rounded.Block) ShxIconBadge(Icons.Rounded.Block)
Spacer(Modifier.width(16.dp)) Spacer(Modifier.width(ShxSpacing.lg))
Column(Modifier.weight(1f)) Column(Modifier.weight(1f))
{ {
Text( Text(
@@ -124,7 +124,6 @@ import org.strongswan.android.ui.compose.components.ShxIconBadge
import org.strongswan.android.ui.compose.components.ShxSection import org.strongswan.android.ui.compose.components.ShxSection
import org.strongswan.android.ui.compose.components.ShxSectionDivider import org.strongswan.android.ui.compose.components.ShxSectionDivider
import org.strongswan.android.ui.compose.theme.LocalShxStatusColors import org.strongswan.android.ui.compose.theme.LocalShxStatusColors
import org.strongswan.android.ui.compose.theme.ScreenPadding
import org.strongswan.android.ui.compose.theme.ShxSpacing import org.strongswan.android.ui.compose.theme.ShxSpacing
/** Collapsed rule set row: tap opens the editor sheet, the switch toggles in place. */ /** Collapsed rule set row: tap opens the editor sheet, the switch toggles in place. */
@@ -140,14 +139,14 @@ internal fun RuleSetRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onOpen) .clickable(onClick = onOpen)
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
{ {
ShxIconBadge( ShxIconBadge(
Icons.Rounded.Rule, Icons.Rounded.Rule,
tint = if (ruleSet.enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant, tint = if (ruleSet.enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant,
) )
Spacer(Modifier.width(16.dp)) Spacer(Modifier.width(ShxSpacing.lg))
Column(Modifier.weight(1f)) Column(Modifier.weight(1f))
{ {
Text( Text(
@@ -179,7 +178,7 @@ internal fun AddRuleRow(onClick: () -> Unit)
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 14.dp), .padding(horizontal = ShxSpacing.lg, vertical = 14.dp),
) )
{ {
Icon( Icon(
@@ -188,7 +187,7 @@ internal fun AddRuleRow(onClick: () -> Unit)
tint = MaterialTheme.colorScheme.primary, tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(22.dp), modifier = Modifier.size(22.dp),
) )
Spacer(Modifier.width(16.dp)) Spacer(Modifier.width(ShxSpacing.lg))
Text( Text(
stringResource(R.string.routing_add_rule), stringResource(R.string.routing_add_rule),
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,