parent
3e6a2981cb
commit
f3debcfe45
|
@ -0,0 +1,281 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 15,
|
||||
"identityHash": "4827f3b1ab5a4e5aa35937a0925d50e4",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "Settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_tunnel_enabled` INTEGER NOT NULL, `is_tunnel_on_mobile_data_enabled` INTEGER NOT NULL, `trusted_network_ssids` TEXT NOT NULL, `is_always_on_vpn_enabled` INTEGER NOT NULL, `is_tunnel_on_ethernet_enabled` INTEGER NOT NULL, `is_shortcuts_enabled` INTEGER NOT NULL DEFAULT false, `is_tunnel_on_wifi_enabled` INTEGER NOT NULL DEFAULT false, `is_kernel_enabled` INTEGER NOT NULL DEFAULT false, `is_restore_on_boot_enabled` INTEGER NOT NULL DEFAULT false, `is_multi_tunnel_enabled` INTEGER NOT NULL DEFAULT false, `is_ping_enabled` INTEGER NOT NULL DEFAULT false, `is_amnezia_enabled` INTEGER NOT NULL DEFAULT false, `is_wildcards_enabled` INTEGER NOT NULL DEFAULT false, `is_wifi_by_shell_enabled` INTEGER NOT NULL DEFAULT false, `is_stop_on_no_internet_enabled` INTEGER NOT NULL DEFAULT false, `is_vpn_kill_switch_enabled` INTEGER NOT NULL DEFAULT false, `is_kernel_kill_switch_enabled` INTEGER NOT NULL DEFAULT false, `is_lan_on_kill_switch_enabled` INTEGER NOT NULL DEFAULT false, `debounce_delay_seconds` INTEGER NOT NULL DEFAULT 3, `is_disable_kill_switch_on_trusted_enabled` INTEGER NOT NULL DEFAULT false)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAutoTunnelEnabled",
|
||||
"columnName": "is_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnMobileDataEnabled",
|
||||
"columnName": "is_tunnel_on_mobile_data_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "trustedNetworkSSIDs",
|
||||
"columnName": "trusted_network_ssids",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAlwaysOnVpnEnabled",
|
||||
"columnName": "is_always_on_vpn_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnEthernetEnabled",
|
||||
"columnName": "is_tunnel_on_ethernet_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isShortcutsEnabled",
|
||||
"columnName": "is_shortcuts_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnWifiEnabled",
|
||||
"columnName": "is_tunnel_on_wifi_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isKernelEnabled",
|
||||
"columnName": "is_kernel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRestoreOnBootEnabled",
|
||||
"columnName": "is_restore_on_boot_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isMultiTunnelEnabled",
|
||||
"columnName": "is_multi_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPingEnabled",
|
||||
"columnName": "is_ping_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAmneziaEnabled",
|
||||
"columnName": "is_amnezia_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isWildcardsEnabled",
|
||||
"columnName": "is_wildcards_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isWifiNameByShellEnabled",
|
||||
"columnName": "is_wifi_by_shell_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isStopOnNoInternetEnabled",
|
||||
"columnName": "is_stop_on_no_internet_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVpnKillSwitchEnabled",
|
||||
"columnName": "is_vpn_kill_switch_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isKernelKillSwitchEnabled",
|
||||
"columnName": "is_kernel_kill_switch_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isLanOnKillSwitchEnabled",
|
||||
"columnName": "is_lan_on_kill_switch_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "debounceDelaySeconds",
|
||||
"columnName": "debounce_delay_seconds",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "3"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDisableKillSwitchOnTrustedEnabled",
|
||||
"columnName": "is_disable_kill_switch_on_trusted_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "TunnelConfig",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `wg_quick` TEXT NOT NULL, `tunnel_networks` TEXT NOT NULL DEFAULT '', `is_mobile_data_tunnel` INTEGER NOT NULL DEFAULT false, `is_primary_tunnel` INTEGER NOT NULL DEFAULT false, `am_quick` TEXT NOT NULL DEFAULT '', `is_Active` INTEGER NOT NULL DEFAULT false, `is_ping_enabled` INTEGER NOT NULL DEFAULT false, `ping_interval` INTEGER DEFAULT null, `ping_cooldown` INTEGER DEFAULT null, `ping_ip` TEXT DEFAULT null, `is_ethernet_tunnel` INTEGER NOT NULL DEFAULT false)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "wgQuick",
|
||||
"columnName": "wg_quick",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tunnelNetworks",
|
||||
"columnName": "tunnel_networks",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isMobileDataTunnel",
|
||||
"columnName": "is_mobile_data_tunnel",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPrimaryTunnel",
|
||||
"columnName": "is_primary_tunnel",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "amQuick",
|
||||
"columnName": "am_quick",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isActive",
|
||||
"columnName": "is_Active",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPingEnabled",
|
||||
"columnName": "is_ping_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingInterval",
|
||||
"columnName": "ping_interval",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false,
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingCooldown",
|
||||
"columnName": "ping_cooldown",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false,
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingIp",
|
||||
"columnName": "ping_ip",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false,
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isEthernetTunnel",
|
||||
"columnName": "is_ethernet_tunnel",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_TunnelConfig_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TunnelConfig_name` ON `${TABLE_NAME}` (`name`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4827f3b1ab5a4e5aa35937a0925d50e4')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
|||
|
||||
@Database(
|
||||
entities = [Settings::class, TunnelConfig::class],
|
||||
version = 14,
|
||||
version = 15,
|
||||
autoMigrations =
|
||||
[
|
||||
AutoMigration(from = 1, to = 2),
|
||||
|
@ -53,6 +53,10 @@ import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
|||
from = 13,
|
||||
to = 14,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 14,
|
||||
to = 15,
|
||||
),
|
||||
],
|
||||
exportSchema = true,
|
||||
)
|
||||
|
|
|
@ -85,6 +85,11 @@ data class Settings(
|
|||
defaultValue = "3",
|
||||
)
|
||||
val debounceDelaySeconds: Int = 3,
|
||||
@ColumnInfo(
|
||||
name = "is_disable_kill_switch_on_trusted_enabled",
|
||||
defaultValue = "false",
|
||||
)
|
||||
val isDisableKillSwitchOnTrustedEnabled: Boolean = false,
|
||||
) {
|
||||
fun debounceDelayMillis(): Long {
|
||||
return debounceDelaySeconds * 1000L
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.zaneschepke.wireguardautotunnel.module.MainImmediateDispatcher
|
|||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.AutoTunnelEvent
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.AutoTunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.KillSwitchEvent
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.NetworkState
|
||||
import com.zaneschepke.wireguardautotunnel.service.network.InternetConnectivityService
|
||||
import com.zaneschepke.wireguardautotunnel.service.network.NetworkService
|
||||
|
@ -24,6 +25,7 @@ import com.zaneschepke.wireguardautotunnel.service.network.NetworkStatus
|
|||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationAction
|
||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
|
||||
import com.zaneschepke.wireguardautotunnel.service.notification.WireGuardNotification
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.BackendState
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
|
||||
|
@ -111,6 +113,7 @@ class AutoTunnelService : LifecycleService() {
|
|||
}
|
||||
startAutoTunnelJob()
|
||||
startAutoTunnelStateJob()
|
||||
startKillSwitchJob()
|
||||
}.onFailure {
|
||||
Timber.e(it)
|
||||
}
|
||||
|
@ -181,7 +184,6 @@ class AutoTunnelService : LifecycleService() {
|
|||
) { double, networkState ->
|
||||
AutoTunnelState(tunnelService.get().vpnState.value, networkState, double.first, double.second)
|
||||
}.collect { state ->
|
||||
Timber.d("Network state: ${state.networkState}")
|
||||
autoTunnelStateFlow.update {
|
||||
it.copy(vpnState = state.vpnState, networkState = state.networkState, settings = state.settings, tunnels = state.tunnels)
|
||||
}
|
||||
|
@ -209,6 +211,23 @@ class AutoTunnelService : LifecycleService() {
|
|||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private fun startKillSwitchJob() = lifecycleScope.launch(ioDispatcher) {
|
||||
autoTunnelStateFlow.collect {
|
||||
if (it == defaultState) return@collect
|
||||
when (val event = it.asKillSwitchEvent()) {
|
||||
KillSwitchEvent.DoNothing -> Unit
|
||||
is KillSwitchEvent.Start -> {
|
||||
Timber.d("Starting kill switch")
|
||||
tunnelService.get().setBackendState(BackendState.KILL_SWITCH_ACTIVE, event.allowedIps)
|
||||
}
|
||||
KillSwitchEvent.Stop -> {
|
||||
Timber.d("Stopping kill switch")
|
||||
tunnelService.get().setBackendState(BackendState.SERVICE_ACTIVE, emptySet())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
private fun startAutoTunnelJob() = lifecycleScope.launch(ioDispatcher) {
|
||||
Timber.i("Starting auto-tunnel network event watcher")
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model
|
|||
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.BackendState
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnState
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isMatchingToWildcardList
|
||||
|
@ -52,6 +53,14 @@ data class AutoTunnelState(
|
|||
return networkState.isEthernetConnected && !settings.isTunnelOnEthernetEnabled && vpnState.status.isUp()
|
||||
}
|
||||
|
||||
private fun stopKillSwitchOnTrusted(): Boolean {
|
||||
return networkState.isWifiConnected && settings.isVpnKillSwitchEnabled && settings.isDisableKillSwitchOnTrustedEnabled && isCurrentSSIDTrusted() && vpnState.backendState == BackendState.KILL_SWITCH_ACTIVE
|
||||
}
|
||||
|
||||
private fun startKillSwitch(): Boolean {
|
||||
return settings.isVpnKillSwitchEnabled && vpnState.backendState != BackendState.KILL_SWITCH_ACTIVE && (!settings.isDisableKillSwitchOnTrustedEnabled || !isCurrentSSIDTrusted())
|
||||
}
|
||||
|
||||
fun isNoConnectivity(): Boolean {
|
||||
return !networkState.isEthernetConnected && !networkState.isWifiConnected && !networkState.isMobileDataConnected
|
||||
}
|
||||
|
@ -116,6 +125,17 @@ data class AutoTunnelState(
|
|||
}
|
||||
}
|
||||
|
||||
fun asKillSwitchEvent(): KillSwitchEvent {
|
||||
return when {
|
||||
stopKillSwitchOnTrusted() -> KillSwitchEvent.Stop
|
||||
startKillSwitch() -> {
|
||||
val allowedIps = if (settings.isLanOnKillSwitchEnabled) TunnelConfig.LAN_BYPASS_ALLOWED_IPS else emptySet()
|
||||
KillSwitchEvent.Start(allowedIps)
|
||||
}
|
||||
else -> KillSwitchEvent.DoNothing
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCurrentSSIDTrusted(): Boolean {
|
||||
return networkState.wifiName?.let {
|
||||
hasTrustedWifiName(it)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model
|
||||
|
||||
sealed class KillSwitchEvent {
|
||||
data class Start(val allowedIps: Set<String>) : KillSwitchEvent()
|
||||
data object Stop : KillSwitchEvent()
|
||||
data object DoNothing : KillSwitchEvent()
|
||||
}
|
|
@ -5,6 +5,7 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStati
|
|||
|
||||
data class VpnState(
|
||||
val status: TunnelState = TunnelState.DOWN,
|
||||
val backendState: BackendState = BackendState.INACTIVE,
|
||||
val tunnelConfig: TunnelConfig? = null,
|
||||
val statistics: TunnelStatistics? = null,
|
||||
)
|
||||
|
|
|
@ -222,6 +222,9 @@ constructor(
|
|||
when (val backend = backend()) {
|
||||
is org.amnezia.awg.backend.Backend -> {
|
||||
backend.setBackendState(backendState.asAmBackendState(), allowedIps)
|
||||
_vpnState.update {
|
||||
it.copy(backendState = backendState)
|
||||
}
|
||||
}
|
||||
is Backend -> {
|
||||
// TODO not yet implemented
|
||||
|
|
|
@ -198,7 +198,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
composable<Route.AutoTunnel> {
|
||||
AutoTunnelScreen(
|
||||
appUiState,
|
||||
appUiState.settings,
|
||||
)
|
||||
}
|
||||
composable<Route.Appearance> {
|
||||
|
@ -214,7 +214,7 @@ class MainActivity : AppCompatActivity() {
|
|||
SupportScreen(appUiState, viewModel)
|
||||
}
|
||||
composable<Route.AutoTunnelAdvanced> {
|
||||
AdvancedScreen(appUiState, viewModel)
|
||||
AdvancedScreen(appUiState.settings, viewModel)
|
||||
}
|
||||
composable<Route.Logs> {
|
||||
LogsScreen()
|
||||
|
|
|
@ -21,6 +21,7 @@ import androidx.compose.material.icons.outlined.Security
|
|||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.outlined.SettingsEthernet
|
||||
import androidx.compose.material.icons.outlined.SignalCellular4Bar
|
||||
import androidx.compose.material.icons.outlined.VpnKeyOff
|
||||
import androidx.compose.material.icons.outlined.Wifi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
@ -42,7 +43,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
|||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
|
@ -64,7 +65,7 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
|||
|
||||
@OptIn(ExperimentalPermissionsApi::class, ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltViewModel()) {
|
||||
fun AutoTunnelScreen(settings: Settings, viewModel: AutoTunnelViewModel = hiltViewModel()) {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
|
||||
|
@ -103,7 +104,7 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(uiState.settings.trustedNetworkSSIDs) {
|
||||
LaunchedEffect(settings.trustedNetworkSSIDs) {
|
||||
currentText = ""
|
||||
}
|
||||
|
||||
|
@ -154,15 +155,15 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
|
|||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
enabled = !uiState.settings.isAlwaysOnVpnEnabled,
|
||||
checked = uiState.settings.isTunnelOnWifiEnabled,
|
||||
enabled = !settings.isAlwaysOnVpnEnabled,
|
||||
checked = settings.isTunnelOnWifiEnabled,
|
||||
onClick = {
|
||||
viewModel.onToggleTunnelOnWifi()
|
||||
viewModel.onToggleTunnelOnWifi(settings)
|
||||
},
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.onToggleTunnelOnWifi()
|
||||
viewModel.onToggleTunnelOnWifi(settings)
|
||||
},
|
||||
),
|
||||
SelectionItem(
|
||||
|
@ -181,19 +182,19 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
|
|||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = uiState.settings.isWifiNameByShellEnabled,
|
||||
checked = settings.isWifiNameByShellEnabled,
|
||||
onClick = {
|
||||
viewModel.onRootShellWifiToggle()
|
||||
viewModel.onRootShellWifiToggle(settings)
|
||||
},
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.onRootShellWifiToggle()
|
||||
viewModel.onRootShellWifiToggle(settings)
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
if (uiState.settings.isTunnelOnWifiEnabled) {
|
||||
if (settings.isTunnelOnWifiEnabled) {
|
||||
addAll(
|
||||
listOf(
|
||||
SelectionItem(
|
||||
|
@ -209,14 +210,14 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
|
|||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = uiState.settings.isWildcardsEnabled,
|
||||
checked = settings.isWildcardsEnabled,
|
||||
onClick = {
|
||||
viewModel.onToggleWildcards()
|
||||
viewModel.onToggleWildcards(settings)
|
||||
},
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.onToggleWildcards()
|
||||
viewModel.onToggleWildcards(settings)
|
||||
},
|
||||
),
|
||||
SelectionItem(
|
||||
|
@ -255,21 +256,44 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
|
|||
},
|
||||
description = {
|
||||
TrustedNetworkTextBox(
|
||||
uiState.settings.trustedNetworkSSIDs,
|
||||
onDelete = viewModel::onDeleteTrustedSSID,
|
||||
settings.trustedNetworkSSIDs,
|
||||
onDelete = { viewModel.onDeleteTrustedSSID(it, settings) },
|
||||
currentText = currentText,
|
||||
onSave = { ssid ->
|
||||
if (uiState.settings.isWifiNameByShellEnabled || isWifiNameReadable()) viewModel.onSaveTrustedSSID(ssid)
|
||||
if (settings.isWifiNameByShellEnabled || isWifiNameReadable()) viewModel.onSaveTrustedSSID(ssid, settings)
|
||||
},
|
||||
onValueChange = { currentText = it },
|
||||
supporting = {
|
||||
if (uiState.settings.isWildcardsEnabled) {
|
||||
if (settings.isWildcardsEnabled) {
|
||||
WildcardsLabel()
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
),
|
||||
SelectionItem(
|
||||
Icons.Outlined.VpnKeyOff,
|
||||
title = {
|
||||
Text(
|
||||
stringResource(R.string.kill_switch_off),
|
||||
style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
|
||||
)
|
||||
},
|
||||
description = {
|
||||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
enabled = settings.isVpnKillSwitchEnabled,
|
||||
checked = settings.isDisableKillSwitchOnTrustedEnabled,
|
||||
onClick = {
|
||||
viewModel.onToggleStopKillSwitchOnTrusted(settings)
|
||||
},
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.onToggleStopKillSwitchOnTrusted(settings)
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -287,13 +311,13 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
|
|||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
enabled = !uiState.settings.isAlwaysOnVpnEnabled,
|
||||
checked = uiState.settings.isTunnelOnMobileDataEnabled,
|
||||
onClick = { viewModel.onToggleTunnelOnMobileData() },
|
||||
enabled = !settings.isAlwaysOnVpnEnabled,
|
||||
checked = settings.isTunnelOnMobileDataEnabled,
|
||||
onClick = { viewModel.onToggleTunnelOnMobileData(settings) },
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.onToggleTunnelOnMobileData()
|
||||
viewModel.onToggleTunnelOnMobileData(settings)
|
||||
},
|
||||
),
|
||||
SelectionItem(
|
||||
|
@ -306,13 +330,13 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
|
|||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
enabled = !uiState.settings.isAlwaysOnVpnEnabled,
|
||||
checked = uiState.settings.isTunnelOnEthernetEnabled,
|
||||
onClick = { viewModel.onToggleTunnelOnEthernet() },
|
||||
enabled = !settings.isAlwaysOnVpnEnabled,
|
||||
checked = settings.isTunnelOnEthernetEnabled,
|
||||
onClick = { viewModel.onToggleTunnelOnEthernet(settings) },
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.onToggleTunnelOnEthernet()
|
||||
viewModel.onToggleTunnelOnEthernet(settings)
|
||||
},
|
||||
),
|
||||
SelectionItem(
|
||||
|
@ -331,12 +355,12 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
|
|||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = uiState.settings.isStopOnNoInternetEnabled,
|
||||
onClick = { viewModel.onToggleStopOnNoInternet() },
|
||||
checked = settings.isStopOnNoInternetEnabled,
|
||||
onClick = { viewModel.onToggleStopOnNoInternet(settings) },
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.onToggleStopOnNoInternet()
|
||||
viewModel.onToggleStopOnNoInternet(settings)
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
@ -12,8 +12,6 @@ import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
|||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
@ -28,11 +26,8 @@ constructor(
|
|||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
|
||||
private val settings = appDataRepository.settings.getSettingsFlow()
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, Settings())
|
||||
|
||||
fun onToggleTunnelOnWifi() = viewModelScope.launch {
|
||||
with(settings.value) {
|
||||
fun onToggleTunnelOnWifi(settings: Settings) = viewModelScope.launch {
|
||||
with(settings) {
|
||||
appDataRepository.settings.save(
|
||||
copy(
|
||||
isTunnelOnWifiEnabled = !isTunnelOnWifiEnabled,
|
||||
|
@ -41,8 +36,8 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onToggleTunnelOnMobileData() = viewModelScope.launch {
|
||||
with(settings.value) {
|
||||
fun onToggleTunnelOnMobileData(settings: Settings) = viewModelScope.launch {
|
||||
with(settings) {
|
||||
appDataRepository.settings.save(
|
||||
copy(
|
||||
isTunnelOnMobileDataEnabled = !isTunnelOnMobileDataEnabled,
|
||||
|
@ -51,8 +46,8 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onToggleWildcards() = viewModelScope.launch {
|
||||
with(settings.value) {
|
||||
fun onToggleWildcards(settings: Settings) = viewModelScope.launch {
|
||||
with(settings) {
|
||||
appDataRepository.settings.save(
|
||||
copy(
|
||||
isWildcardsEnabled = !isWildcardsEnabled,
|
||||
|
@ -61,8 +56,8 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onDeleteTrustedSSID(ssid: String) = viewModelScope.launch {
|
||||
with(settings.value) {
|
||||
fun onDeleteTrustedSSID(ssid: String, settings: Settings) = viewModelScope.launch {
|
||||
with(settings) {
|
||||
appDataRepository.settings.save(
|
||||
copy(
|
||||
trustedNetworkSSIDs = (trustedNetworkSSIDs - ssid).toMutableList(),
|
||||
|
@ -71,9 +66,9 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onRootShellWifiToggle() = viewModelScope.launch {
|
||||
fun onRootShellWifiToggle(settings: Settings) = viewModelScope.launch {
|
||||
requestRoot().onSuccess {
|
||||
with(settings.value) {
|
||||
with(settings) {
|
||||
appDataRepository.settings.save(
|
||||
copy(isWifiNameByShellEnabled = !isWifiNameByShellEnabled),
|
||||
)
|
||||
|
@ -92,8 +87,8 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onToggleTunnelOnEthernet() = viewModelScope.launch {
|
||||
with(settings.value) {
|
||||
fun onToggleTunnelOnEthernet(settings: Settings) = viewModelScope.launch {
|
||||
with(settings) {
|
||||
appDataRepository.settings.save(
|
||||
copy(
|
||||
isTunnelOnEthernetEnabled = !isTunnelOnEthernetEnabled,
|
||||
|
@ -102,13 +97,16 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onSaveTrustedSSID(ssid: String) = viewModelScope.launch {
|
||||
fun onSaveTrustedSSID(ssid: String, settings: Settings) = viewModelScope.launch {
|
||||
if (ssid.isEmpty()) return@launch
|
||||
val trimmed = ssid.trim()
|
||||
with(settings.value) {
|
||||
with(settings) {
|
||||
if (!trustedNetworkSSIDs.contains(trimmed)) {
|
||||
this.trustedNetworkSSIDs.add(ssid)
|
||||
appDataRepository.settings.save(this)
|
||||
appDataRepository.settings.save(
|
||||
settings.copy(
|
||||
trustedNetworkSSIDs = (trustedNetworkSSIDs + ssid).toMutableList(),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
SnackbarController.showMessage(
|
||||
StringValue.StringResource(
|
||||
|
@ -119,11 +117,19 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onToggleStopOnNoInternet() = viewModelScope.launch {
|
||||
with(settings.value) {
|
||||
fun onToggleStopOnNoInternet(settings: Settings) = viewModelScope.launch {
|
||||
with(settings) {
|
||||
appDataRepository.settings.save(
|
||||
copy(isStopOnNoInternetEnabled = !isStopOnNoInternetEnabled),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onToggleStopKillSwitchOnTrusted(settings: Settings) = viewModelScope.launch {
|
||||
with(settings) {
|
||||
appDataRepository.settings.save(
|
||||
copy(isDisableKillSwitchOnTrustedEnabled = !isDisableKillSwitchOnTrustedEnabled),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,9 +18,7 @@ import androidx.compose.material3.MaterialTheme
|
|||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
|
@ -29,7 +27,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.ui.AppViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
|
@ -38,21 +36,11 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
|||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
||||
|
||||
@Composable
|
||||
fun AdvancedScreen(appUiState: AppUiState, appViewModel: AppViewModel) {
|
||||
fun AdvancedScreen(settings: Settings, appViewModel: AppViewModel) {
|
||||
var isDropDownExpanded by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
var selected by remember { mutableIntStateOf(appUiState.settings.debounceDelaySeconds) }
|
||||
|
||||
LaunchedEffect(selected) {
|
||||
if (selected == appUiState.settings.debounceDelaySeconds) return@LaunchedEffect
|
||||
appViewModel.saveSettings(appUiState.settings.copy(debounceDelaySeconds = selected))
|
||||
if (appUiState.settings.isAutoTunnelEnabled) {
|
||||
appViewModel.bounceAutoTunnel()
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopNavBar(stringResource(R.string.advanced_settings))
|
||||
|
@ -87,7 +75,7 @@ fun AdvancedScreen(appUiState: AppUiState, appViewModel: AppViewModel) {
|
|||
horizontalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterHorizontally),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(text = selected.toString(), style = MaterialTheme.typography.bodyMedium)
|
||||
Text(text = settings.debounceDelaySeconds.toString(), style = MaterialTheme.typography.bodyMedium)
|
||||
val icon = Icons.Default.ArrowDropDown
|
||||
Icon(icon, icon.name)
|
||||
}
|
||||
|
@ -107,7 +95,9 @@ fun AdvancedScreen(appUiState: AppUiState, appViewModel: AppViewModel) {
|
|||
},
|
||||
onClick = {
|
||||
isDropDownExpanded = false
|
||||
selected = num
|
||||
appViewModel.saveSettings(
|
||||
settings.copy(debounceDelaySeconds = num),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -205,4 +205,5 @@
|
|||
<string name="error_tunnel_start">Failed to starting tunnel</string>
|
||||
<string name="tunnel_control">Tunnel control</string>
|
||||
<string name="auto_tunnel">Auto-tunnel</string>
|
||||
<string name="kill_switch_off">Stop kill switch on trusted</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue