mirror of
https://github.com/wgtunnel/android.git
synced 2026-07-03 14:07:49 +02:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69e76e88e2 | |||
| c8b65fb7fa | |||
| feec7f0ffc | |||
| b63c6a9b73 | |||
| 46975607c4 | |||
| 0c7bcb5453 | |||
| 599bf9c9e0 | |||
| 03345bdf86 | |||
| b07e604003 | |||
| c8b3af4857 | |||
| 0a3447c63d | |||
| 7f3297db79 | |||
| aa33aebd2f | |||
| 53b1d03ca8 | |||
| 53f72850e2 |
@@ -20,7 +20,7 @@ jobs:
|
|||||||
- name: Check for new commits
|
- name: Check for new commits
|
||||||
id: check
|
id: check
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
NEW_COMMITS=$(git rev-list --count --after="$(date -Iseconds -d '23 hours ago')" ${{ github.sha }})
|
NEW_COMMITS=$(git rev-list --count --after="$(date -Iseconds -d '23 hours ago')" ${{ github.sha }})
|
||||||
echo "new_commits=$NEW_COMMITS" >> $GITHUB_OUTPUT
|
echo "new_commits=$NEW_COMMITS" >> $GITHUB_OUTPUT
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ android {
|
|||||||
|
|
||||||
licensee {
|
licensee {
|
||||||
Constants.allowedLicenses.forEach { allow(it) }
|
Constants.allowedLicenses.forEach { allow(it) }
|
||||||
allowUrl(Constants.XZING_LICENSE_URL)
|
Constants.allowedLicenseUrls.forEach { allowUrl(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationVariants.all {
|
applicationVariants.all {
|
||||||
|
|||||||
@@ -155,9 +155,7 @@
|
|||||||
"columnNames": [
|
"columnNames": [
|
||||||
"id"
|
"id"
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
"indices": [],
|
|
||||||
"foreignKeys": []
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "TunnelConfig",
|
"tableName": "TunnelConfig",
|
||||||
@@ -227,21 +225,18 @@
|
|||||||
"fieldPath": "pingInterval",
|
"fieldPath": "pingInterval",
|
||||||
"columnName": "ping_interval",
|
"columnName": "ping_interval",
|
||||||
"affinity": "INTEGER",
|
"affinity": "INTEGER",
|
||||||
"notNull": false,
|
|
||||||
"defaultValue": "null"
|
"defaultValue": "null"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "pingCooldown",
|
"fieldPath": "pingCooldown",
|
||||||
"columnName": "ping_cooldown",
|
"columnName": "ping_cooldown",
|
||||||
"affinity": "INTEGER",
|
"affinity": "INTEGER",
|
||||||
"notNull": false,
|
|
||||||
"defaultValue": "null"
|
"defaultValue": "null"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "pingIp",
|
"fieldPath": "pingIp",
|
||||||
"columnName": "ping_ip",
|
"columnName": "ping_ip",
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": false,
|
|
||||||
"defaultValue": "null"
|
"defaultValue": "null"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -275,11 +270,9 @@
|
|||||||
"orders": [],
|
"orders": [],
|
||||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TunnelConfig_name` ON `${TABLE_NAME}` (`name`)"
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TunnelConfig_name` ON `${TABLE_NAME}` (`name`)"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"foreignKeys": []
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"views": [],
|
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"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, 'ae51793c4d09ea3194ecd26f0606f35c')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ae51793c4d09ea3194ecd26f0606f35c')"
|
||||||
|
|||||||
@@ -0,0 +1,295 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 17,
|
||||||
|
"identityHash": "380d82359c99933cc9ce783347c4ec31",
|
||||||
|
"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_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, `is_tunnel_on_unsecure_enabled` INTEGER NOT NULL DEFAULT false, `split_tunnel_apps` TEXT NOT NULL DEFAULT '', `wifi_detection_method` INTEGER NOT NULL DEFAULT 0)",
|
||||||
|
"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": "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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isTunnelOnUnsecureEnabled",
|
||||||
|
"columnName": "is_tunnel_on_unsecure_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "splitTunnelApps",
|
||||||
|
"columnName": "split_tunnel_apps",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wifiDetectionMethod",
|
||||||
|
"columnName": "wifi_detection_method",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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, `is_ipv4_preferred` INTEGER NOT NULL DEFAULT true)",
|
||||||
|
"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",
|
||||||
|
"defaultValue": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pingCooldown",
|
||||||
|
"columnName": "ping_cooldown",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"defaultValue": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pingIp",
|
||||||
|
"columnName": "ping_ip",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isEthernetTunnel",
|
||||||
|
"columnName": "is_ethernet_tunnel",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isIpv4Preferred",
|
||||||
|
"columnName": "is_ipv4_preferred",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "true"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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`)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, '380d82359c99933cc9ce783347c4ec31')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ import com.zaneschepke.wireguardautotunnel.ui.navigation.components.DynamicTopAp
|
|||||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.components.currentNavBackStackEntryAsNavBarState
|
import com.zaneschepke.wireguardautotunnel.ui.navigation.components.currentNavBackStackEntryAsNavBarState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.AutoTunnelScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.AutoTunnelScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.advanced.AutoTunnelAdvancedScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.advanced.AutoTunnelAdvancedScreen
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.detection.WifiDetectionMethodScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.disclosure.LocationDisclosureScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.disclosure.LocationDisclosureScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.MainScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.main.MainScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.TunnelAutoTunnelScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.TunnelAutoTunnelScreen
|
||||||
@@ -284,6 +285,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
composable<Route.AutoTunnelAdvanced> {
|
composable<Route.AutoTunnelAdvanced> {
|
||||||
AutoTunnelAdvancedScreen(appUiState, viewModel)
|
AutoTunnelAdvancedScreen(appUiState, viewModel)
|
||||||
}
|
}
|
||||||
|
composable<Route.WifiDetectionMethod> {
|
||||||
|
WifiDetectionMethodScreen(appUiState, viewModel)
|
||||||
|
}
|
||||||
composable<Route.Logs> { LogsScreen(appViewState, viewModel) }
|
composable<Route.Logs> { LogsScreen(appViewState, viewModel) }
|
||||||
composable<Route.Config> { backStack ->
|
composable<Route.Config> { backStack ->
|
||||||
val args = backStack.toRoute<Route.Config>()
|
val args = backStack.toRoute<Route.Config>()
|
||||||
|
|||||||
+1
-1
@@ -14,9 +14,9 @@ import com.zaneschepke.wireguardautotunnel.core.notification.NotificationManager
|
|||||||
import com.zaneschepke.wireguardautotunnel.core.notification.WireGuardNotification
|
import com.zaneschepke.wireguardautotunnel.core.notification.WireGuardNotification
|
||||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
|
import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
|
|||||||
+43
-11
@@ -15,12 +15,12 @@ import com.zaneschepke.wireguardautotunnel.core.notification.WireGuardNotificati
|
|||||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
|
import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent
|
import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.events.KillSwitchEvent
|
import com.zaneschepke.wireguardautotunnel.domain.events.KillSwitchEvent
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.AutoTunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.AutoTunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.NetworkState
|
import com.zaneschepke.wireguardautotunnel.domain.state.NetworkState
|
||||||
@@ -33,6 +33,7 @@ import kotlinx.coroutines.CoroutineDispatcher
|
|||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@@ -72,6 +73,8 @@ class AutoTunnelService : LifecycleService() {
|
|||||||
|
|
||||||
private val binder = LocalBinder(this)
|
private val binder = LocalBinder(this)
|
||||||
|
|
||||||
|
private var isServiceRunning = false
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
launchWatcherNotification()
|
launchWatcherNotification()
|
||||||
@@ -90,6 +93,8 @@ class AutoTunnelService : LifecycleService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
|
if (isServiceRunning) return
|
||||||
|
isServiceRunning = true
|
||||||
kotlin
|
kotlin
|
||||||
.runCatching {
|
.runCatching {
|
||||||
launchWatcherNotification()
|
launchWatcherNotification()
|
||||||
@@ -102,6 +107,7 @@ class AutoTunnelService : LifecycleService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
|
isServiceRunning = false
|
||||||
wakeLock?.let { if (it.isHeld) it.release() }
|
wakeLock?.let { if (it.isHeld) it.release() }
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
@@ -261,18 +267,44 @@ class AutoTunnelService : LifecycleService() {
|
|||||||
lifecycleScope.launch(ioDispatcher) {
|
lifecycleScope.launch(ioDispatcher) {
|
||||||
Timber.i("Starting auto-tunnel network event watcher")
|
Timber.i("Starting auto-tunnel network event watcher")
|
||||||
val settings = appDataRepository.get().settings.get()
|
val settings = appDataRepository.get().settings.get()
|
||||||
Timber.d("Starting with debounce delay of: ${settings.debounceDelaySeconds} seconds")
|
|
||||||
|
var reevaluationJob: Job? = null
|
||||||
|
|
||||||
autoTunnelStateFlow.debounce(settings.debounceDelayMillis()).collect { watcherState ->
|
autoTunnelStateFlow.debounce(settings.debounceDelayMillis()).collect { watcherState ->
|
||||||
if (watcherState == defaultState) return@collect
|
if (watcherState == defaultState) return@collect
|
||||||
Timber.d("New auto tunnel state emitted ${watcherState.networkState}")
|
reevaluationJob?.cancel()
|
||||||
when (val event = watcherState.asAutoTunnelEvent()) {
|
handleAutoTunnelEvent(watcherState)
|
||||||
is AutoTunnelEvent.Start ->
|
|
||||||
(event.tunnelConf ?: appDataRepository.get().getPrimaryOrFirstTunnel())
|
// schedule one-time re-evaluation
|
||||||
?.let { tunnelManager.startTunnel(it) }
|
reevaluationJob = launch {
|
||||||
// TODO improve this to target specific tunnels to better support multi-tunnel
|
delay(REEVALUATE_CHECK_DELAY)
|
||||||
is AutoTunnelEvent.Stop -> tunnelManager.stopTunnel()
|
if (watcherState != defaultState) {
|
||||||
AutoTunnelEvent.DoNothing -> Timber.i("Auto-tunneling: no condition met")
|
Timber.d("Re-evaluating auto-tunnel state..")
|
||||||
|
handleAutoTunnelEvent(watcherState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun handleAutoTunnelEvent(watcherState: AutoTunnelState) {
|
||||||
|
Timber.i("Auto-tunnel settings: ${watcherState.settings.toAutoTunnelStateString()}")
|
||||||
|
Timber.i("Auto-tunnel network state: ${watcherState.networkState}")
|
||||||
|
when (
|
||||||
|
val event =
|
||||||
|
watcherState.asAutoTunnelEvent().also {
|
||||||
|
Timber.i("Auto-tunnel event: ${it.javaClass.simpleName}")
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
is AutoTunnelEvent.Start ->
|
||||||
|
(event.tunnelConf ?: appDataRepository.get().getPrimaryOrFirstTunnel())?.let {
|
||||||
|
tunnelManager.startTunnel(it)
|
||||||
|
}
|
||||||
|
is AutoTunnelEvent.Stop -> tunnelManager.stopTunnel()
|
||||||
|
AutoTunnelEvent.DoNothing -> Timber.i("Auto-tunneling: nothing to do")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val REEVALUATE_CHECK_DELAY = 5_000L
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ import com.zaneschepke.wireguardautotunnel.R
|
|||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package com.zaneschepke.wireguardautotunnel.core.tunnel
|
|||||||
import com.wireguard.android.backend.Tunnel
|
import com.wireguard.android.backend.Tunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||||
@@ -114,16 +114,16 @@ abstract class BaseTunnel(
|
|||||||
if (this@BaseTunnel is UserspaceTunnel) stopActiveTunnels()
|
if (this@BaseTunnel is UserspaceTunnel) stopActiveTunnels()
|
||||||
tunMutex.withLock {
|
tunMutex.withLock {
|
||||||
tunThreads[tunnelConf.id] = thread {
|
tunThreads[tunnelConf.id] = thread {
|
||||||
runBlocking {
|
try {
|
||||||
try {
|
runBlocking {
|
||||||
Timber.d("Starting tunnel ${tunnelConf.id}...")
|
Timber.d("Starting tunnel ${tunnelConf.id}...")
|
||||||
startTunnelInner(tunnelConf)
|
startTunnelInner(tunnelConf)
|
||||||
Timber.d("Started complete for tunnel ${tunnelConf.name}...")
|
Timber.d("Started complete for tunnel ${tunnelConf.name}...")
|
||||||
} catch (e: InterruptedException) {
|
|
||||||
Timber.w(
|
|
||||||
"Tunnel start has been interrupted as ${tunnelConf.name} failed to start"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
Timber.w(
|
||||||
|
"Tunnel start has been interrupted as ${tunnelConf.name} failed to start"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import com.wireguard.android.backend.BackendException
|
|||||||
import com.wireguard.android.backend.Tunnel
|
import com.wireguard.android.backend.Tunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.WireGuardStatistics
|
import com.zaneschepke.wireguardautotunnel.domain.state.WireGuardStatistics
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
|||||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.di.Kernel
|
import com.zaneschepke.wireguardautotunnel.di.Kernel
|
||||||
import com.zaneschepke.wireguardautotunnel.di.Userspace
|
import com.zaneschepke.wireguardautotunnel.di.Userspace
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|||||||
+1
-1
@@ -2,9 +2,9 @@ package com.zaneschepke.wireguardautotunnel.core.tunnel
|
|||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.AmneziaStatistics
|
import com.zaneschepke.wireguardautotunnel.domain.state.AmneziaStatistics
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import androidx.room.TypeConverters
|
|||||||
import androidx.room.migration.AutoMigrationSpec
|
import androidx.room.migration.AutoMigrationSpec
|
||||||
import com.zaneschepke.wireguardautotunnel.data.dao.SettingsDao
|
import com.zaneschepke.wireguardautotunnel.data.dao.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.data.dao.TunnelConfigDao
|
import com.zaneschepke.wireguardautotunnel.data.dao.TunnelConfigDao
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [Settings::class, TunnelConfig::class],
|
entities = [Settings::class, TunnelConfig::class],
|
||||||
version = 16,
|
version = 17,
|
||||||
autoMigrations =
|
autoMigrations =
|
||||||
[
|
[
|
||||||
AutoMigration(from = 1, to = 2),
|
AutoMigration(from = 1, to = 2),
|
||||||
@@ -31,10 +31,11 @@ import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
|||||||
AutoMigration(from = 13, to = 14),
|
AutoMigration(from = 13, to = 14),
|
||||||
AutoMigration(from = 14, to = 15),
|
AutoMigration(from = 14, to = 15),
|
||||||
AutoMigration(from = 15, to = 16),
|
AutoMigration(from = 15, to = 16),
|
||||||
|
AutoMigration(from = 16, to = 17, spec = WifiDetectionMigration::class),
|
||||||
],
|
],
|
||||||
exportSchema = true,
|
exportSchema = true,
|
||||||
)
|
)
|
||||||
@TypeConverters(DatabaseListConverters::class)
|
@TypeConverters(DatabaseConverters::class)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun settingDao(): SettingsDao
|
abstract fun settingDao(): SettingsDao
|
||||||
|
|
||||||
@@ -47,3 +48,6 @@ class RemoveLegacySettingColumnsMigration : AutoMigrationSpec
|
|||||||
|
|
||||||
@DeleteColumn(tableName = "Settings", columnName = "is_auto_tunnel_paused")
|
@DeleteColumn(tableName = "Settings", columnName = "is_auto_tunnel_paused")
|
||||||
class RemoveTunnelPauseMigration : AutoMigrationSpec
|
class RemoveTunnelPauseMigration : AutoMigrationSpec
|
||||||
|
|
||||||
|
@DeleteColumn(tableName = "Settings", columnName = "is_wifi_by_shell_enabled")
|
||||||
|
class WifiDetectionMigration : AutoMigrationSpec
|
||||||
|
|||||||
+8
-1
@@ -1,9 +1,10 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data
|
package com.zaneschepke.wireguardautotunnel.data
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
class DatabaseListConverters {
|
class DatabaseConverters {
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun listToString(value: MutableList<String>): String {
|
fun listToString(value: MutableList<String>): String {
|
||||||
return Json.encodeToString(value)
|
return Json.encodeToString(value)
|
||||||
@@ -20,4 +21,10 @@ class DatabaseListConverters {
|
|||||||
Json.decodeFromString<MutableList<String>>(json)
|
Json.decodeFromString<MutableList<String>>(json)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TypeConverter fun fromStatus(status: Settings.WifiDetectionMethod): Int = status.value
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun toStatus(value: Int): Settings.WifiDetectionMethod =
|
||||||
|
Settings.WifiDetectionMethod.fromValue(value)
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import androidx.room.Delete
|
|||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import androidx.room.Delete
|
|||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
|
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.model
|
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||||
|
|
||||||
|
data class GeneralState(
|
||||||
|
val isLocationDisclosureShown: Boolean = LOCATION_DISCLOSURE_SHOWN_DEFAULT,
|
||||||
|
val isBatteryOptimizationDisableShown: Boolean = BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT,
|
||||||
|
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
|
||||||
|
val expandedTunnelIds: List<Int> = emptyList(),
|
||||||
|
val isLocalLogsEnabled: Boolean = IS_LOGS_ENABLED_DEFAULT,
|
||||||
|
val isRemoteControlEnabled: Boolean = IS_REMOTE_CONTROL_ENABLED,
|
||||||
|
val remoteKey: String? = null,
|
||||||
|
val locale: String? = null,
|
||||||
|
val theme: Theme = Theme.AUTOMATIC,
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val LOCATION_DISCLOSURE_SHOWN_DEFAULT = false
|
||||||
|
const val BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT = false
|
||||||
|
const val PIN_LOCK_ENABLED_DEFAULT = false
|
||||||
|
const val IS_LOGS_ENABLED_DEFAULT = false
|
||||||
|
const val IS_REMOTE_CONTROL_ENABLED = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GitHubRelease(
|
||||||
|
@SerialName("tag_name") val tagName: String,
|
||||||
|
val name: String?,
|
||||||
|
val body: String?,
|
||||||
|
val assets: List<Asset>,
|
||||||
|
)
|
||||||
+15
-56
@@ -1,9 +1,8 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.model
|
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
data class Settings(
|
data class Settings(
|
||||||
@@ -32,8 +31,6 @@ data class Settings(
|
|||||||
val isAmneziaEnabled: Boolean = false,
|
val isAmneziaEnabled: Boolean = false,
|
||||||
@ColumnInfo(name = "is_wildcards_enabled", defaultValue = "false")
|
@ColumnInfo(name = "is_wildcards_enabled", defaultValue = "false")
|
||||||
val isWildcardsEnabled: Boolean = false,
|
val isWildcardsEnabled: Boolean = false,
|
||||||
@ColumnInfo(name = "is_wifi_by_shell_enabled", defaultValue = "false")
|
|
||||||
val isWifiNameByShellEnabled: Boolean = false,
|
|
||||||
@ColumnInfo(name = "is_stop_on_no_internet_enabled", defaultValue = "false")
|
@ColumnInfo(name = "is_stop_on_no_internet_enabled", defaultValue = "false")
|
||||||
val isStopOnNoInternetEnabled: Boolean = false,
|
val isStopOnNoInternetEnabled: Boolean = false,
|
||||||
@ColumnInfo(name = "is_vpn_kill_switch_enabled", defaultValue = "false")
|
@ColumnInfo(name = "is_vpn_kill_switch_enabled", defaultValue = "false")
|
||||||
@@ -46,61 +43,23 @@ data class Settings(
|
|||||||
val debounceDelaySeconds: Int = 3,
|
val debounceDelaySeconds: Int = 3,
|
||||||
@ColumnInfo(name = "is_disable_kill_switch_on_trusted_enabled", defaultValue = "false")
|
@ColumnInfo(name = "is_disable_kill_switch_on_trusted_enabled", defaultValue = "false")
|
||||||
val isDisableKillSwitchOnTrustedEnabled: Boolean = false,
|
val isDisableKillSwitchOnTrustedEnabled: Boolean = false,
|
||||||
|
@ColumnInfo(name = "is_tunnel_on_unsecure_enabled", defaultValue = "false")
|
||||||
|
val isTunnelOnUnsecureEnabled: Boolean = false,
|
||||||
|
@ColumnInfo(name = "split_tunnel_apps", defaultValue = "")
|
||||||
|
val splitTunnelApps: MutableList<String> = mutableListOf(),
|
||||||
|
@ColumnInfo(name = "wifi_detection_method", defaultValue = "0")
|
||||||
|
val wifiDetectionMethod: WifiDetectionMethod = WifiDetectionMethod.fromValue(0),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun toAppSettings(): AppSettings {
|
enum class WifiDetectionMethod(val value: Int) {
|
||||||
return AppSettings(
|
DEFAULT(0),
|
||||||
id,
|
LEGACY(1),
|
||||||
isAutoTunnelEnabled,
|
ROOT(2),
|
||||||
isTunnelOnMobileDataEnabled,
|
SHIZUKU(3);
|
||||||
trustedNetworkSSIDs,
|
|
||||||
isAlwaysOnVpnEnabled,
|
|
||||||
isTunnelOnEthernetEnabled,
|
|
||||||
isShortcutsEnabled,
|
|
||||||
isTunnelOnWifiEnabled,
|
|
||||||
isKernelEnabled,
|
|
||||||
isRestoreOnBootEnabled,
|
|
||||||
isMultiTunnelEnabled,
|
|
||||||
isPingEnabled,
|
|
||||||
isAmneziaEnabled,
|
|
||||||
isWildcardsEnabled,
|
|
||||||
isWifiNameByShellEnabled,
|
|
||||||
isStopOnNoInternetEnabled,
|
|
||||||
isVpnKillSwitchEnabled,
|
|
||||||
isKernelKillSwitchEnabled,
|
|
||||||
isLanOnKillSwitchEnabled,
|
|
||||||
debounceDelaySeconds,
|
|
||||||
isDisableKillSwitchOnTrustedEnabled,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(appSettings: AppSettings): Settings {
|
fun fromValue(value: Int): WifiDetectionMethod =
|
||||||
return with(appSettings) {
|
entries.find { it.value == value } ?: DEFAULT
|
||||||
Settings(
|
|
||||||
id,
|
|
||||||
isAutoTunnelEnabled,
|
|
||||||
isTunnelOnMobileDataEnabled,
|
|
||||||
trustedNetworkSSIDs.toMutableList(),
|
|
||||||
isAlwaysOnVpnEnabled,
|
|
||||||
isTunnelOnEthernetEnabled,
|
|
||||||
isShortcutsEnabled,
|
|
||||||
isTunnelOnWifiEnabled,
|
|
||||||
isKernelEnabled,
|
|
||||||
isRestoreOnBootEnabled,
|
|
||||||
isMultiTunnelEnabled,
|
|
||||||
isPingEnabled,
|
|
||||||
isAmneziaEnabled,
|
|
||||||
isWildcardsEnabled,
|
|
||||||
isWifiNameByShellEnabled,
|
|
||||||
isStopOnNoInternetEnabled,
|
|
||||||
isVpnKillSwitchEnabled,
|
|
||||||
isKernelKillSwitchEnabled,
|
|
||||||
isLanOnKillSwitchEnabled,
|
|
||||||
debounceDelaySeconds,
|
|
||||||
isDisableKillSwitchOnTrustedEnabled,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-43
@@ -1,10 +1,9 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.model
|
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
|
|
||||||
@Entity(indices = [Index(value = ["name"], unique = true)])
|
@Entity(indices = [Index(value = ["name"], unique = true)])
|
||||||
data class TunnelConfig(
|
data class TunnelConfig(
|
||||||
@@ -30,48 +29,7 @@ data class TunnelConfig(
|
|||||||
var isIpv4Preferred: Boolean = true,
|
var isIpv4Preferred: Boolean = true,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun toTunnel(): TunnelConf {
|
|
||||||
return TunnelConf(
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
wgQuick,
|
|
||||||
tunnelNetworks,
|
|
||||||
isMobileDataTunnel,
|
|
||||||
isPrimaryTunnel,
|
|
||||||
amQuick,
|
|
||||||
isActive,
|
|
||||||
isPingEnabled,
|
|
||||||
pingInterval,
|
|
||||||
pingCooldown,
|
|
||||||
pingIp,
|
|
||||||
isEthernetTunnel,
|
|
||||||
isIpv4Preferred,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val AM_QUICK_DEFAULT = ""
|
const val AM_QUICK_DEFAULT = ""
|
||||||
|
|
||||||
fun from(tunnelConf: TunnelConf): TunnelConfig {
|
|
||||||
return with(tunnelConf) {
|
|
||||||
return TunnelConfig(
|
|
||||||
id,
|
|
||||||
tunName,
|
|
||||||
wgQuick,
|
|
||||||
tunnelNetworks.toMutableList(),
|
|
||||||
isMobileDataTunnel,
|
|
||||||
isPrimaryTunnel,
|
|
||||||
amQuick,
|
|
||||||
isActive,
|
|
||||||
isPingEnabled,
|
|
||||||
pingInterval,
|
|
||||||
pingCooldown,
|
|
||||||
pingIp,
|
|
||||||
isEthernetTunnel,
|
|
||||||
isIpv4Preferred,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.entity.GeneralState
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||||
|
|
||||||
|
object GeneralStateMapper {
|
||||||
|
fun toAppState(generalState: GeneralState): AppState =
|
||||||
|
with(generalState) {
|
||||||
|
AppState(
|
||||||
|
isLocationDisclosureShown,
|
||||||
|
isBatteryOptimizationDisableShown,
|
||||||
|
isPinLockEnabled,
|
||||||
|
expandedTunnelIds,
|
||||||
|
isLocalLogsEnabled,
|
||||||
|
isRemoteControlEnabled,
|
||||||
|
remoteKey,
|
||||||
|
locale,
|
||||||
|
theme,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toGeneralState(appState: AppState): GeneralState {
|
||||||
|
return with(appState) {
|
||||||
|
GeneralState(
|
||||||
|
isLocationDisclosureShown,
|
||||||
|
isBatteryOptimizationDisableShown,
|
||||||
|
isPinLockEnabled,
|
||||||
|
expandedTunnelIds,
|
||||||
|
isLocalLogsEnabled,
|
||||||
|
isRemoteControlEnabled,
|
||||||
|
remoteKey,
|
||||||
|
locale,
|
||||||
|
theme,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.entity.GitHubRelease
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||||
|
import kotlin.collections.firstOrNull
|
||||||
|
|
||||||
|
object GitHubReleaseMapper {
|
||||||
|
fun toAppUpdate(gitHubRelease: GitHubRelease, newVersion: String): AppUpdate {
|
||||||
|
with(gitHubRelease) {
|
||||||
|
val apkAsset = assets.firstOrNull { it.name.endsWith(".apk") }
|
||||||
|
return AppUpdate(
|
||||||
|
version = newVersion,
|
||||||
|
title = name ?: "Update $tagName",
|
||||||
|
releaseNotes = body ?: "No release notes provided",
|
||||||
|
apkUrl = apkAsset?.browserDownloadUrl,
|
||||||
|
apkFileName = apkAsset?.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||||
|
|
||||||
|
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
|
|
||||||
|
object SettingsMapper {
|
||||||
|
fun toAppSettings(settings: Settings): AppSettings {
|
||||||
|
return AppSettings(
|
||||||
|
id = settings.id,
|
||||||
|
isAutoTunnelEnabled = settings.isAutoTunnelEnabled,
|
||||||
|
isTunnelOnMobileDataEnabled = settings.isTunnelOnMobileDataEnabled,
|
||||||
|
trustedNetworkSSIDs = settings.trustedNetworkSSIDs,
|
||||||
|
isAlwaysOnVpnEnabled = settings.isAlwaysOnVpnEnabled,
|
||||||
|
isTunnelOnEthernetEnabled = settings.isTunnelOnEthernetEnabled,
|
||||||
|
isShortcutsEnabled = settings.isShortcutsEnabled,
|
||||||
|
isTunnelOnWifiEnabled = settings.isTunnelOnWifiEnabled,
|
||||||
|
isKernelEnabled = settings.isKernelEnabled,
|
||||||
|
isRestoreOnBootEnabled = settings.isRestoreOnBootEnabled,
|
||||||
|
isMultiTunnelEnabled = settings.isMultiTunnelEnabled,
|
||||||
|
isPingEnabled = settings.isPingEnabled,
|
||||||
|
isAmneziaEnabled = settings.isAmneziaEnabled,
|
||||||
|
isWildcardsEnabled = settings.isWildcardsEnabled,
|
||||||
|
isStopOnNoInternetEnabled = settings.isStopOnNoInternetEnabled,
|
||||||
|
isVpnKillSwitchEnabled = settings.isVpnKillSwitchEnabled,
|
||||||
|
isKernelKillSwitchEnabled = settings.isKernelKillSwitchEnabled,
|
||||||
|
isLanOnKillSwitchEnabled = settings.isLanOnKillSwitchEnabled,
|
||||||
|
debounceDelaySeconds = settings.debounceDelaySeconds,
|
||||||
|
isDisableKillSwitchOnTrustedEnabled = settings.isDisableKillSwitchOnTrustedEnabled,
|
||||||
|
isTunnelOnUnsecureEnabled = settings.isTunnelOnUnsecureEnabled,
|
||||||
|
splitTunnelApps = settings.splitTunnelApps,
|
||||||
|
wifiDetectionMethod =
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.fromValue(
|
||||||
|
settings.wifiDetectionMethod.value
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toSettings(appSettings: AppSettings): Settings {
|
||||||
|
return Settings(
|
||||||
|
id = appSettings.id,
|
||||||
|
isAutoTunnelEnabled = appSettings.isAutoTunnelEnabled,
|
||||||
|
isTunnelOnMobileDataEnabled = appSettings.isTunnelOnMobileDataEnabled,
|
||||||
|
trustedNetworkSSIDs = appSettings.trustedNetworkSSIDs.toMutableList(),
|
||||||
|
isAlwaysOnVpnEnabled = appSettings.isAlwaysOnVpnEnabled,
|
||||||
|
isTunnelOnEthernetEnabled = appSettings.isTunnelOnEthernetEnabled,
|
||||||
|
isShortcutsEnabled = appSettings.isShortcutsEnabled,
|
||||||
|
isTunnelOnWifiEnabled = appSettings.isTunnelOnWifiEnabled,
|
||||||
|
isKernelEnabled = appSettings.isKernelEnabled,
|
||||||
|
isRestoreOnBootEnabled = appSettings.isRestoreOnBootEnabled,
|
||||||
|
isMultiTunnelEnabled = appSettings.isMultiTunnelEnabled,
|
||||||
|
isPingEnabled = appSettings.isPingEnabled,
|
||||||
|
isAmneziaEnabled = appSettings.isAmneziaEnabled,
|
||||||
|
isWildcardsEnabled = appSettings.isWildcardsEnabled,
|
||||||
|
isStopOnNoInternetEnabled = appSettings.isStopOnNoInternetEnabled,
|
||||||
|
isVpnKillSwitchEnabled = appSettings.isVpnKillSwitchEnabled,
|
||||||
|
isKernelKillSwitchEnabled = appSettings.isKernelKillSwitchEnabled,
|
||||||
|
isLanOnKillSwitchEnabled = appSettings.isLanOnKillSwitchEnabled,
|
||||||
|
debounceDelaySeconds = appSettings.debounceDelaySeconds,
|
||||||
|
isDisableKillSwitchOnTrustedEnabled = appSettings.isDisableKillSwitchOnTrustedEnabled,
|
||||||
|
isTunnelOnUnsecureEnabled = appSettings.isTunnelOnUnsecureEnabled,
|
||||||
|
splitTunnelApps = appSettings.splitTunnelApps.toMutableList(),
|
||||||
|
wifiDetectionMethod =
|
||||||
|
Settings.WifiDetectionMethod.fromValue(appSettings.wifiDetectionMethod.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
|
|
||||||
|
object TunnelConfigMapper {
|
||||||
|
fun toTunnelConf(tunnelConfig: TunnelConfig): TunnelConf {
|
||||||
|
return with(tunnelConfig) {
|
||||||
|
TunnelConf(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
wgQuick,
|
||||||
|
tunnelNetworks,
|
||||||
|
isMobileDataTunnel,
|
||||||
|
isPrimaryTunnel,
|
||||||
|
amQuick,
|
||||||
|
isActive,
|
||||||
|
isPingEnabled,
|
||||||
|
pingInterval,
|
||||||
|
pingCooldown,
|
||||||
|
pingIp,
|
||||||
|
isEthernetTunnel,
|
||||||
|
isIpv4Preferred,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toTunnelConfig(tunnelConf: TunnelConf): TunnelConfig {
|
||||||
|
return with(tunnelConf) {
|
||||||
|
TunnelConfig(
|
||||||
|
id,
|
||||||
|
tunName,
|
||||||
|
wgQuick,
|
||||||
|
tunnelNetworks.toMutableList(),
|
||||||
|
isMobileDataTunnel,
|
||||||
|
isPrimaryTunnel,
|
||||||
|
amQuick,
|
||||||
|
isActive,
|
||||||
|
isPingEnabled,
|
||||||
|
pingInterval,
|
||||||
|
pingCooldown,
|
||||||
|
pingIp,
|
||||||
|
isEthernetTunnel,
|
||||||
|
isIpv4Preferred,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.model
|
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
|
||||||
|
|
||||||
data class GeneralState(
|
|
||||||
val isLocationDisclosureShown: Boolean = LOCATION_DISCLOSURE_SHOWN_DEFAULT,
|
|
||||||
val isBatteryOptimizationDisableShown: Boolean = BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT,
|
|
||||||
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
|
|
||||||
val expandedTunnelIds: List<Int> = emptyList(),
|
|
||||||
val isLocalLogsEnabled: Boolean = IS_LOGS_ENABLED_DEFAULT,
|
|
||||||
val isRemoteControlEnabled: Boolean = IS_REMOTE_CONTROL_ENABLED,
|
|
||||||
val remoteKey: String? = null,
|
|
||||||
val locale: String? = null,
|
|
||||||
val theme: Theme = Theme.AUTOMATIC,
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun toAppState(): AppState =
|
|
||||||
AppState(
|
|
||||||
isLocationDisclosureShown,
|
|
||||||
isBatteryOptimizationDisableShown,
|
|
||||||
isPinLockEnabled,
|
|
||||||
expandedTunnelIds,
|
|
||||||
isLocalLogsEnabled,
|
|
||||||
isRemoteControlEnabled,
|
|
||||||
remoteKey,
|
|
||||||
locale,
|
|
||||||
theme,
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun from(appState: AppState): GeneralState {
|
|
||||||
return with(appState) {
|
|
||||||
GeneralState(
|
|
||||||
isLocationDisclosureShown,
|
|
||||||
isBatteryOptimizationDisableShown,
|
|
||||||
isPinLockEnabled,
|
|
||||||
expandedTunnelIds,
|
|
||||||
isLocalLogsEnabled,
|
|
||||||
isRemoteControlEnabled,
|
|
||||||
remoteKey,
|
|
||||||
locale,
|
|
||||||
theme,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const val LOCATION_DISCLOSURE_SHOWN_DEFAULT = false
|
|
||||||
const val BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT = false
|
|
||||||
const val PIN_LOCK_ENABLED_DEFAULT = false
|
|
||||||
const val IS_LOGS_ENABLED_DEFAULT = false
|
|
||||||
const val IS_REMOTE_CONTROL_ENABLED = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.model
|
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class GitHubRelease(
|
|
||||||
@SerialName("tag_name") val tagName: String,
|
|
||||||
val name: String?,
|
|
||||||
val body: String?,
|
|
||||||
val assets: List<Asset>,
|
|
||||||
) {
|
|
||||||
fun toAppUpdate(): AppUpdate {
|
|
||||||
val apkAsset = assets.firstOrNull { it.name.endsWith(".apk") }
|
|
||||||
return AppUpdate(
|
|
||||||
version = tagName.removePrefix("v"),
|
|
||||||
title = name ?: "Update $tagName",
|
|
||||||
releaseNotes = body ?: "No release notes provided",
|
|
||||||
apkUrl = apkAsset?.browserDownloadUrl,
|
|
||||||
apkFileName = apkAsset?.name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.network
|
package com.zaneschepke.wireguardautotunnel.data.network
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.GitHubRelease
|
import com.zaneschepke.wireguardautotunnel.data.entity.GitHubRelease
|
||||||
|
|
||||||
interface GitHubApi {
|
interface GitHubApi {
|
||||||
suspend fun getLatestRelease(owner: String, repo: String): Result<GitHubRelease>
|
suspend fun getLatestRelease(owner: String, repo: String): Result<GitHubRelease>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.network
|
package com.zaneschepke.wireguardautotunnel.data.network
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.GitHubRelease
|
import com.zaneschepke.wireguardautotunnel.data.entity.GitHubRelease
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
import io.ktor.client.plugins.ClientRequestException
|
import io.ktor.client.plugins.ClientRequestException
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppSettingRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppSettingRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
||||||
|
|||||||
+4
-3
@@ -1,8 +1,9 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.DataStoreManager
|
import com.zaneschepke.wireguardautotunnel.data.DataStoreManager
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.GeneralState
|
import com.zaneschepke.wireguardautotunnel.data.entity.GeneralState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
import com.zaneschepke.wireguardautotunnel.data.mapper.GeneralStateMapper
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -153,5 +154,5 @@ class DataStoreAppStateRepository(private val dataStoreManager: DataStoreManager
|
|||||||
}
|
}
|
||||||
} ?: GeneralState()
|
} ?: GeneralState()
|
||||||
}
|
}
|
||||||
.map { it.toAppState() }
|
.map(GeneralStateMapper::toAppState)
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-8
@@ -2,10 +2,12 @@ package com.zaneschepke.wireguardautotunnel.data.repository
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.mapper.GitHubReleaseMapper
|
||||||
import com.zaneschepke.wireguardautotunnel.data.network.GitHubApi
|
import com.zaneschepke.wireguardautotunnel.data.network.GitHubApi
|
||||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.UpdateRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.UpdateRepository
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
@@ -30,24 +32,30 @@ class GitHubUpdateRepository(
|
|||||||
override suspend fun checkForUpdate(currentVersion: String): Result<AppUpdate?> =
|
override suspend fun checkForUpdate(currentVersion: String): Result<AppUpdate?> =
|
||||||
withContext(ioDispatcher) {
|
withContext(ioDispatcher) {
|
||||||
Timber.i("Checking for update")
|
Timber.i("Checking for update")
|
||||||
|
val isNightly = BuildConfig.VERSION_NAME.contains("nightly")
|
||||||
val release =
|
val release =
|
||||||
if (BuildConfig.VERSION_NAME.contains("nightly")) {
|
if (isNightly) {
|
||||||
gitHubApi.getNightlyRelease(githubOwner, githubRepo)
|
gitHubApi.getNightlyRelease(githubOwner, githubRepo).onFailure(Timber::e)
|
||||||
} else {
|
} else {
|
||||||
gitHubApi.getLatestRelease(githubOwner, githubRepo)
|
gitHubApi.getLatestRelease(githubOwner, githubRepo).onFailure(Timber::e)
|
||||||
}
|
}
|
||||||
release.map { release ->
|
release.map { release ->
|
||||||
val apkAsset =
|
val apkAsset =
|
||||||
release.assets.find { asset ->
|
release.assets.find { asset ->
|
||||||
asset.name.startsWith("wgtunnel-full-v") && asset.name.endsWith(".apk")
|
asset.name.startsWith("wgtunnel-${Constants.STANDALONE_FLAVOR}-v") &&
|
||||||
|
asset.name.endsWith(".apk")
|
||||||
}
|
}
|
||||||
val newVersion =
|
val newVersion =
|
||||||
apkAsset?.name?.removePrefix("wgtunnel-full-v")?.removeSuffix(".apk")
|
apkAsset
|
||||||
?: return@map null
|
?.name
|
||||||
|
?.removePrefix("wgtunnel-${Constants.STANDALONE_FLAVOR}-v")
|
||||||
|
?.removeSuffix(".apk") ?: return@map null
|
||||||
|
|
||||||
Timber.i("Latest version: $newVersion, current version: $currentVersion")
|
Timber.i("Latest version: $newVersion, current version: $currentVersion")
|
||||||
|
if (isNightly && newVersion != currentVersion)
|
||||||
|
return@map GitHubReleaseMapper.toAppUpdate(release, newVersion)
|
||||||
if (NumberUtils.compareVersions(newVersion, currentVersion) > 0) {
|
if (NumberUtils.compareVersions(newVersion, currentVersion) > 0) {
|
||||||
release.toAppUpdate()
|
GitHubReleaseMapper.toAppUpdate(release, newVersion)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -1,9 +1,10 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.dao.SettingsDao
|
import com.zaneschepke.wireguardautotunnel.data.dao.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.mapper.SettingsMapper
|
||||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppSettingRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppSettingRepository
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
@@ -16,15 +17,15 @@ class RoomSettingsRepository(
|
|||||||
) : AppSettingRepository {
|
) : AppSettingRepository {
|
||||||
|
|
||||||
override suspend fun save(appSettings: AppSettings) {
|
override suspend fun save(appSettings: AppSettings) {
|
||||||
withContext(ioDispatcher) { settingsDoa.save(Settings.from(appSettings)) }
|
withContext(ioDispatcher) { settingsDoa.save(SettingsMapper.toSettings(appSettings)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val flow =
|
override val flow =
|
||||||
settingsDoa.getSettingsFlow().flowOn(ioDispatcher).map { it.toAppSettings() }
|
settingsDoa.getSettingsFlow().flowOn(ioDispatcher).map(SettingsMapper::toAppSettings)
|
||||||
|
|
||||||
override suspend fun get(): AppSettings {
|
override suspend fun get(): AppSettings {
|
||||||
return withContext(ioDispatcher) {
|
return withContext(ioDispatcher) {
|
||||||
(settingsDoa.getAll().firstOrNull() ?: Settings()).toAppSettings()
|
SettingsMapper.toAppSettings(settingsDoa.getAll().firstOrNull() ?: Settings())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-13
@@ -1,9 +1,9 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.dao.TunnelConfigDao
|
import com.zaneschepke.wireguardautotunnel.data.dao.TunnelConfigDao
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.mapper.TunnelConfigMapper
|
||||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
|
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
@@ -17,19 +17,25 @@ class RoomTunnelRepository(
|
|||||||
) : TunnelRepository {
|
) : TunnelRepository {
|
||||||
|
|
||||||
override val flow =
|
override val flow =
|
||||||
tunnelConfigDao.getAllFlow().flowOn(ioDispatcher).map { it.map { it.toTunnel() } }
|
tunnelConfigDao.getAllFlow().flowOn(ioDispatcher).map {
|
||||||
|
it.map(TunnelConfigMapper::toTunnelConf)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getAll(): Tunnels {
|
override suspend fun getAll(): Tunnels {
|
||||||
return withContext(ioDispatcher) { tunnelConfigDao.getAll().map { it.toTunnel() } }
|
return withContext(ioDispatcher) {
|
||||||
|
tunnelConfigDao.getAll().map(TunnelConfigMapper::toTunnelConf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun save(tunnelConf: TunnelConf) {
|
override suspend fun save(tunnelConf: TunnelConf) {
|
||||||
withContext(ioDispatcher) { tunnelConfigDao.save(TunnelConfig.from(tunnelConf)) }
|
withContext(ioDispatcher) {
|
||||||
|
tunnelConfigDao.save(TunnelConfigMapper.toTunnelConfig(tunnelConf))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun saveAll(tunnelConfList: List<TunnelConf>) {
|
override suspend fun saveAll(tunnelConfList: List<TunnelConf>) {
|
||||||
withContext(ioDispatcher) {
|
withContext(ioDispatcher) {
|
||||||
tunnelConfigDao.saveAll(tunnelConfList.map(TunnelConfig::from))
|
tunnelConfigDao.saveAll(tunnelConfList.map(TunnelConfigMapper::toTunnelConfig))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,15 +61,21 @@ class RoomTunnelRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun delete(tunnelConf: TunnelConf) {
|
override suspend fun delete(tunnelConf: TunnelConf) {
|
||||||
withContext(ioDispatcher) { tunnelConfigDao.delete(TunnelConfig.from(tunnelConf)) }
|
withContext(ioDispatcher) {
|
||||||
|
tunnelConfigDao.delete(TunnelConfigMapper.toTunnelConfig(tunnelConf))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getById(id: Int): TunnelConf? {
|
override suspend fun getById(id: Int): TunnelConf? {
|
||||||
return withContext(ioDispatcher) { tunnelConfigDao.getById(id.toLong())?.toTunnel() }
|
return withContext(ioDispatcher) {
|
||||||
|
tunnelConfigDao.getById(id.toLong())?.let(TunnelConfigMapper::toTunnelConf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getActive(): Tunnels {
|
override suspend fun getActive(): Tunnels {
|
||||||
return withContext(ioDispatcher) { tunnelConfigDao.getActive().map { it.toTunnel() } }
|
return withContext(ioDispatcher) {
|
||||||
|
tunnelConfigDao.getActive().map(TunnelConfigMapper::toTunnelConf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun count(): Int {
|
override suspend fun count(): Int {
|
||||||
@@ -71,22 +83,26 @@ class RoomTunnelRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByTunnelName(name: String): TunnelConf? {
|
override suspend fun findByTunnelName(name: String): TunnelConf? {
|
||||||
return withContext(ioDispatcher) { tunnelConfigDao.getByName(name)?.toTunnel() }
|
return withContext(ioDispatcher) {
|
||||||
|
tunnelConfigDao.getByName(name)?.let(TunnelConfigMapper::toTunnelConf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByTunnelNetworksName(name: String): Tunnels {
|
override suspend fun findByTunnelNetworksName(name: String): Tunnels {
|
||||||
return withContext(ioDispatcher) {
|
return withContext(ioDispatcher) {
|
||||||
tunnelConfigDao.findByTunnelNetworkName(name).map { it.toTunnel() }
|
tunnelConfigDao.findByTunnelNetworkName(name).map(TunnelConfigMapper::toTunnelConf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByMobileDataTunnel(): Tunnels {
|
override suspend fun findByMobileDataTunnel(): Tunnels {
|
||||||
return withContext(ioDispatcher) {
|
return withContext(ioDispatcher) {
|
||||||
tunnelConfigDao.findByMobileDataTunnel().map { it.toTunnel() }
|
tunnelConfigDao.findByMobileDataTunnel().map(TunnelConfigMapper::toTunnelConf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findPrimary(): Tunnels {
|
override suspend fun findPrimary(): Tunnels {
|
||||||
return withContext(ioDispatcher) { tunnelConfigDao.findByPrimary().map { it.toTunnel() } }
|
return withContext(ioDispatcher) {
|
||||||
|
tunnelConfigDao.findByPrimary().map(TunnelConfigMapper::toTunnelConf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,9 +113,11 @@ class TunnelModule {
|
|||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
settingsRepository: AppSettingRepository,
|
settingsRepository: AppSettingRepository,
|
||||||
): NetworkMonitor {
|
): NetworkMonitor {
|
||||||
return AndroidNetworkMonitor(context) {
|
val method = runBlocking { settingsRepository.get().wifiDetectionMethod }
|
||||||
runBlocking { settingsRepository.get().isWifiNameByShellEnabled }
|
return AndroidNetworkMonitor(
|
||||||
}
|
context,
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.fromValue(method.value),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.events
|
package com.zaneschepke.wireguardautotunnel.domain.events
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
|
|
||||||
sealed class AutoTunnelEvent {
|
sealed class AutoTunnelEvent {
|
||||||
data class Start(val tunnelConf: TunnelConf? = null) : AutoTunnelEvent()
|
data class Start(val tunnelConf: TunnelConf? = null) : AutoTunnelEvent()
|
||||||
|
|||||||
+19
-2
@@ -1,4 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.entity
|
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||||
|
|
||||||
|
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||||
|
|
||||||
data class AppSettings(
|
data class AppSettings(
|
||||||
val id: Int = 0,
|
val id: Int = 0,
|
||||||
@@ -15,15 +17,30 @@ data class AppSettings(
|
|||||||
val isPingEnabled: Boolean = false,
|
val isPingEnabled: Boolean = false,
|
||||||
val isAmneziaEnabled: Boolean = false,
|
val isAmneziaEnabled: Boolean = false,
|
||||||
val isWildcardsEnabled: Boolean = false,
|
val isWildcardsEnabled: Boolean = false,
|
||||||
val isWifiNameByShellEnabled: Boolean = false,
|
|
||||||
val isStopOnNoInternetEnabled: Boolean = false,
|
val isStopOnNoInternetEnabled: Boolean = false,
|
||||||
val isVpnKillSwitchEnabled: Boolean = false,
|
val isVpnKillSwitchEnabled: Boolean = false,
|
||||||
val isKernelKillSwitchEnabled: Boolean = false,
|
val isKernelKillSwitchEnabled: Boolean = false,
|
||||||
val isLanOnKillSwitchEnabled: Boolean = false,
|
val isLanOnKillSwitchEnabled: Boolean = false,
|
||||||
val debounceDelaySeconds: Int = 3,
|
val debounceDelaySeconds: Int = 3,
|
||||||
val isDisableKillSwitchOnTrustedEnabled: Boolean = false,
|
val isDisableKillSwitchOnTrustedEnabled: Boolean = false,
|
||||||
|
val isTunnelOnUnsecureEnabled: Boolean = false,
|
||||||
|
val splitTunnelApps: List<String> = emptyList(),
|
||||||
|
val wifiDetectionMethod: AndroidNetworkMonitor.WifiDetectionMethod =
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.DEFAULT,
|
||||||
) {
|
) {
|
||||||
fun debounceDelayMillis(): Long {
|
fun debounceDelayMillis(): Long {
|
||||||
return debounceDelaySeconds * 1000L
|
return debounceDelaySeconds * 1000L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toAutoTunnelStateString(): String {
|
||||||
|
return """
|
||||||
|
TunnelOnWifi: $isTunnelOnWifiEnabled
|
||||||
|
TunnelOnMobileData: $isTunnelOnMobileDataEnabled
|
||||||
|
TunnelOnEthernet: $isTunnelOnEthernetEnabled
|
||||||
|
Wildcards: $isWildcardsEnabled
|
||||||
|
StopOnNoInternet: $isStopOnNoInternetEnabled
|
||||||
|
Trusted Networks: $trustedNetworkSSIDs
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.entity
|
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.entity
|
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||||
|
|
||||||
data class AppUpdate(
|
data class AppUpdate(
|
||||||
val version: String,
|
val version: String,
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.entity
|
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||||
|
|
||||||
import com.wireguard.android.backend.Tunnel
|
import com.wireguard.android.backend.Tunnel
|
||||||
import com.wireguard.config.Config
|
import com.wireguard.config.Config
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
|
|
||||||
interface AppDataRepository {
|
interface AppDataRepository {
|
||||||
suspend fun getPrimaryOrFirstTunnel(): TunnelConf?
|
suspend fun getPrimaryOrFirstTunnel(): TunnelConf?
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface AppSettingRepository {
|
interface AppSettingRepository {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
|
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
interface UpdateRepository {
|
interface UpdateRepository {
|
||||||
|
|||||||
+4
-2
@@ -3,10 +3,10 @@ package com.zaneschepke.wireguardautotunnel.domain.state
|
|||||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.allDown
|
import com.zaneschepke.wireguardautotunnel.core.tunnel.allDown
|
||||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.hasActive
|
import com.zaneschepke.wireguardautotunnel.core.tunnel.hasActive
|
||||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.isUp
|
import com.zaneschepke.wireguardautotunnel.core.tunnel.isUp
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent
|
import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.events.KillSwitchEvent
|
import com.zaneschepke.wireguardautotunnel.domain.events.KillSwitchEvent
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isMatchingToWildcardList
|
import com.zaneschepke.wireguardautotunnel.util.extensions.isMatchingToWildcardList
|
||||||
|
|
||||||
data class AutoTunnelState(
|
data class AutoTunnelState(
|
||||||
@@ -16,6 +16,7 @@ data class AutoTunnelState(
|
|||||||
val tunnels: List<TunnelConf> = emptyList(),
|
val tunnels: List<TunnelConf> = emptyList(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
// also need to check for Wi-Fi state as there is some overlap when they are both connected
|
||||||
private fun isMobileDataActive(): Boolean {
|
private fun isMobileDataActive(): Boolean {
|
||||||
return !networkState.isEthernetConnected &&
|
return !networkState.isEthernetConnected &&
|
||||||
!networkState.isWifiConnected &&
|
!networkState.isWifiConnected &&
|
||||||
@@ -50,6 +51,7 @@ data class AutoTunnelState(
|
|||||||
return getTunnelWithMatchingTunnelNetwork() ?: tunnels.firstOrNull { it.isPrimaryTunnel }
|
return getTunnelWithMatchingTunnelNetwork() ?: tunnels.firstOrNull { it.isPrimaryTunnel }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore cellular state as there is overlap where it may still be active, but not prioritized
|
||||||
private fun isWifiActive(): Boolean {
|
private fun isWifiActive(): Boolean {
|
||||||
return !networkState.isEthernetConnected && networkState.isWifiConnected
|
return !networkState.isEthernetConnected && networkState.isWifiConnected
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ sealed class Route {
|
|||||||
|
|
||||||
@Serializable data object AutoTunnelAdvanced : Route()
|
@Serializable data object AutoTunnelAdvanced : Route()
|
||||||
|
|
||||||
|
@Serializable data object WifiDetectionMethod : Route()
|
||||||
|
|
||||||
@Serializable data object LocationDisclosure : Route()
|
@Serializable data object LocationDisclosure : Route()
|
||||||
|
|
||||||
@Serializable data object Appearance : Route()
|
@Serializable data object Appearance : Route()
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ fun IconSurfaceButton(
|
|||||||
else MaterialTheme.colorScheme.onSurface,
|
else MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column {
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
Text(title, style = MaterialTheme.typography.titleMedium)
|
Text(title, style = MaterialTheme.typography.titleMedium)
|
||||||
description?.let {
|
description?.let {
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
+8
@@ -188,6 +188,14 @@ fun currentNavBackStackEntryAsNavBarState(
|
|||||||
route = Route.Display,
|
route = Route.Display,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
backStackEntry.isCurrentRoute(Route.WifiDetectionMethod::class) ->
|
||||||
|
NavBarState(
|
||||||
|
showTop = true,
|
||||||
|
showBottom = true,
|
||||||
|
topTitle = { Text(stringResource(R.string.wifi_detection_method)) },
|
||||||
|
route = Route.WifiDetectionMethod,
|
||||||
|
)
|
||||||
|
|
||||||
backStackEntry.isCurrentRoute(Route.KillSwitch::class) ->
|
backStackEntry.isCurrentRoute(Route.KillSwitch::class) ->
|
||||||
NavBarState(
|
NavBarState(
|
||||||
showTop = true,
|
showTop = true,
|
||||||
|
|||||||
+39
-41
@@ -1,18 +1,9 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components
|
package com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Code
|
import androidx.compose.material.icons.outlined.*
|
||||||
import androidx.compose.material.icons.outlined.Filter1
|
|
||||||
import androidx.compose.material.icons.outlined.Security
|
|
||||||
import androidx.compose.material.icons.outlined.VpnKeyOff
|
|
||||||
import androidx.compose.material.icons.outlined.Wifi
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -26,14 +17,19 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||||
import com.zaneschepke.networkmonitor.NetworkStatus
|
import com.zaneschepke.networkmonitor.NetworkStatus
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberClipboardHelper
|
import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberClipboardHelper
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalNavController
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.LearnMoreLinkLabel
|
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.LearnMoreLinkLabel
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.state.AppUiState
|
import com.zaneschepke.wireguardautotunnel.ui.state.AppUiState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
|
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.extensions.asString
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
|
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
|
||||||
@@ -47,6 +43,7 @@ fun WifiTunnelingItems(
|
|||||||
isWifiNameReadable: () -> Boolean,
|
isWifiNameReadable: () -> Boolean,
|
||||||
): List<SelectionItem> {
|
): List<SelectionItem> {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val navController = LocalNavController.current
|
||||||
val clipboardHelper = rememberClipboardHelper()
|
val clipboardHelper = rememberClipboardHelper()
|
||||||
|
|
||||||
val baseItems =
|
val baseItems =
|
||||||
@@ -107,40 +104,40 @@ fun WifiTunnelingItems(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick = { viewModel.handleEvent(AppEvent.ToggleAutoTunnelOnWifi) },
|
onClick = { viewModel.handleEvent(AppEvent.ToggleAutoTunnelOnWifi) },
|
||||||
),
|
)
|
||||||
SelectionItem(
|
|
||||||
leadingIcon = Icons.Outlined.Code,
|
|
||||||
title = {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.wifi_name_via_shell),
|
|
||||||
style =
|
|
||||||
MaterialTheme.typography.bodyMedium.copy(
|
|
||||||
MaterialTheme.colorScheme.onSurface
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
description = {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.use_root_shell_for_wifi),
|
|
||||||
style =
|
|
||||||
MaterialTheme.typography.bodySmall.copy(
|
|
||||||
MaterialTheme.colorScheme.outline
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
trailing = {
|
|
||||||
ScaledSwitch(
|
|
||||||
checked = uiState.appSettings.isWifiNameByShellEnabled,
|
|
||||||
onClick = { viewModel.handleEvent(AppEvent.ToggleRootShellWifi) },
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onClick = { viewModel.handleEvent(AppEvent.ToggleRootShellWifi) },
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return if (uiState.appSettings.isTunnelOnWifiEnabled) {
|
return if (uiState.appSettings.isTunnelOnWifiEnabled) {
|
||||||
baseItems +
|
baseItems +
|
||||||
listOf(
|
listOf(
|
||||||
|
SelectionItem(
|
||||||
|
leadingIcon = Icons.Outlined.WifiFind,
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.wifi_detection_method),
|
||||||
|
style =
|
||||||
|
MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
MaterialTheme.colorScheme.onSurface
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
description = {
|
||||||
|
Text(
|
||||||
|
stringResource(
|
||||||
|
R.string.current_template,
|
||||||
|
uiState.appSettings.wifiDetectionMethod.asString(context),
|
||||||
|
),
|
||||||
|
style =
|
||||||
|
MaterialTheme.typography.bodySmall.copy(
|
||||||
|
MaterialTheme.colorScheme.outline
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
trailing = {
|
||||||
|
ForwardButton { navController.navigate(Route.WifiDetectionMethod) }
|
||||||
|
},
|
||||||
|
onClick = { navController.navigate(Route.WifiDetectionMethod) },
|
||||||
|
),
|
||||||
SelectionItem(
|
SelectionItem(
|
||||||
leadingIcon = Icons.Outlined.Filter1,
|
leadingIcon = Icons.Outlined.Filter1,
|
||||||
title = {
|
title = {
|
||||||
@@ -205,7 +202,8 @@ fun WifiTunnelingItems(
|
|||||||
currentText = currentText,
|
currentText = currentText,
|
||||||
onSave = { ssid ->
|
onSave = { ssid ->
|
||||||
if (
|
if (
|
||||||
uiState.appSettings.isWifiNameByShellEnabled ||
|
uiState.appSettings.wifiDetectionMethod ==
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.ROOT ||
|
||||||
isWifiNameReadable()
|
isWifiNameReadable()
|
||||||
) {
|
) {
|
||||||
viewModel.handleEvent(AppEvent.SaveTrustedSSID(ssid))
|
viewModel.handleEvent(AppEvent.SaveTrustedSSID(ssid))
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.detection
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.IconSurfaceButton
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.state.AppUiState
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.extensions.asDescriptionString
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.extensions.asString
|
||||||
|
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||||
|
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun WifiDetectionMethodScreen(uiState: AppUiState, viewModel: AppViewModel) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.Start,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.Top),
|
||||||
|
modifier = Modifier.fillMaxSize().padding(top = 24.dp).padding(horizontal = 24.dp),
|
||||||
|
) {
|
||||||
|
enumValues<AndroidNetworkMonitor.WifiDetectionMethod>().forEach {
|
||||||
|
val title = it.asString(context)
|
||||||
|
val description = it.asDescriptionString(context)
|
||||||
|
// TODO skip shizuku for now
|
||||||
|
if (it == AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU) return@forEach
|
||||||
|
IconSurfaceButton(
|
||||||
|
title = title,
|
||||||
|
onClick = { viewModel.handleEvent(AppEvent.SetDetectionMethod(it)) },
|
||||||
|
selected = uiState.appSettings.wifiDetectionMethod == it,
|
||||||
|
description = description,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,7 +67,9 @@ fun MainScreen(appUiState: AppUiState, appViewState: AppViewState, viewModel: Ap
|
|||||||
)
|
)
|
||||||
return@rememberLauncherForActivityResult
|
return@rememberLauncherForActivityResult
|
||||||
}
|
}
|
||||||
scanLauncher.launch(ScanOptions().setDesiredBarcodeFormats(ScanOptions.QR_CODE).setBeepEnabled(false))
|
scanLauncher.launch(
|
||||||
|
ScanOptions().setDesiredBarcodeFormats(ScanOptions.QR_CODE).setBeepEnabled(false)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appViewState.showModal == AppViewState.ModalType.DELETE) {
|
if (appViewState.showModal == AppViewState.ModalType.DELETE) {
|
||||||
|
|||||||
+2
-2
@@ -15,8 +15,8 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.components.EthernetTunnelItem
|
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.components.EthernetTunnelItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.components.MobileDataTunnelItem
|
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.components.MobileDataTunnelItem
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||||
|
|||||||
+2
-2
@@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.TrustedNetworkTextBox
|
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.TrustedNetworkTextBox
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.WildcardsLabel
|
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.WildcardsLabel
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ import androidx.compose.ui.input.pointer.pointerInput
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.getValueById
|
import com.zaneschepke.wireguardautotunnel.core.tunnel.getValueById
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
|
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.ExpandingRowListItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.ExpandingRowListItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||||
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.toThreeDecimalPlaceString
|
import com.zaneschepke.wireguardautotunnel.util.extensions.toThreeDecimalPlaceString
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.zaneschepke.wireguardautotunnel.MainActivity
|
import com.zaneschepke.wireguardautotunnel.MainActivity
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
|
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.components.AddPeerButton
|
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.components.AddPeerButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.components.InterfaceSection
|
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.components.InterfaceSection
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.state.ConfigUiState
|
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.state.ConfigUiState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.state.ConfigProxy
|
import com.zaneschepke.wireguardautotunnel.ui.state.ConfigProxy
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ import android.content.Context
|
|||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.state.SplitOption
|
import com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.state.SplitOption
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.state
|
package com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.state
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
|
|
||||||
data class SplitTunnelUiState(
|
data class SplitTunnelUiState(
|
||||||
val loading: Boolean = true,
|
val loading: Boolean = true,
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
|
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ 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.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.config.SubmitConfigurationTextBox
|
import com.zaneschepke.wireguardautotunnel.ui.common.config.SubmitConfigurationTextBox
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||||
|
|||||||
+1
-1
@@ -35,8 +35,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.zaneschepke.wireguardautotunnel.MainActivity
|
import com.zaneschepke.wireguardautotunnel.MainActivity
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.setScreenBrightness
|
import com.zaneschepke.wireguardautotunnel.util.extensions.setScreenBrightness
|
||||||
import io.github.alexzhirkevich.qrose.options.QrBallShape
|
import io.github.alexzhirkevich.qrose.options.QrBallShape
|
||||||
import io.github.alexzhirkevich.qrose.options.QrBrush
|
import io.github.alexzhirkevich.qrose.options.QrBrush
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||||
|
|||||||
+41
-2
@@ -12,6 +12,12 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
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.LinkAnnotation
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.TextLinkStyles
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.text.withLink
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
@@ -72,8 +78,41 @@ fun SupportScreen(viewModel: SupportViewModel = hiltViewModel(), appViewModel: A
|
|||||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Text(uiState.appUpdate?.version ?: "")
|
val annotatedString = buildAnnotatedString {
|
||||||
Text(uiState.appUpdate?.releaseNotes ?: "")
|
append("${uiState.appUpdate?.version ?: ""}\n")
|
||||||
|
// Add clickable text for second line
|
||||||
|
withLink(
|
||||||
|
link =
|
||||||
|
LinkAnnotation.Clickable(
|
||||||
|
tag = stringResource(id = R.string.release_notes),
|
||||||
|
linkInteractionListener = {
|
||||||
|
val version =
|
||||||
|
if (BuildConfig.VERSION_NAME.contains("nightly")) {
|
||||||
|
"nightly"
|
||||||
|
} else {
|
||||||
|
uiState.appUpdate
|
||||||
|
?.version
|
||||||
|
?.removePrefix("v")
|
||||||
|
?.trim() ?: ""
|
||||||
|
}
|
||||||
|
val url = "${Constants.BASE_RELEASE_URL}$version".trim()
|
||||||
|
context.openWebUrl(url)
|
||||||
|
},
|
||||||
|
styles =
|
||||||
|
TextLinkStyles(
|
||||||
|
style =
|
||||||
|
SpanStyle(
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
textDecoration = TextDecoration.Underline,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
append(stringResource(R.string.release_notes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(text = annotatedString)
|
||||||
if (uiState.isLoading) {
|
if (uiState.isLoading) {
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
progress = { uiState.downloadProgress },
|
progress = { uiState.downloadProgress },
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.screens.support
|
package com.zaneschepke.wireguardautotunnel.ui.screens.support
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||||
|
|
||||||
data class SupportUiState(
|
data class SupportUiState(
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.UpdateRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.UpdateRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||||
|
|
||||||
import com.zaneschepke.networkmonitor.NetworkStatus
|
import com.zaneschepke.networkmonitor.NetworkStatus
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.GeneralState
|
import com.zaneschepke.wireguardautotunnel.data.entity.GeneralState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
import com.zaneschepke.wireguardautotunnel.data.mapper.GeneralStateMapper
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
|
|
||||||
data class AppUiState(
|
data class AppUiState(
|
||||||
val appSettings: AppSettings = AppSettings(),
|
val appSettings: AppSettings = AppSettings(),
|
||||||
val tunnels: List<TunnelConf> = emptyList(),
|
val tunnels: List<TunnelConf> = emptyList(),
|
||||||
val activeTunnels: Map<TunnelConf, TunnelState> = emptyMap(),
|
val activeTunnels: Map<TunnelConf, TunnelState> = emptyMap(),
|
||||||
val appState: AppState = GeneralState().toAppState(),
|
val appState: AppState = GeneralStateMapper.toAppState(GeneralState()),
|
||||||
val isAutoTunnelActive: Boolean = false,
|
val isAutoTunnelActive: Boolean = false,
|
||||||
val appConfigurationChange: Boolean = false,
|
val appConfigurationChange: Boolean = false,
|
||||||
val isAppLoaded: Boolean = false,
|
val isAppLoaded: Boolean = false,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||||
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||||
|
|
||||||
data class AppViewState(
|
data class AppViewState(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||||
|
|
||||||
import com.wireguard.config.Peer
|
import com.wireguard.config.Peer
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.joinAndTrim
|
import com.zaneschepke.wireguardautotunnel.util.extensions.joinAndTrim
|
||||||
|
|
||||||
data class PeerProxy(
|
data class PeerProxy(
|
||||||
|
|||||||
@@ -38,4 +38,6 @@ object Constants {
|
|||||||
const val GOOGLE_PLAY_FLAVOR = "google"
|
const val GOOGLE_PLAY_FLAVOR = "google"
|
||||||
const val STANDALONE_FLAVOR = "standalone"
|
const val STANDALONE_FLAVOR = "standalone"
|
||||||
const val RELEASE = "release"
|
const val RELEASE = "release"
|
||||||
|
|
||||||
|
const val BASE_RELEASE_URL = "https://github.com/wgtunnel/wgtunnel/releases/tag/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import android.provider.OpenableColumns
|
|||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.getInputStreamFromUri
|
import com.zaneschepke.wireguardautotunnel.util.extensions.getInputStreamFromUri
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.installApk
|
import com.zaneschepke.wireguardautotunnel.util.extensions.installApk
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.launchShareFile
|
import com.zaneschepke.wireguardautotunnel.util.extensions.launchShareFile
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
package com.zaneschepke.wireguardautotunnel.util.extensions
|
||||||
|
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
package com.zaneschepke.wireguardautotunnel.util.extensions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||||
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.isCurrentRoute
|
import com.zaneschepke.wireguardautotunnel.ui.navigation.isCurrentRoute
|
||||||
|
|
||||||
@@ -11,3 +14,25 @@ fun NavController.goFromRoot(route: Route) {
|
|||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun AndroidNetworkMonitor.WifiDetectionMethod.asString(context: Context): String {
|
||||||
|
return when (this) {
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.DEFAULT -> context.getString(R.string._default)
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.LEGACY -> context.getString(R.string.legacy)
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.ROOT -> context.getString(R.string.root)
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU -> context.getString(R.string.shizuku)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AndroidNetworkMonitor.WifiDetectionMethod.asDescriptionString(context: Context): String? {
|
||||||
|
return when (this) {
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.LEGACY ->
|
||||||
|
context.getString(R.string.legacy_api_description)
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.ROOT ->
|
||||||
|
context.getString(R.string.use_root_shell_for_wifi)
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU ->
|
||||||
|
context.getString(R.string.use_shell_via_shizuku)
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.DEFAULT ->
|
||||||
|
context.getString(R.string.use_android_recommended)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.wireguard.android.backend.WgQuickBackend
|
|||||||
import com.wireguard.android.util.RootShell
|
import com.wireguard.android.util.RootShell
|
||||||
import com.zaneschepke.logcatter.LogReader
|
import com.zaneschepke.logcatter.LogReader
|
||||||
import com.zaneschepke.logcatter.model.LogMessage
|
import com.zaneschepke.logcatter.model.LogMessage
|
||||||
|
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||||
import com.zaneschepke.networkmonitor.NetworkMonitor
|
import com.zaneschepke.networkmonitor.NetworkMonitor
|
||||||
import com.zaneschepke.networkmonitor.NetworkStatus
|
import com.zaneschepke.networkmonitor.NetworkStatus
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
@@ -18,11 +19,11 @@ import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
|||||||
import com.zaneschepke.wireguardautotunnel.di.AppShell
|
import com.zaneschepke.wireguardautotunnel.di.AppShell
|
||||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.di.MainDispatcher
|
import com.zaneschepke.wireguardautotunnel.di.MainDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.state.AppUiState
|
import com.zaneschepke.wireguardautotunnel.ui.state.AppUiState
|
||||||
@@ -177,7 +178,6 @@ constructor(
|
|||||||
handleDeleteTrustedSSID(event.ssid, state.appSettings)
|
handleDeleteTrustedSSID(event.ssid, state.appSettings)
|
||||||
AppEvent.ToggleAutoTunnelWildcards ->
|
AppEvent.ToggleAutoTunnelWildcards ->
|
||||||
handleToggleAutoTunnelWildcards(state.appSettings)
|
handleToggleAutoTunnelWildcards(state.appSettings)
|
||||||
AppEvent.ToggleRootShellWifi -> handleToggleRootShellWifi(state.appSettings)
|
|
||||||
is AppEvent.SaveTrustedSSID ->
|
is AppEvent.SaveTrustedSSID ->
|
||||||
handleSaveTrustedSSID(event.ssid, state.appSettings)
|
handleSaveTrustedSSID(event.ssid, state.appSettings)
|
||||||
AppEvent.ToggleAutoTunnelOnEthernet ->
|
AppEvent.ToggleAutoTunnelOnEthernet ->
|
||||||
@@ -210,10 +210,31 @@ constructor(
|
|||||||
AppEvent.ClearSelectedTunnels -> clearSelectedTunnels()
|
AppEvent.ClearSelectedTunnels -> clearSelectedTunnels()
|
||||||
is AppEvent.SetShowModal ->
|
is AppEvent.SetShowModal ->
|
||||||
_appViewState.update { it.copy(showModal = event.modalType) }
|
_appViewState.update { it.copy(showModal = event.modalType) }
|
||||||
|
|
||||||
|
is AppEvent.SetDetectionMethod ->
|
||||||
|
handleSetDetectionMethod(event.detectionMethod, state.appSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun handleSetDetectionMethod(
|
||||||
|
detectionMethod: AndroidNetworkMonitor.WifiDetectionMethod,
|
||||||
|
appSettings: AppSettings,
|
||||||
|
) {
|
||||||
|
if (detectionMethod == appSettings.wifiDetectionMethod) return
|
||||||
|
when (detectionMethod) {
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.ROOT -> {
|
||||||
|
val allowed = requestRoot()
|
||||||
|
if (!allowed) return
|
||||||
|
}
|
||||||
|
// TODO check if shizuku available
|
||||||
|
AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU -> Unit
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
saveSettings(appSettings.copy(wifiDetectionMethod = detectionMethod))
|
||||||
|
handleShowMessage(StringValue.StringResource(R.string.app_restart_required))
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleToggleSelectAllTunnels(tunnels: List<TunnelConf>) =
|
private fun handleToggleSelectAllTunnels(tunnels: List<TunnelConf>) =
|
||||||
_appViewState.update { it ->
|
_appViewState.update { it ->
|
||||||
val remove = tunnels.size == it.selectedTunnels.size
|
val remove = tunnels.size == it.selectedTunnels.size
|
||||||
@@ -264,6 +285,7 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
private fun handleTunnelErrors() =
|
private fun handleTunnelErrors() =
|
||||||
viewModelScope.launch { tunnelManager.errorEvents.collect { errorEvent -> } }
|
viewModelScope.launch { tunnelManager.errorEvents.collect { errorEvent -> } }
|
||||||
|
|
||||||
@@ -630,14 +652,6 @@ constructor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private suspend fun handleToggleRootShellWifi(appSettings: AppSettings) {
|
|
||||||
if (requestRoot()) {
|
|
||||||
saveSettings(
|
|
||||||
appSettings.copy(isWifiNameByShellEnabled = !appSettings.isWifiNameByShellEnabled)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun handleToggleTunnelOnEthernet(appSettings: AppSettings) =
|
private suspend fun handleToggleTunnelOnEthernet(appSettings: AppSettings) =
|
||||||
saveSettings(
|
saveSettings(
|
||||||
appSettings.copy(isTunnelOnEthernetEnabled = !appSettings.isTunnelOnEthernetEnabled)
|
appSettings.copy(isTunnelOnEthernetEnabled = !appSettings.isTunnelOnEthernetEnabled)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.viewmodel.event
|
package com.zaneschepke.wireguardautotunnel.viewmodel.event
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.state.AppViewState
|
import com.zaneschepke.wireguardautotunnel.ui.state.AppViewState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||||
@@ -78,6 +79,9 @@ sealed class AppEvent {
|
|||||||
|
|
||||||
data class SetTheme(val theme: Theme) : AppEvent()
|
data class SetTheme(val theme: Theme) : AppEvent()
|
||||||
|
|
||||||
|
data class SetDetectionMethod(val detectionMethod: AndroidNetworkMonitor.WifiDetectionMethod) :
|
||||||
|
AppEvent()
|
||||||
|
|
||||||
data object ToggleAutoTunnelOnWifi : AppEvent()
|
data object ToggleAutoTunnelOnWifi : AppEvent()
|
||||||
|
|
||||||
data object ToggleAutoTunnelOnCellular : AppEvent()
|
data object ToggleAutoTunnelOnCellular : AppEvent()
|
||||||
@@ -90,8 +94,6 @@ sealed class AppEvent {
|
|||||||
|
|
||||||
data object ToggleAutoTunnelWildcards : AppEvent()
|
data object ToggleAutoTunnelWildcards : AppEvent()
|
||||||
|
|
||||||
data object ToggleRootShellWifi : AppEvent()
|
|
||||||
|
|
||||||
data class DeleteTrustedSSID(val ssid: String) : AppEvent()
|
data class DeleteTrustedSSID(val ssid: String) : AppEvent()
|
||||||
|
|
||||||
data class SaveTrustedSSID(val ssid: String) : AppEvent()
|
data class SaveTrustedSSID(val ssid: String) : AppEvent()
|
||||||
|
|||||||
@@ -152,7 +152,7 @@
|
|||||||
<string name="learn_more">Learn more</string>
|
<string name="learn_more">Learn more</string>
|
||||||
<string name="wildcards_active">Wildcards active</string>
|
<string name="wildcards_active">Wildcards active</string>
|
||||||
<string name="wifi_name_via_shell">Wifi name via shell</string>
|
<string name="wifi_name_via_shell">Wifi name via shell</string>
|
||||||
<string name="use_root_shell_for_wifi">Use root shell to get wifi name</string>
|
<string name="use_root_shell_for_wifi">Use a root shell to get Wi-Fi information, preventing the need for location permissions</string>
|
||||||
<string name="kernel_not_supported">Kernel not supported</string>
|
<string name="kernel_not_supported">Kernel not supported</string>
|
||||||
<string name="start_auto">Start auto-tunnel</string>
|
<string name="start_auto">Start auto-tunnel</string>
|
||||||
<string name="stop_auto">Stop auto-tunnel</string>
|
<string name="stop_auto">Stop auto-tunnel</string>
|
||||||
@@ -222,6 +222,7 @@
|
|||||||
<string name="remote_key_template">Key: %1$s</string>
|
<string name="remote_key_template">Key: %1$s</string>
|
||||||
<string name="version_template">Version: %1$s</string>
|
<string name="version_template">Version: %1$s</string>
|
||||||
<string name="security_template">Security: %1$s</string>
|
<string name="security_template">Security: %1$s</string>
|
||||||
|
<string name="current_template">Current: %1$s</string>
|
||||||
<string name="flavor_template">Flavor: %1$s</string>
|
<string name="flavor_template">Flavor: %1$s</string>
|
||||||
<string name="config_error">config error</string>
|
<string name="config_error">config error</string>
|
||||||
<string name="dns_resolve_error">dns resolution error</string>
|
<string name="dns_resolve_error">dns resolution error</string>
|
||||||
@@ -255,11 +256,28 @@
|
|||||||
<string name="install_updated_permission">This app needs permission to install updates.</string>
|
<string name="install_updated_permission">This app needs permission to install updates.</string>
|
||||||
<string name="allow">Allow</string>
|
<string name="allow">Allow</string>
|
||||||
<string name="licenses">Licenses</string>
|
<string name="licenses">Licenses</string>
|
||||||
<string name="update_check_unsupported">Update check not supported this build type.</string>
|
<string name="update_check_unsupported">Update check is not supported for this build type.</string>
|
||||||
<string name="darker">Darker</string>
|
<string name="darker">Darker</string>
|
||||||
<string name="amoled">AMOLED</string>
|
<string name="amoled">AMOLED</string>
|
||||||
<string name="show_qr">Show QR</string>
|
<string name="show_qr">Show QR</string>
|
||||||
<string name="amnezia">Amnezia</string>
|
<string name="amnezia">Amnezia</string>
|
||||||
<string name="wireguard">WireGuard</string>
|
<string name="wireguard">WireGuard</string>
|
||||||
<string name="done">Done</string>
|
<string name="done">Done</string>
|
||||||
|
<string name="use_deprecated_wifi_api">Use deprecated Wi-Fi API</string>
|
||||||
|
<string name="app_restart_required">App restart required</string>
|
||||||
|
<string name="wifi_detection_method">Wi-Fi detection method</string>
|
||||||
|
<string name="_default">Default</string>
|
||||||
|
<string name="legacy">Legacy</string>
|
||||||
|
<string name="root">Root</string>
|
||||||
|
<string name="shizuku">Shizuku</string>
|
||||||
|
<string name="legacy_api_description">Use an Android 12 deprecated method for getting Wi-Fi information that limits
|
||||||
|
location queries, but may not work reliably on newer devices
|
||||||
|
</string>
|
||||||
|
<string name="use_shell_via_shizuku">Use shell via Shizuku to get Wi-Fi information, preventing the need for
|
||||||
|
location permission on non-rooted devices
|
||||||
|
</string>
|
||||||
|
<string name="use_android_recommended">Use Android\'s recommended method for getting Wi-Fi information, based on
|
||||||
|
Android version
|
||||||
|
</string>
|
||||||
|
<string name="release_notes">Release notes</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
object Constants {
|
object Constants {
|
||||||
const val VERSION_NAME = "3.9.2"
|
const val VERSION_NAME = "3.9.3"
|
||||||
const val JVM_TARGET = "17"
|
const val JVM_TARGET = "17"
|
||||||
const val VERSION_CODE = 39200
|
const val VERSION_CODE = 39300
|
||||||
const val TARGET_SDK = 35
|
const val TARGET_SDK = 35
|
||||||
const val MIN_SDK = 26
|
const val MIN_SDK = 26
|
||||||
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
||||||
@@ -13,5 +13,5 @@ object Constants {
|
|||||||
const val PRERELEASE = "prerelease"
|
const val PRERELEASE = "prerelease"
|
||||||
|
|
||||||
val allowedLicenses = listOf("MIT", "Apache-2.0", "BSD-3-Clause")
|
val allowedLicenses = listOf("MIT", "Apache-2.0", "BSD-3-Clause")
|
||||||
const val XZING_LICENSE_URL: String = "https://github.com/journeyapps/zxing-android-embedded/blob/master/COPYING"
|
val allowedLicenseUrls = listOf("https://github.com/journeyapps/zxing-android-embedded/blob/master/COPYING")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
What's new:
|
||||||
|
- Fixes deprecate location API bug by adding Wi-Fi info method selection
|
||||||
|
- Simplified update check dialog UI
|
||||||
|
- Improve auto-tunnel reliability with delayed recheck
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
Features
|
||||||
|
|
||||||
|
- Add tunnels via .conf file, zip, manual entry, or QR code
|
||||||
|
- Auto connect to VPN based on Wi-Fi SSID, ethernet, or mobile data
|
||||||
|
- Split tunneling by application with search
|
||||||
|
- WireGuard support for kernel and userspace modes
|
||||||
|
- Amnezia support for userspace mode for DPI/censorship protection
|
||||||
|
- Always-On VPN support
|
||||||
|
- Export Amnezia and WireGuard tunnels to zip
|
||||||
|
- Quick tile support for VPN toggling
|
||||||
|
- Static shortcuts support for primary tunnel for automation integration
|
||||||
|
- Intent automation support for all tunnels
|
||||||
|
- Automatic service restart after reboot
|
||||||
|
- Battery preservation measures
|
||||||
@@ -1 +0,0 @@
|
|||||||
Um cliente de VPN alternativo para WireGuard com recursos adicionais
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
WG Tunnel
|
|
||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=2g -XX:+HeapDumpOnOutOfMemoryError
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
|||||||
+30
-27
@@ -3,7 +3,7 @@ accompanist = "0.37.3"
|
|||||||
activityCompose = "1.10.1"
|
activityCompose = "1.10.1"
|
||||||
amneziawgAndroid = "1.4.0"
|
amneziawgAndroid = "1.4.0"
|
||||||
androidx-junit = "1.2.1"
|
androidx-junit = "1.2.1"
|
||||||
appcompat = "1.7.0"
|
appcompat = "1.7.1"
|
||||||
biometricKtx = "1.2.0-alpha05"
|
biometricKtx = "1.2.0-alpha05"
|
||||||
coreKtx = "1.16.0"
|
coreKtx = "1.16.0"
|
||||||
datastorePreferences = "1.2.0-alpha02"
|
datastorePreferences = "1.2.0-alpha02"
|
||||||
@@ -14,7 +14,7 @@ hiltCompiler = "1.2.0"
|
|||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
kotlinx-serialization-json = "1.8.1"
|
kotlinx-serialization-json = "1.8.1"
|
||||||
ktorClientCore = "3.1.3"
|
ktorClientCore = "3.1.3"
|
||||||
lifecycle-runtime-compose = "2.9.0"
|
lifecycle-runtime-compose = "2.9.1"
|
||||||
material3 = "1.3.2"
|
material3 = "1.3.2"
|
||||||
navigationCompose = "2.9.0"
|
navigationCompose = "2.9.0"
|
||||||
pinLockCompose = "1.0.4"
|
pinLockCompose = "1.0.4"
|
||||||
@@ -24,11 +24,11 @@ semver4j = "3.1.0"
|
|||||||
slf4jAndroid = "1.7.36"
|
slf4jAndroid = "1.7.36"
|
||||||
timber = "5.0.1"
|
timber = "5.0.1"
|
||||||
tunnel = "1.3.0"
|
tunnel = "1.3.0"
|
||||||
androidGradlePlugin = "8.9.3"
|
androidGradlePlugin = "8.10.1"
|
||||||
kotlin = "2.1.21"
|
kotlin = "2.1.21"
|
||||||
ksp = "2.1.21-2.0.1"
|
ksp = "2.2.0-2.0.2"
|
||||||
composeBom = "2025.05.00"
|
composeBom = "2025.06.00"
|
||||||
compose = "1.8.1"
|
compose = "1.8.2"
|
||||||
icons = "1.7.8"
|
icons = "1.7.8"
|
||||||
workRuntimeKtxVersion = "2.10.1"
|
workRuntimeKtxVersion = "2.10.1"
|
||||||
zxingAndroidEmbedded = "4.3.0"
|
zxingAndroidEmbedded = "4.3.0"
|
||||||
@@ -48,19 +48,15 @@ licensee = "1.13.0"
|
|||||||
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist" }
|
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist" }
|
||||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
|
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
|
||||||
|
|
||||||
#room
|
# db
|
||||||
amneziawg-android = { module = "com.zaneschepke:amneziawg-android", version.ref = "amneziawgAndroid" }
|
|
||||||
androidx-biometric-ktx = { module = "androidx.biometric:biometric-ktx", version.ref = "biometricKtx" }
|
|
||||||
androidx-core = { module = "androidx.core:core", version.ref = "coreKtx" }
|
androidx-core = { module = "androidx.core:core", version.ref = "coreKtx" }
|
||||||
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
||||||
androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "hiltCompiler" }
|
|
||||||
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle-runtime-compose" }
|
|
||||||
androidx-lifecycle-service = { module = "androidx.lifecycle:lifecycle-service", version.ref = "lifecycle-runtime-compose" }
|
|
||||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomVersion" }
|
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomVersion" }
|
||||||
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomVersion" }
|
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomVersion" }
|
||||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomVersion" }
|
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomVersion" }
|
||||||
|
androidx-storage = { group = "androidx.test.services", name = "storage", version.ref = "storage" }
|
||||||
|
|
||||||
#compose
|
# ui
|
||||||
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
|
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
|
||||||
androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" }
|
androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" }
|
||||||
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
|
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
|
||||||
@@ -68,15 +64,7 @@ androidx-compose-manifest = { module = "androidx.compose.ui:ui-test-manifest", v
|
|||||||
androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics", version.ref = "compose" }
|
androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics", version.ref = "compose" }
|
||||||
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
|
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
|
||||||
androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
|
androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
|
||||||
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
#hilt
|
|
||||||
androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "roomVersion" }
|
|
||||||
androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtxVersion" }
|
|
||||||
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
|
|
||||||
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
|
|
||||||
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }
|
|
||||||
androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltCompiler" }
|
|
||||||
|
|
||||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
|
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
|
||||||
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||||
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
|
||||||
@@ -84,10 +72,22 @@ androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", vers
|
|||||||
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltCompiler" }
|
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltCompiler" }
|
||||||
androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" }
|
androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" }
|
||||||
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
|
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
|
||||||
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-compose" }
|
|
||||||
androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
|
androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
|
||||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||||
|
|
||||||
|
# lifecyle
|
||||||
|
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle-runtime-compose" }
|
||||||
|
androidx-lifecycle-service = { module = "androidx.lifecycle:lifecycle-service", version.ref = "lifecycle-runtime-compose" }
|
||||||
|
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-compose" }
|
||||||
|
|
||||||
|
# di
|
||||||
|
androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "roomVersion" }
|
||||||
|
androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtxVersion" }
|
||||||
|
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
|
||||||
|
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }
|
||||||
|
androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltCompiler" }
|
||||||
|
androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "hiltCompiler" }
|
||||||
|
|
||||||
junit = { module = "junit:junit", version.ref = "junit" }
|
junit = { module = "junit:junit", version.ref = "junit" }
|
||||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
|
||||||
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientCore" }
|
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientCore" }
|
||||||
@@ -98,16 +98,19 @@ ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx
|
|||||||
lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle-runtime-compose" }
|
lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle-runtime-compose" }
|
||||||
material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "icons" }
|
material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "icons" }
|
||||||
|
|
||||||
|
# util
|
||||||
pin-lock-compose = { module = "com.zaneschepke:pin_lock_compose", version.ref = "pinLockCompose" }
|
pin-lock-compose = { module = "com.zaneschepke:pin_lock_compose", version.ref = "pinLockCompose" }
|
||||||
qrose = { module = "io.github.alexzhirkevich:qrose", version.ref = "qrose" }
|
qrose = { module = "io.github.alexzhirkevich:qrose", version.ref = "qrose" }
|
||||||
semver4j = { module = "com.vdurmont:semver4j", version.ref = "semver4j" }
|
semver4j = { module = "com.vdurmont:semver4j", version.ref = "semver4j" }
|
||||||
slf4j-android = { module = "org.slf4j:slf4j-android", version.ref = "slf4jAndroid" }
|
slf4j-android = { module = "org.slf4j:slf4j-android", version.ref = "slf4jAndroid" }
|
||||||
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
||||||
tunnel = { module = "com.zaneschepke:wireguard-android", version.ref = "tunnel" }
|
|
||||||
|
|
||||||
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
|
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
|
||||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
|
||||||
androidx-storage = { group = "androidx.test.services", name = "storage", version.ref = "storage" }
|
androidx-biometric-ktx = { module = "androidx.biometric:biometric-ktx", version.ref = "biometricKtx" }
|
||||||
|
|
||||||
|
# tunnel
|
||||||
|
tunnel = { module = "com.zaneschepke:wireguard-android", version.ref = "tunnel" }
|
||||||
|
amneziawg-android = { module = "com.zaneschepke:amneziawg-android", version.ref = "amneziawgAndroid" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
||||||
|
|||||||
+113
-58
@@ -12,7 +12,6 @@ import android.net.NetworkRequest
|
|||||||
import android.net.wifi.WifiManager
|
import android.net.wifi.WifiManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import com.wireguard.android.util.RootShell
|
import com.wireguard.android.util.RootShell
|
||||||
import java.util.Collections
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
@@ -21,19 +20,31 @@ import kotlinx.coroutines.flow.callbackFlow
|
|||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class AndroidNetworkMonitor(
|
class AndroidNetworkMonitor(context: Context, val wifiDetectionMethod: WifiDetectionMethod) :
|
||||||
context: Context,
|
NetworkMonitor {
|
||||||
private val useRootShellCallback: suspend () -> Boolean,
|
|
||||||
) : NetworkMonitor {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val LOCATION_GRANTED = "LOCATION_PERMISSIONS_GRANTED"
|
const val LOCATION_GRANTED = "LOCATION_PERMISSIONS_GRANTED"
|
||||||
const val LOCATION_SERVICES_FILTER = "android.location.PROVIDERS_CHANGED"
|
const val LOCATION_SERVICES_FILTER = "android.location.PROVIDERS_CHANGED"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class WifiDetectionMethod(val value: Int) {
|
||||||
|
DEFAULT(0),
|
||||||
|
LEGACY(1),
|
||||||
|
ROOT(2),
|
||||||
|
SHIZUKU(3);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromValue(value: Int): WifiDetectionMethod =
|
||||||
|
WifiDetectionMethod.entries.find { it.value == value } ?: DEFAULT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val appContext = context.applicationContext
|
private val appContext = context.applicationContext
|
||||||
private val packageName = appContext.packageName
|
private val packageName = appContext.packageName
|
||||||
private val connectivityManager =
|
private val connectivityManager =
|
||||||
@@ -43,15 +54,17 @@ class AndroidNetworkMonitor(
|
|||||||
appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
private val rootShell = RootShell(context)
|
private val rootShell = RootShell(context)
|
||||||
|
|
||||||
|
private val wifiMutex = Mutex()
|
||||||
|
|
||||||
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
|
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
|
|
||||||
@get:Synchronized @set:Synchronized var currentSsid: String? = null
|
private var currentSsid: String? = null
|
||||||
@get:Synchronized @set:Synchronized var securityType: WifiSecurityType? = null
|
private var securityType: WifiSecurityType? = null
|
||||||
|
|
||||||
@get:Synchronized @set:Synchronized var wifiConnected = false
|
private var wifiConnected = false
|
||||||
|
|
||||||
// Track active Wi-Fi networks and last active network ID
|
// Track active Wi-Fi networks and last active network ID
|
||||||
private val activeNetworks = Collections.synchronizedSet(mutableSetOf<String>())
|
private val activeWifiNetworks = mutableSetOf<String>()
|
||||||
|
|
||||||
data class WifiState(
|
data class WifiState(
|
||||||
val connected: Boolean = false,
|
val connected: Boolean = false,
|
||||||
@@ -65,30 +78,28 @@ class AndroidNetworkMonitor(
|
|||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
suspend fun getWifiSsid(): String? {
|
suspend fun getWifiSsid(): String? {
|
||||||
return withContext(ioDispatcher) {
|
return withContext(ioDispatcher) {
|
||||||
if (useRootShellCallback()) {
|
if (wifiManager == null) return@withContext null
|
||||||
rootShell.getCurrentWifiName()
|
try {
|
||||||
} else {
|
wifiManager.connectionInfo?.ssid?.trim('"')?.takeIf { it.isNotEmpty() }
|
||||||
if (wifiManager == null) return@withContext null
|
} catch (e: Exception) {
|
||||||
try {
|
Timber.e(e)
|
||||||
wifiManager.connectionInfo?.ssid?.trim('"')?.takeIf { it.isNotEmpty() }
|
null
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun handleUnknownWifi() {
|
suspend fun handleUnknownWifi() {
|
||||||
val newSsid = getWifiSsid()
|
wifiMutex.withLock {
|
||||||
val securityType = wifiManager?.getCurrentSecurityType()
|
val newSsid = getWifiSsid()
|
||||||
// Only update if new SSID is valid; preserve existing valid SSID otherwise
|
val securityType = wifiManager?.getCurrentSecurityType()
|
||||||
if (newSsid != null && newSsid != WifiManager.UNKNOWN_SSID) {
|
// Only update if new SSID is valid; preserve existing valid SSID otherwise
|
||||||
currentSsid = newSsid
|
if (newSsid != null && newSsid != WifiManager.UNKNOWN_SSID) {
|
||||||
trySend(WifiState(wifiConnected, currentSsid, securityType))
|
currentSsid = newSsid
|
||||||
} else if (currentSsid == null || currentSsid == WifiManager.UNKNOWN_SSID) {
|
trySend(WifiState(wifiConnected, currentSsid, securityType))
|
||||||
currentSsid = newSsid
|
} else if (currentSsid == null || currentSsid == WifiManager.UNKNOWN_SSID) {
|
||||||
trySend(WifiState(wifiConnected, currentSsid, securityType))
|
currentSsid = newSsid
|
||||||
|
trySend(WifiState(wifiConnected, currentSsid, securityType))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,39 +155,83 @@ class AndroidNetworkMonitor(
|
|||||||
flags,
|
flags,
|
||||||
)
|
)
|
||||||
|
|
||||||
val callback =
|
suspend fun handleOnWifiLost(network: Network) {
|
||||||
object : ConnectivityManager.NetworkCallback() {
|
wifiMutex.withLock {
|
||||||
override fun onAvailable(network: Network) {
|
Timber.d("Wi-Fi onLost: network=$network")
|
||||||
Timber.d("Wi-Fi onAvailable: network=$network")
|
activeWifiNetworks.remove(network.toString())
|
||||||
activeNetworks.add(network.toString())
|
if (activeWifiNetworks.isEmpty()) {
|
||||||
launch {
|
Timber.d(
|
||||||
currentSsid = getWifiSsid()
|
"All Wi-Fi networks disconnected, clearing currentSsid and wifiConnected"
|
||||||
securityType = wifiManager?.getCurrentSecurityType()
|
)
|
||||||
wifiConnected = true
|
currentSsid = null
|
||||||
trySend(
|
wifiConnected = false
|
||||||
WifiState(
|
trySend(WifiState(connected = false, ssid = null, securityType = null))
|
||||||
connected = true,
|
} else {
|
||||||
ssid = currentSsid,
|
Timber.d("Wi-Fi onLost, but still connected to other networks, ignoring")
|
||||||
securityType = securityType,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onLost(network: Network) {
|
suspend fun handleOnWifiAvailable(
|
||||||
Timber.d("Wi-Fi onLost: network=$network")
|
network: Network,
|
||||||
activeNetworks.remove(network.toString())
|
networkCapabilities: NetworkCapabilities?,
|
||||||
if (activeNetworks.isEmpty()) {
|
) {
|
||||||
Timber.d(
|
wifiMutex.withLock {
|
||||||
"All Wi-Fi networks disconnected, clearing currentSsid and wifiConnected"
|
Timber.d("Wi-Fi onAvailable: network=$network")
|
||||||
)
|
activeWifiNetworks.add(network.toString())
|
||||||
currentSsid = null
|
currentSsid =
|
||||||
wifiConnected = false
|
when (wifiDetectionMethod) {
|
||||||
trySend(WifiState(connected = false, ssid = null, securityType = null))
|
WifiDetectionMethod.DEFAULT -> {
|
||||||
} else {
|
if (networkCapabilities == null) {
|
||||||
Timber.d("Wi-Fi onLost, but still connected to other networks, ignoring")
|
Timber.d("Capabilities not available, getting SSID via legacy API")
|
||||||
|
getWifiSsid()
|
||||||
|
} else
|
||||||
|
networkCapabilities.getWifiSsid().also {
|
||||||
|
Timber.d("Got SSID from capabilities")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WifiDetectionMethod.LEGACY -> getWifiSsid()
|
||||||
|
WifiDetectionMethod.ROOT -> rootShell.getCurrentWifiName()
|
||||||
|
// TODO implement Shizuku
|
||||||
|
else -> networkCapabilities?.getWifiSsid() ?: getWifiSsid()
|
||||||
|
}
|
||||||
|
securityType = wifiManager?.getCurrentSecurityType()
|
||||||
|
wifiConnected = true
|
||||||
|
trySend(
|
||||||
|
WifiState(connected = true, ssid = currentSsid, securityType = securityType)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val callback =
|
||||||
|
when {
|
||||||
|
wifiDetectionMethod == WifiDetectionMethod.LEGACY ||
|
||||||
|
Build.VERSION.SDK_INT < Build.VERSION_CODES.S ->
|
||||||
|
object : ConnectivityManager.NetworkCallback() {
|
||||||
|
override fun onAvailable(network: Network) {
|
||||||
|
Timber.i("Wi-Fi change detected using old API")
|
||||||
|
launch { handleOnWifiAvailable(network, null) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
launch { handleOnWifiLost(network) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else ->
|
||||||
|
object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
|
||||||
|
|
||||||
|
override fun onCapabilitiesChanged(
|
||||||
|
network: Network,
|
||||||
|
networkCapabilities: NetworkCapabilities,
|
||||||
|
) {
|
||||||
|
launch { handleOnWifiAvailable(network, networkCapabilities) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
launch { handleOnWifiLost(network) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val request =
|
val request =
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.zaneschepke.networkmonitor
|
package com.zaneschepke.networkmonitor
|
||||||
|
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import android.net.wifi.WifiInfo
|
||||||
import android.net.wifi.WifiManager
|
import android.net.wifi.WifiManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import com.wireguard.android.util.RootShell
|
import com.wireguard.android.util.RootShell
|
||||||
@@ -21,3 +23,14 @@ fun WifiManager.getCurrentSecurityType(): WifiSecurityType? {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun NetworkCapabilities.getWifiSsid(): String? {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
val info: WifiInfo
|
||||||
|
if (transportInfo is WifiInfo) {
|
||||||
|
info = transportInfo as WifiInfo
|
||||||
|
return info.ssid.removeSurrounding("\"").trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user