android: routing - advanced block (RU details, GeoIP, force prefixes), kill switch vs excluded apps note
This commit is contained in:
1 parent
c5585df135
commit
8bd65d535e
4 files changed
+108
-50
No files matched your search
+103
-49
@@ -61,6 +61,12 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearWavyProgressIndicator
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.material.icons.rounded.ExpandLess
|
||||
import androidx.compose.material.icons.rounded.ExpandMore
|
||||
import androidx.compose.ui.semantics.heading
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
@@ -119,6 +125,7 @@ import org.strongswan.android.ui.compose.components.ShxSection
|
||||
import org.strongswan.android.ui.compose.components.ShxSectionDivider
|
||||
import org.strongswan.android.ui.compose.theme.LocalShxStatusColors
|
||||
import org.strongswan.android.ui.compose.theme.ScreenPadding
|
||||
import org.strongswan.android.ui.compose.theme.ShxSpacing
|
||||
|
||||
@Composable
|
||||
fun RoutingScreen()
|
||||
@@ -246,6 +253,19 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
|
||||
modifier = Modifier.padding(horizontal = ScreenPadding),
|
||||
)
|
||||
|
||||
/* kill switch + excluded apps: excluded apps are added to the VPN's disallowed list
|
||||
* (CharonVpnService.addDisallowedApplication), so the system lockdown does not
|
||||
* cover them - on a tunnel drop they keep using the normal network */
|
||||
if (config.enabled && excludedApps > 0 && remember { context.isVpnLockdownEnabled() })
|
||||
{
|
||||
Text(
|
||||
text = stringResource(R.string.routing_killswitch_note, excludedApps),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = ScreenPadding + ShxSpacing.xs, vertical = ShxSpacing.sm),
|
||||
)
|
||||
}
|
||||
|
||||
/* ---- dead direct destinations the engine keeps refusing ---- */
|
||||
val deadTargets by produceState(initialValue = emptyList<DeadTargetUi>())
|
||||
{
|
||||
@@ -322,55 +342,6 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
|
||||
onCheckedChange = { ruList.setEnabled(it) },
|
||||
onUpdate = { refreshGeoip() },
|
||||
)
|
||||
if (ruState.enabled)
|
||||
{
|
||||
ShxSectionDivider()
|
||||
RuModeSelector(
|
||||
mode = ruState.mode,
|
||||
onModeChange = { ruList.setMode(it) },
|
||||
)
|
||||
if (ruState.status == RuIpListStatus.UPDATING)
|
||||
{
|
||||
LinearWavyProgressIndicator(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
GeoipChipsRow(state = ruState)
|
||||
RuDetailsDisclosure(mode = ruState.mode)
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- force-tunnel prefixes: always through the tunnel ---- */
|
||||
ShxSection(title = stringResource(R.string.routing_force_section))
|
||||
{
|
||||
if (config.forceTunnelPrefixes.isEmpty())
|
||||
{
|
||||
Text(
|
||||
text = stringResource(R.string.routing_force_summary),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp),
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
config.forceTunnelPrefixes.forEachIndexed { index, entry ->
|
||||
ForcePrefixRow(
|
||||
entry = entry,
|
||||
onToggle = { manager.setForcePrefixEnabled(entry.id, it) },
|
||||
onEdit = { forceDialogId = entry.id },
|
||||
onDelete = { manager.deleteForcePrefix(entry.id) },
|
||||
)
|
||||
if (index < config.forceTunnelPrefixes.lastIndex)
|
||||
{
|
||||
ShxSectionDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
ShxSectionDivider()
|
||||
AddForcePrefixRow(onClick = { forceDialogId = "" })
|
||||
}
|
||||
|
||||
/* ---- rule sets ---- */
|
||||
@@ -410,6 +381,89 @@ private fun RoutingContent(snackbarHostState: SnackbarHostState)
|
||||
AddRuleRow(onClick = { addNewSet() })
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- advanced: userspace mode, GeoIP list, force-tunnel prefixes ---- */
|
||||
var advancedOpen by rememberSaveable { mutableStateOf(false) }
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { advancedOpen = !advancedOpen }
|
||||
.padding(horizontal = ScreenPadding + 16.dp, vertical = 16.dp),
|
||||
)
|
||||
{
|
||||
Text(
|
||||
text = stringResource(R.string.routing_advanced).uppercase(),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.weight(1f).semantics { heading() },
|
||||
)
|
||||
Icon(
|
||||
if (advancedOpen) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(visible = advancedOpen)
|
||||
{
|
||||
Column {
|
||||
if (ruState.enabled)
|
||||
{
|
||||
ShxSection(title = stringResource(R.string.routing_section_ru))
|
||||
{
|
||||
if (ruState.enabled)
|
||||
{
|
||||
RuModeSelector(
|
||||
mode = ruState.mode,
|
||||
onModeChange = { ruList.setMode(it) },
|
||||
)
|
||||
if (ruState.status == RuIpListStatus.UPDATING)
|
||||
{
|
||||
LinearWavyProgressIndicator(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
GeoipChipsRow(state = ruState)
|
||||
RuDetailsDisclosure(mode = ruState.mode)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/* ---- force-tunnel prefixes: always through the tunnel ---- */
|
||||
ShxSection(title = stringResource(R.string.routing_force_section))
|
||||
{
|
||||
if (config.forceTunnelPrefixes.isEmpty())
|
||||
{
|
||||
Text(
|
||||
text = stringResource(R.string.routing_force_summary),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp),
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
config.forceTunnelPrefixes.forEachIndexed { index, entry ->
|
||||
ForcePrefixRow(
|
||||
entry = entry,
|
||||
onToggle = { manager.setForcePrefixEnabled(entry.id, it) },
|
||||
onEdit = { forceDialogId = entry.id },
|
||||
onDelete = { manager.deleteForcePrefix(entry.id) },
|
||||
)
|
||||
if (index < config.forceTunnelPrefixes.lastIndex)
|
||||
{
|
||||
ShxSectionDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
ShxSectionDivider()
|
||||
AddForcePrefixRow(onClick = { forceDialogId = "" })
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- rule set editor, hosted outside the scrolling column ---- */
|
||||
|
||||
+1
-1
@@ -612,7 +612,7 @@ private fun updateStatusText(state: UpdateState): String
|
||||
* Checks whether the system-level lockdown ("Block connections without VPN") is
|
||||
* active for our package. Returns false if not determinable.
|
||||
*/
|
||||
private fun Context.isVpnLockdownEnabled(): Boolean
|
||||
internal fun Context.isVpnLockdownEnabled(): Boolean
|
||||
{
|
||||
return try
|
||||
{
|
||||
|
||||
@@ -491,4 +491,6 @@
|
||||
<string name="home_manage_profiles">Управление профилями</string>
|
||||
<string name="availability_expand">Показать детали</string>
|
||||
<string name="availability_collapse">Скрыть детали</string>
|
||||
<string name="routing_advanced">Дополнительно</string>
|
||||
<string name="routing_killswitch_note">Исключённые приложения (%1$d) всегда идут мимо VPN, в том числе при обрыве туннеля: kill switch их не блокирует.</string>
|
||||
</resources>
|
||||
@@ -496,4 +496,6 @@
|
||||
<string name="home_manage_profiles">Manage profiles</string>
|
||||
<string name="availability_expand">Show details</string>
|
||||
<string name="availability_collapse">Hide details</string>
|
||||
<string name="routing_advanced">Advanced</string>
|
||||
<string name="routing_killswitch_note">%1$d excluded app(s) always use the normal network, even when the tunnel drops: the kill switch does not block them.</string>
|
||||
</resources>
|
||||
Reference in new issue
Block a user