mirror of
https://github.com/wgtunnel/android.git
synced 2026-06-02 00:29:08 +02:00
feat!: dual-stack kill switch support, metered tunnels
Adds dual-stack option for kill switch. Add metered option for kill switch and individual tunnels. closes #966 closes #962
This commit is contained in:
@@ -0,0 +1,509 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 26,
|
||||
"identityHash": "a420594a08fff58ecda3e0424fb43e47",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "tunnel_config",
|
||||
"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, `restart_on_ping_failure` INTEGER NOT NULL DEFAULT false, `ping_target` TEXT DEFAULT null, `is_ethernet_tunnel` INTEGER NOT NULL DEFAULT false, `is_ipv4_preferred` INTEGER NOT NULL DEFAULT true, `position` INTEGER NOT NULL DEFAULT 0, `auto_tunnel_apps` TEXT NOT NULL DEFAULT '[]')",
|
||||
"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": "restartOnPingFailure",
|
||||
"columnName": "restart_on_ping_failure",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingTarget",
|
||||
"columnName": "ping_target",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "autoTunnelApps",
|
||||
"columnName": "auto_tunnel_apps",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "'[]'"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_tunnel_config_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_tunnel_config_name` ON `${TABLE_NAME}` (`name`)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "proxy_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `socks5_proxy_enabled` INTEGER NOT NULL DEFAULT 0, `socks5_proxy_bind_address` TEXT, `http_proxy_enable` INTEGER NOT NULL DEFAULT 0, `http_proxy_bind_address` TEXT, `proxy_username` TEXT, `proxy_password` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "socks5ProxyEnabled",
|
||||
"columnName": "socks5_proxy_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "socks5ProxyBindAddress",
|
||||
"columnName": "socks5_proxy_bind_address",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "httpProxyEnabled",
|
||||
"columnName": "http_proxy_enable",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "httpProxyBindAddress",
|
||||
"columnName": "http_proxy_bind_address",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "proxyUsername",
|
||||
"columnName": "proxy_username",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "proxyPassword",
|
||||
"columnName": "proxy_password",
|
||||
"affinity": "TEXT"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "general_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_shortcuts_enabled` INTEGER NOT NULL DEFAULT 0, `is_restore_on_boot_enabled` INTEGER NOT NULL DEFAULT 0, `is_multi_tunnel_enabled` INTEGER NOT NULL DEFAULT 0, `is_tunnel_globals_enabled` INTEGER NOT NULL DEFAULT 0, `app_mode` INTEGER NOT NULL DEFAULT 0, `theme` TEXT NOT NULL DEFAULT 'AUTOMATIC', `locale` TEXT, `remote_key` TEXT, `is_remote_control_enabled` INTEGER NOT NULL DEFAULT 0, `is_pin_lock_enabled` INTEGER NOT NULL DEFAULT 0, `is_always_on_vpn_enabled` INTEGER NOT NULL DEFAULT 0, `custom_split_packages` TEXT NOT NULL DEFAULT '{}')",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isShortcutsEnabled",
|
||||
"columnName": "is_shortcuts_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRestoreOnBootEnabled",
|
||||
"columnName": "is_restore_on_boot_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isMultiTunnelEnabled",
|
||||
"columnName": "is_multi_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelGlobalsEnabled",
|
||||
"columnName": "is_tunnel_globals_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "appMode",
|
||||
"columnName": "app_mode",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "theme",
|
||||
"columnName": "theme",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "'AUTOMATIC'"
|
||||
},
|
||||
{
|
||||
"fieldPath": "locale",
|
||||
"columnName": "locale",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteKey",
|
||||
"columnName": "remote_key",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRemoteControlEnabled",
|
||||
"columnName": "is_remote_control_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPinLockEnabled",
|
||||
"columnName": "is_pin_lock_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAlwaysOnVpnEnabled",
|
||||
"columnName": "is_always_on_vpn_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "customSplitPackages",
|
||||
"columnName": "custom_split_packages",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "'{}'"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "auto_tunnel_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_tunnel_enabled` INTEGER NOT NULL DEFAULT 0, `is_tunnel_on_mobile_data_enabled` INTEGER NOT NULL DEFAULT 0, `trusted_network_ssids` TEXT NOT NULL DEFAULT '', `is_tunnel_on_ethernet_enabled` INTEGER NOT NULL DEFAULT 0, `is_tunnel_on_wifi_enabled` INTEGER NOT NULL DEFAULT 0, `is_wildcards_enabled` INTEGER NOT NULL DEFAULT 0, `is_stop_on_no_internet_enabled` INTEGER NOT NULL DEFAULT 0, `debounce_delay_seconds` INTEGER NOT NULL DEFAULT 3, `is_tunnel_on_unsecure_enabled` INTEGER NOT NULL DEFAULT 0, `wifi_detection_method` INTEGER NOT NULL DEFAULT 0, `start_on_boot` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAutoTunnelEnabled",
|
||||
"columnName": "is_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnMobileDataEnabled",
|
||||
"columnName": "is_tunnel_on_mobile_data_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "trustedNetworkSSIDs",
|
||||
"columnName": "trusted_network_ssids",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnEthernetEnabled",
|
||||
"columnName": "is_tunnel_on_ethernet_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnWifiEnabled",
|
||||
"columnName": "is_tunnel_on_wifi_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isWildcardsEnabled",
|
||||
"columnName": "is_wildcards_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isStopOnNoInternetEnabled",
|
||||
"columnName": "is_stop_on_no_internet_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "debounceDelaySeconds",
|
||||
"columnName": "debounce_delay_seconds",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "3"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnUnsecureEnabled",
|
||||
"columnName": "is_tunnel_on_unsecure_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "wifiDetectionMethod",
|
||||
"columnName": "wifi_detection_method",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "startOnBoot",
|
||||
"columnName": "start_on_boot",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "monitoring_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_ping_enabled` INTEGER NOT NULL DEFAULT 0, `is_ping_monitoring_enabled` INTEGER NOT NULL DEFAULT 1, `tunnel_ping_interval_sec` INTEGER NOT NULL DEFAULT 30, `tunnel_ping_attempts` INTEGER NOT NULL DEFAULT 3, `tunnel_ping_timeout_sec` INTEGER, `show_detailed_ping_stats` INTEGER NOT NULL DEFAULT 0, `is_local_logs_enabled` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPingEnabled",
|
||||
"columnName": "is_ping_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPingMonitoringEnabled",
|
||||
"columnName": "is_ping_monitoring_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "tunnelPingIntervalSeconds",
|
||||
"columnName": "tunnel_ping_interval_sec",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "30"
|
||||
},
|
||||
{
|
||||
"fieldPath": "tunnelPingAttempts",
|
||||
"columnName": "tunnel_ping_attempts",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "3"
|
||||
},
|
||||
{
|
||||
"fieldPath": "tunnelPingTimeoutSeconds",
|
||||
"columnName": "tunnel_ping_timeout_sec",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "showDetailedPingStats",
|
||||
"columnName": "show_detailed_ping_stats",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isLocalLogsEnabled",
|
||||
"columnName": "is_local_logs_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "dns_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dns_protocol` INTEGER NOT NULL DEFAULT 0, `dns_endpoint` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dnsProtocol",
|
||||
"columnName": "dns_protocol",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "dnsEndpoint",
|
||||
"columnName": "dns_endpoint",
|
||||
"affinity": "TEXT"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "lockdown_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `bypass_lan` INTEGER NOT NULL DEFAULT 0, `metered` INTEGER NOT NULL DEFAULT 0, `dual_stack` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "bypassLan",
|
||||
"columnName": "bypass_lan",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "metered",
|
||||
"columnName": "metered",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "dualStack",
|
||||
"columnName": "dual_stack",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"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, 'a420594a08fff58ecda3e0424fb43e47')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 27,
|
||||
"identityHash": "98452d8160a1ae66c852ec8cd739e675",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "tunnel_config",
|
||||
"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, `restart_on_ping_failure` INTEGER NOT NULL DEFAULT false, `ping_target` TEXT DEFAULT null, `is_ethernet_tunnel` INTEGER NOT NULL DEFAULT false, `is_ipv4_preferred` INTEGER NOT NULL DEFAULT true, `position` INTEGER NOT NULL DEFAULT 0, `auto_tunnel_apps` TEXT NOT NULL DEFAULT '[]', `is_metered` 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": "restartOnPingFailure",
|
||||
"columnName": "restart_on_ping_failure",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingTarget",
|
||||
"columnName": "ping_target",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "autoTunnelApps",
|
||||
"columnName": "auto_tunnel_apps",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "'[]'"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isMetered",
|
||||
"columnName": "is_metered",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "true"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_tunnel_config_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_tunnel_config_name` ON `${TABLE_NAME}` (`name`)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "proxy_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `socks5_proxy_enabled` INTEGER NOT NULL DEFAULT 0, `socks5_proxy_bind_address` TEXT, `http_proxy_enable` INTEGER NOT NULL DEFAULT 0, `http_proxy_bind_address` TEXT, `proxy_username` TEXT, `proxy_password` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "socks5ProxyEnabled",
|
||||
"columnName": "socks5_proxy_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "socks5ProxyBindAddress",
|
||||
"columnName": "socks5_proxy_bind_address",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "httpProxyEnabled",
|
||||
"columnName": "http_proxy_enable",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "httpProxyBindAddress",
|
||||
"columnName": "http_proxy_bind_address",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "proxyUsername",
|
||||
"columnName": "proxy_username",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "proxyPassword",
|
||||
"columnName": "proxy_password",
|
||||
"affinity": "TEXT"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "general_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_shortcuts_enabled` INTEGER NOT NULL DEFAULT 0, `is_restore_on_boot_enabled` INTEGER NOT NULL DEFAULT 0, `is_multi_tunnel_enabled` INTEGER NOT NULL DEFAULT 0, `global_split_tunnel_enabled` INTEGER NOT NULL DEFAULT 0, `app_mode` INTEGER NOT NULL DEFAULT 0, `theme` TEXT NOT NULL DEFAULT 'AUTOMATIC', `locale` TEXT, `remote_key` TEXT, `is_remote_control_enabled` INTEGER NOT NULL DEFAULT 0, `is_pin_lock_enabled` INTEGER NOT NULL DEFAULT 0, `is_always_on_vpn_enabled` INTEGER NOT NULL DEFAULT 0, `custom_split_packages` TEXT NOT NULL DEFAULT '{}')",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isShortcutsEnabled",
|
||||
"columnName": "is_shortcuts_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRestoreOnBootEnabled",
|
||||
"columnName": "is_restore_on_boot_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isMultiTunnelEnabled",
|
||||
"columnName": "is_multi_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isGlobalSplitTunnelEnabled",
|
||||
"columnName": "global_split_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "appMode",
|
||||
"columnName": "app_mode",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "theme",
|
||||
"columnName": "theme",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "'AUTOMATIC'"
|
||||
},
|
||||
{
|
||||
"fieldPath": "locale",
|
||||
"columnName": "locale",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteKey",
|
||||
"columnName": "remote_key",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRemoteControlEnabled",
|
||||
"columnName": "is_remote_control_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPinLockEnabled",
|
||||
"columnName": "is_pin_lock_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAlwaysOnVpnEnabled",
|
||||
"columnName": "is_always_on_vpn_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "customSplitPackages",
|
||||
"columnName": "custom_split_packages",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "'{}'"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "auto_tunnel_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_tunnel_enabled` INTEGER NOT NULL DEFAULT 0, `is_tunnel_on_mobile_data_enabled` INTEGER NOT NULL DEFAULT 0, `trusted_network_ssids` TEXT NOT NULL DEFAULT '', `is_tunnel_on_ethernet_enabled` INTEGER NOT NULL DEFAULT 0, `is_tunnel_on_wifi_enabled` INTEGER NOT NULL DEFAULT 0, `is_wildcards_enabled` INTEGER NOT NULL DEFAULT 0, `is_stop_on_no_internet_enabled` INTEGER NOT NULL DEFAULT 0, `debounce_delay_seconds` INTEGER NOT NULL DEFAULT 3, `is_tunnel_on_unsecure_enabled` INTEGER NOT NULL DEFAULT 0, `wifi_detection_method` INTEGER NOT NULL DEFAULT 0, `start_on_boot` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAutoTunnelEnabled",
|
||||
"columnName": "is_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnMobileDataEnabled",
|
||||
"columnName": "is_tunnel_on_mobile_data_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "trustedNetworkSSIDs",
|
||||
"columnName": "trusted_network_ssids",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnEthernetEnabled",
|
||||
"columnName": "is_tunnel_on_ethernet_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnWifiEnabled",
|
||||
"columnName": "is_tunnel_on_wifi_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isWildcardsEnabled",
|
||||
"columnName": "is_wildcards_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isStopOnNoInternetEnabled",
|
||||
"columnName": "is_stop_on_no_internet_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "debounceDelaySeconds",
|
||||
"columnName": "debounce_delay_seconds",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "3"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnUnsecureEnabled",
|
||||
"columnName": "is_tunnel_on_unsecure_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "wifiDetectionMethod",
|
||||
"columnName": "wifi_detection_method",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "startOnBoot",
|
||||
"columnName": "start_on_boot",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "monitoring_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_ping_enabled` INTEGER NOT NULL DEFAULT 0, `is_ping_monitoring_enabled` INTEGER NOT NULL DEFAULT 1, `tunnel_ping_interval_sec` INTEGER NOT NULL DEFAULT 30, `tunnel_ping_attempts` INTEGER NOT NULL DEFAULT 3, `tunnel_ping_timeout_sec` INTEGER, `show_detailed_ping_stats` INTEGER NOT NULL DEFAULT 0, `is_local_logs_enabled` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPingEnabled",
|
||||
"columnName": "is_ping_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPingMonitoringEnabled",
|
||||
"columnName": "is_ping_monitoring_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "tunnelPingIntervalSeconds",
|
||||
"columnName": "tunnel_ping_interval_sec",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "30"
|
||||
},
|
||||
{
|
||||
"fieldPath": "tunnelPingAttempts",
|
||||
"columnName": "tunnel_ping_attempts",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "3"
|
||||
},
|
||||
{
|
||||
"fieldPath": "tunnelPingTimeoutSeconds",
|
||||
"columnName": "tunnel_ping_timeout_sec",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "showDetailedPingStats",
|
||||
"columnName": "show_detailed_ping_stats",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isLocalLogsEnabled",
|
||||
"columnName": "is_local_logs_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "dns_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dns_protocol` INTEGER NOT NULL DEFAULT 0, `dns_endpoint` TEXT, `global_tunnel_dns_enabled` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dnsProtocol",
|
||||
"columnName": "dns_protocol",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "dnsEndpoint",
|
||||
"columnName": "dns_endpoint",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isGlobalTunnelDnsEnabled",
|
||||
"columnName": "global_tunnel_dns_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "lockdown_settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `bypass_lan` INTEGER NOT NULL DEFAULT 0, `metered` INTEGER NOT NULL DEFAULT 0, `dual_stack` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "bypassLan",
|
||||
"columnName": "bypass_lan",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "metered",
|
||||
"columnName": "metered",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "dualStack",
|
||||
"columnName": "dual_stack",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"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, '98452d8160a1ae66c852ec8cd739e675')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,8 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.Appear
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.display.DisplayScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.language.LanguageScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.dns.DnsSettingsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.globals.TunnelGlobalsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.integrations.AndroidIntegrationsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.lockdown.LockdownSettingsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.TunnelMonitoringScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.logs.LogsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.ping.PingTargetScreen
|
||||
@@ -75,9 +75,9 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.support.donate.crypto.Addr
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.license.LicenseScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.TunnelsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.config.ConfigScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.settings.TunnelSettingsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.sort.SortScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.splittunnel.SplitTunnelScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.tunneloptions.TunnelOptionsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.AlertRed
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.OffWhite
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme
|
||||
@@ -331,7 +331,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
entry<Route.Tunnels> { TunnelsScreen() }
|
||||
entry<Route.Sort> { SortScreen() }
|
||||
entry<Route.TunnelOptions> { key ->
|
||||
entry<Route.TunnelSettings> { key ->
|
||||
val viewModel =
|
||||
hiltViewModel<
|
||||
TunnelViewModel,
|
||||
@@ -341,7 +341,7 @@ class MainActivity : AppCompatActivity() {
|
||||
factory.create(key.id)
|
||||
}
|
||||
)
|
||||
TunnelOptionsScreen(viewModel)
|
||||
TunnelSettingsScreen(viewModel)
|
||||
}
|
||||
entry<Route.SplitTunnel> { key ->
|
||||
val viewModel =
|
||||
@@ -388,9 +388,6 @@ class MainActivity : AppCompatActivity() {
|
||||
AndroidIntegrationsScreen()
|
||||
}
|
||||
entry<Route.Dns> { DnsSettingsScreen() }
|
||||
entry<Route.TunnelGlobals> { key ->
|
||||
TunnelGlobalsScreen(key.id)
|
||||
}
|
||||
entry<Route.ConfigGlobal> { key ->
|
||||
val viewModel =
|
||||
hiltViewModel<
|
||||
@@ -415,6 +412,9 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
SplitTunnelScreen(viewModel)
|
||||
}
|
||||
entry<Route.LockdownSettings> {
|
||||
LockdownSettingsScreen()
|
||||
}
|
||||
entry<Route.ProxySettings> { ProxySettingsScreen() }
|
||||
entry<Route.Appearance> { AppearanceScreen() }
|
||||
entry<Route.Language> { LanguageScreen() }
|
||||
|
||||
@@ -7,18 +7,15 @@ import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import com.zaneschepke.logcatter.LogReader
|
||||
import com.zaneschepke.wireguardautotunnel.core.notification.NotificationMonitor
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||
import com.zaneschepke.wireguardautotunnel.core.worker.ServiceWorker
|
||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendMode
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.MonitoringSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.util.ReleaseTree
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
@@ -43,8 +40,6 @@ class WireGuardAutoTunnel : Application(), Configuration.Provider {
|
||||
|
||||
@Inject lateinit var notificationMonitor: NotificationMonitor
|
||||
|
||||
@Inject lateinit var tunnelManager: TunnelManager
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
@@ -73,12 +68,6 @@ class WireGuardAutoTunnel : Application(), Configuration.Provider {
|
||||
ServiceWorker.start(this)
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
applicationScope.cancel()
|
||||
tunnelManager.setBackendMode(BackendMode.Inactive)
|
||||
super.onTerminate()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val _uiActive = MutableStateFlow(false)
|
||||
|
||||
@@ -35,6 +35,7 @@ class KernelTunnel
|
||||
constructor(
|
||||
@ApplicationScope applicationScope: CoroutineScope,
|
||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
private val runConfigHelper: RunConfigHelper,
|
||||
@Kernel private val backend: Backend,
|
||||
) : BaseTunnel(applicationScope, ioDispatcher) {
|
||||
|
||||
@@ -53,7 +54,6 @@ constructor(
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
// TODO Add DNS settings
|
||||
override fun tunnelStateFlow(tunnelConfig: TunnelConfig): Flow<TunnelStatus> = callbackFlow {
|
||||
validateWireGuardInterfaceName(tunnelConfig.name).onFailure { close(it) }
|
||||
|
||||
@@ -69,7 +69,8 @@ constructor(
|
||||
try {
|
||||
withTimeout(STARTUP_TIMEOUT_MS) {
|
||||
updateTunnelStatus(tunnelConfig.id, TunnelStatus.Starting)
|
||||
backend.setState(runtimeTunnel, WgTunnel.State.UP, tunnelConfig.toWgConfig())
|
||||
val runConfig = runConfigHelper.buildWgRunConfig(tunnelConfig)
|
||||
backend.setState(runtimeTunnel, WgTunnel.State.UP, runConfig)
|
||||
}
|
||||
} catch (e: TimeoutCancellationException) {
|
||||
Timber.e("Startup timed out for ${tunnelConfig.name}")
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.AppMode
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.DnsProtocol
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.InvalidConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.DnsSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.GeneralSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.ProxySettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.DnsSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.ProxySettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import org.amnezia.awg.config.Config
|
||||
import org.amnezia.awg.config.proxy.HttpProxy
|
||||
import org.amnezia.awg.config.proxy.Socks5Proxy
|
||||
|
||||
class RunConfigHelper
|
||||
@Inject
|
||||
constructor(
|
||||
private val settingsRepository: GeneralSettingRepository,
|
||||
private val proxySettingsRepository: ProxySettingsRepository,
|
||||
private val dnsSettingsRepository: DnsSettingsRepository,
|
||||
private val tunnelsRepository: TunnelRepository,
|
||||
) {
|
||||
|
||||
private data class PrepResult(
|
||||
val effectiveConfig: TunnelConfig,
|
||||
val generalSettings: GeneralSettings,
|
||||
val dnsSettings: DnsSettings,
|
||||
)
|
||||
|
||||
private suspend fun prepare(tunnelConfig: TunnelConfig): PrepResult {
|
||||
val generalSettings = settingsRepository.getGeneralSettings()
|
||||
val dnsSettings = dnsSettingsRepository.getDnsSettings()
|
||||
val effectiveConfig =
|
||||
if (
|
||||
generalSettings.isGlobalSplitTunnelEnabled || dnsSettings.isGlobalTunnelDnsEnabled
|
||||
) {
|
||||
val globalConfig =
|
||||
tunnelsRepository.globalTunnelFlow.firstOrNull() ?: throw InvalidConfig()
|
||||
tunnelConfig.copyWithGlobalValues(
|
||||
globalConfig,
|
||||
dnsSettings.isGlobalTunnelDnsEnabled,
|
||||
generalSettings.isGlobalSplitTunnelEnabled,
|
||||
)
|
||||
} else {
|
||||
tunnelConfig
|
||||
}
|
||||
return PrepResult(effectiveConfig, generalSettings, dnsSettings)
|
||||
}
|
||||
|
||||
suspend fun buildAmRunConfig(tunnelConfig: TunnelConfig): Config {
|
||||
val prep = prepare(tunnelConfig)
|
||||
val proxies =
|
||||
if (prep.generalSettings.appMode == AppMode.PROXY) {
|
||||
val proxySettings = proxySettingsRepository.getProxySettings()
|
||||
buildList {
|
||||
if (proxySettings.socks5ProxyEnabled) {
|
||||
add(
|
||||
Socks5Proxy(
|
||||
proxySettings.socks5ProxyBindAddress
|
||||
?: ProxySettings.DEFAULT_SOCKS_BIND_ADDRESS,
|
||||
proxySettings.proxyUsername,
|
||||
proxySettings.proxyPassword,
|
||||
)
|
||||
)
|
||||
}
|
||||
if (proxySettings.httpProxyEnabled) {
|
||||
add(
|
||||
HttpProxy(
|
||||
proxySettings.httpProxyBindAddress
|
||||
?: ProxySettings.DEFAULT_HTTP_BIND_ADDRESS,
|
||||
proxySettings.proxyUsername,
|
||||
proxySettings.proxyPassword,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
val amConfig = prep.effectiveConfig.toAmConfig()
|
||||
return Config.Builder()
|
||||
.setInterface(amConfig.`interface`)
|
||||
.addPeers(amConfig.peers)
|
||||
.addProxies(proxies)
|
||||
.setDnsSettings(
|
||||
org.amnezia.awg.config.DnsSettings(
|
||||
prep.dnsSettings.dnsProtocol == DnsProtocol.DOH,
|
||||
Optional.ofNullable(prep.dnsSettings.dnsEndpoint),
|
||||
)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
suspend fun buildWgRunConfig(tunnelConfig: TunnelConfig): com.wireguard.config.Config {
|
||||
val prep = prepare(tunnelConfig)
|
||||
return prep.effectiveConfig.toWgConfig()
|
||||
}
|
||||
}
|
||||
@@ -16,4 +16,6 @@ class RuntimeAwgTunnel(
|
||||
}
|
||||
|
||||
override fun isIpv4ResolutionPreferred() = tunnelConfig.isIpv4Preferred
|
||||
|
||||
override fun isMetered() = tunnelConfig.isMetered
|
||||
}
|
||||
|
||||
+20
-30
@@ -1,7 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig as Entity
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.AppMode
|
||||
import com.zaneschepke.wireguardautotunnel.di.*
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendMode
|
||||
@@ -14,6 +13,7 @@ import com.zaneschepke.wireguardautotunnel.domain.model.GeneralSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AutoTunnelSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.LockdownSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.LogHealthState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.PingState
|
||||
@@ -40,6 +40,7 @@ constructor(
|
||||
private val serviceManager: ServiceManager,
|
||||
private val settingsRepository: GeneralSettingRepository,
|
||||
private val autoTunnelSettingsRepository: AutoTunnelSettingsRepository,
|
||||
private val lockdownSettingsRepository: LockdownSettingsRepository,
|
||||
private val tunnelsRepository: TunnelRepository,
|
||||
private val tunnelMonitor: TunnelMonitor,
|
||||
@ApplicationScope private val applicationScope: CoroutineScope,
|
||||
@@ -70,12 +71,6 @@ constructor(
|
||||
val condition: (SideEffectState) -> Boolean,
|
||||
)
|
||||
|
||||
private suspend fun getSettings(): GeneralSettings =
|
||||
settingsRepository.flow.filterNotNull().first { it != GeneralSettings() }
|
||||
|
||||
private suspend fun getTunnels(): List<TunnelConfig> =
|
||||
tunnelsRepository.flow.first { it.isNotEmpty() }
|
||||
|
||||
private val tunnelProviderFlow: StateFlow<TunnelProvider> = run {
|
||||
val currentBackend = AtomicReference(userspaceTunnel)
|
||||
val currentSettings = AtomicReference(GeneralSettings())
|
||||
@@ -85,10 +80,7 @@ constructor(
|
||||
.filterNotNull()
|
||||
// ignore default state
|
||||
.filterNot { it == GeneralSettings() }
|
||||
.distinctUntilChanged { old, new ->
|
||||
old.appMode == new.appMode &&
|
||||
old.isLanOnKillSwitchEnabled == new.isLanOnKillSwitchEnabled
|
||||
}
|
||||
.distinctUntilChangedBy { it.appMode }
|
||||
.map { settings ->
|
||||
Timber.d("App mode changes with ${settings.appMode}")
|
||||
val backend =
|
||||
@@ -109,7 +101,7 @@ constructor(
|
||||
handleModeChangeCleanup(previousBackend, previousSettings.appMode)
|
||||
}
|
||||
if (settings.appMode == AppMode.LOCK_DOWN) {
|
||||
handleLockDownModeInit(settings.isLanOnKillSwitchEnabled)
|
||||
handleLockDownModeInit()
|
||||
}
|
||||
}
|
||||
.map { (_, backend) -> backend }
|
||||
@@ -236,17 +228,7 @@ constructor(
|
||||
activeTunnels.first { it.isEmpty() }
|
||||
} ?: run { activeTunnels.value.keys.forEach { id -> provider.forceStopTunnel(id) } }
|
||||
}
|
||||
val runConfig =
|
||||
tunnelConfig.run {
|
||||
if (getSettings().isTunnelGlobalsEnabled) {
|
||||
val globalTunnel =
|
||||
getTunnels().firstOrNull { it.name == Entity.GLOBAL_CONFIG_NAME }
|
||||
?: return@run this
|
||||
return@run copyWithGlobalValues(globalTunnel)
|
||||
}
|
||||
this
|
||||
}
|
||||
tunnelProviderFlow.value.startTunnel(runConfig)
|
||||
tunnelProviderFlow.value.startTunnel(tunnelConfig)
|
||||
}
|
||||
|
||||
override suspend fun stopTunnel(tunnelId: Int) {
|
||||
@@ -303,11 +285,21 @@ constructor(
|
||||
serviceManager.updateTunnelTile()
|
||||
}
|
||||
|
||||
private fun handleLockDownModeInit(withLanBypass: Boolean) {
|
||||
val allowedIps = if (withLanBypass) TunnelConfig.IPV4_PUBLIC_NETWORKS else emptySet()
|
||||
// TODO this can crash if we haven't started foreground service yet, especially for
|
||||
// workerManager
|
||||
private suspend fun handleLockDownModeInit() {
|
||||
val lockdownSettings = lockdownSettingsRepository.getLockdownSettings()
|
||||
val allowedIps =
|
||||
if (lockdownSettings.bypassLan) TunnelConfig.IPV4_PUBLIC_NETWORKS else emptySet()
|
||||
try {
|
||||
if (serviceManager.hasVpnPermission()) {
|
||||
proxyUserspaceTunnel.setBackendMode(BackendMode.KillSwitch(allowedIps))
|
||||
proxyUserspaceTunnel.setBackendMode(
|
||||
BackendMode.KillSwitch(
|
||||
allowedIps,
|
||||
lockdownSettings.metered,
|
||||
lockdownSettings.dualStack,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
throw NotAuthorized()
|
||||
}
|
||||
@@ -350,8 +342,7 @@ constructor(
|
||||
AppMode.VPN,
|
||||
AppMode.PROXY,
|
||||
AppMode.LOCK_DOWN -> {
|
||||
if (mode == AppMode.LOCK_DOWN)
|
||||
handleLockDownModeInit(settings.isLanOnKillSwitchEnabled)
|
||||
if (mode == AppMode.LOCK_DOWN) handleLockDownModeInit()
|
||||
tunnels.firstOrNull { it.isActive }?.let { startTunnel(it) }
|
||||
}
|
||||
AppMode.KERNEL ->
|
||||
@@ -378,8 +369,7 @@ constructor(
|
||||
tunnelsRepository.resetActiveTunnels()
|
||||
if (isVpnAuthorized(settings.appMode)) {
|
||||
when (val mode = settings.appMode) {
|
||||
AppMode.LOCK_DOWN ->
|
||||
handleLockDownModeInit(settings.isLanOnKillSwitchEnabled)
|
||||
AppMode.LOCK_DOWN -> handleLockDownModeInit()
|
||||
AppMode.KERNEL,
|
||||
AppMode.VPN,
|
||||
AppMode.PROXY -> Unit
|
||||
|
||||
+29
-16
@@ -1,5 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||
|
||||
import android.os.PowerManager
|
||||
import com.zaneschepke.logcatter.LogReader
|
||||
import com.zaneschepke.networkmonitor.NetworkMonitor
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.AppMode
|
||||
@@ -31,6 +32,7 @@ constructor(
|
||||
private val networkMonitor: NetworkMonitor,
|
||||
private val networkUtils: NetworkUtils,
|
||||
private val logReader: LogReader,
|
||||
private val powerManager: PowerManager,
|
||||
) {
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
@@ -74,7 +76,7 @@ constructor(
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
.distinctUntilChangedBy { it.isHealthy } // Only emit when health changes
|
||||
.distinctUntilChangedBy { it.isHealthy }
|
||||
.collect { logHealthState ->
|
||||
Timber.d("Tunnel log health updated for ${tunnelConfig.name}: $logHealthState")
|
||||
updateTunnelStatus(tunnelConfig.id, null, null, null, logHealthState)
|
||||
@@ -199,6 +201,7 @@ constructor(
|
||||
}
|
||||
|
||||
val attemptTime = System.currentTimeMillis()
|
||||
val timeout = settings.tunnelPingTimeoutSeconds?.toMillis() ?: 5000L
|
||||
runCatching {
|
||||
withTimeout(
|
||||
settings.tunnelPingTimeoutSeconds?.toMillis() ?: 5000L
|
||||
@@ -270,20 +273,28 @@ constructor(
|
||||
|
||||
while (isActive) {
|
||||
ensureActive()
|
||||
if (isNetworkConnected.value) {
|
||||
performPing()
|
||||
} else {
|
||||
pingStatsFlow.update { current ->
|
||||
current.mapValues { entry ->
|
||||
entry.value.copy(
|
||||
isReachable = false,
|
||||
failureReason = FailureReason.NoConnectivity,
|
||||
lastPingAttemptMillis = System.currentTimeMillis(),
|
||||
)
|
||||
if (!powerManager.isDeviceIdleMode) {
|
||||
if (isNetworkConnected.value) {
|
||||
performPing()
|
||||
} else {
|
||||
pingStatsFlow.update { current ->
|
||||
current.mapValues { entry ->
|
||||
entry.value.copy(
|
||||
isReachable = false,
|
||||
failureReason = FailureReason.NoConnectivity,
|
||||
lastPingAttemptMillis = System.currentTimeMillis(),
|
||||
)
|
||||
}
|
||||
}
|
||||
ensureActive()
|
||||
updateTunnelStatus(
|
||||
tunnelConfig.id,
|
||||
null,
|
||||
null,
|
||||
pingStatsFlow.value,
|
||||
null,
|
||||
)
|
||||
}
|
||||
ensureActive()
|
||||
updateTunnelStatus(tunnelConfig.id, null, null, pingStatsFlow.value, null)
|
||||
}
|
||||
delay(settings.tunnelPingIntervalSeconds.toMillis())
|
||||
}
|
||||
@@ -300,9 +311,11 @@ constructor(
|
||||
) = coroutineScope {
|
||||
while (isActive) {
|
||||
ensureActive()
|
||||
val stats = getStatistics(tunnelId)
|
||||
ensureActive()
|
||||
updateTunnelStatus(tunnelId, null, stats, null, null)
|
||||
if (!powerManager.isDeviceIdleMode) {
|
||||
val stats = getStatistics(tunnelId)
|
||||
ensureActive()
|
||||
updateTunnelStatus(tunnelId, null, stats, null, null)
|
||||
}
|
||||
delay(STATS_DELAY)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-68
@@ -1,19 +1,11 @@
|
||||
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.DnsProtocol
|
||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendMode
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.DnsFailure
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.InvalidConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.ServiceNotRunning
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.UnknownError
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.VpnUnauthorized
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.ProxySettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.*
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.DnsSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.ProxySettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.AmneziaStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asAmBackendMode
|
||||
@@ -21,7 +13,6 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.asBackendMode
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asTunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.toBackendCoreException
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.*
|
||||
@@ -33,13 +24,7 @@ import kotlinx.coroutines.flow.consumeAsFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.amnezia.awg.backend.Backend
|
||||
import org.amnezia.awg.backend.BackendException
|
||||
import org.amnezia.awg.backend.ProxyGoBackend
|
||||
import org.amnezia.awg.backend.Tunnel as AwgTunnel
|
||||
import org.amnezia.awg.config.Config
|
||||
import org.amnezia.awg.config.DnsSettings
|
||||
import org.amnezia.awg.config.proxy.HttpProxy
|
||||
import org.amnezia.awg.config.proxy.Proxy
|
||||
import org.amnezia.awg.config.proxy.Socks5Proxy
|
||||
import timber.log.Timber
|
||||
|
||||
class UserspaceTunnel
|
||||
@@ -47,9 +32,8 @@ class UserspaceTunnel
|
||||
constructor(
|
||||
@ApplicationScope applicationScope: CoroutineScope,
|
||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
private val proxySettingsRepository: ProxySettingsRepository,
|
||||
private val dnsSettingsRepository: DnsSettingsRepository,
|
||||
private val backend: Backend,
|
||||
private val runConfigHelper: RunConfigHelper,
|
||||
) : BaseTunnel(applicationScope, ioDispatcher) {
|
||||
|
||||
private val runtimeTunnels = ConcurrentHashMap<Int, AwgTunnel>()
|
||||
@@ -67,63 +51,17 @@ constructor(
|
||||
try {
|
||||
withTimeout(STARTUP_TIMEOUT_MS) {
|
||||
updateTunnelStatus(tunnelConfig.id, TunnelStatus.Starting)
|
||||
|
||||
val proxies: List<Proxy> =
|
||||
when (backend) {
|
||||
is ProxyGoBackend -> {
|
||||
val proxySettings = proxySettingsRepository.getProxySettings()
|
||||
Timber.d("Adding proxy configs")
|
||||
buildList {
|
||||
if (proxySettings.socks5ProxyEnabled) {
|
||||
add(
|
||||
Socks5Proxy(
|
||||
proxySettings.socks5ProxyBindAddress
|
||||
?: ProxySettings.DEFAULT_SOCKS_BIND_ADDRESS,
|
||||
proxySettings.proxyUsername,
|
||||
proxySettings.proxyPassword,
|
||||
)
|
||||
)
|
||||
}
|
||||
if (proxySettings.httpProxyEnabled) {
|
||||
add(
|
||||
HttpProxy(
|
||||
proxySettings.httpProxyBindAddress
|
||||
?: ProxySettings.DEFAULT_HTTP_BIND_ADDRESS,
|
||||
proxySettings.proxyUsername,
|
||||
proxySettings.proxyPassword,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
val setting = dnsSettingsRepository.getDnsSettings()
|
||||
val config = tunnelConfig.toAmConfig()
|
||||
val updatedConfig =
|
||||
Config.Builder()
|
||||
.apply {
|
||||
setInterface(config.`interface`)
|
||||
addPeers(config.peers)
|
||||
addProxies(proxies)
|
||||
setDnsSettings(
|
||||
DnsSettings(
|
||||
setting.dnsProtocol == DnsProtocol.DOH,
|
||||
Optional.ofNullable(setting.dnsEndpoint),
|
||||
)
|
||||
)
|
||||
}
|
||||
.build()
|
||||
backend.setState(runtimeTunnel, AwgTunnel.State.UP, updatedConfig)
|
||||
val runConfig = runConfigHelper.buildAmRunConfig(tunnelConfig)
|
||||
backend.setState(runtimeTunnel, AwgTunnel.State.UP, runConfig)
|
||||
}
|
||||
} catch (e: TimeoutCancellationException) {
|
||||
} catch (_: TimeoutCancellationException) {
|
||||
Timber.e("Startup timed out for ${tunnelConfig.name} (likely DNS hang)")
|
||||
errors.emit(tunnelConfig.name to DnsFailure())
|
||||
forceStopTunnel(tunnelConfig.id)
|
||||
close()
|
||||
} catch (e: BackendException) {
|
||||
close(e.toBackendCoreException())
|
||||
} catch (e: IllegalArgumentException) {
|
||||
} catch (_: IllegalArgumentException) {
|
||||
close(InvalidConfig())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while setting tunnel state")
|
||||
|
||||
@@ -15,8 +15,9 @@ import com.zaneschepke.wireguardautotunnel.data.entity.*
|
||||
AutoTunnelSettings::class,
|
||||
MonitoringSettings::class,
|
||||
DnsSettings::class,
|
||||
LockdownSettings::class,
|
||||
],
|
||||
version = 25,
|
||||
version = 27,
|
||||
autoMigrations =
|
||||
[
|
||||
AutoMigration(from = 1, to = 2),
|
||||
@@ -42,6 +43,7 @@ import com.zaneschepke.wireguardautotunnel.data.entity.*
|
||||
AutoMigration(from = 21, to = 22),
|
||||
AutoMigration(from = 22, to = 23),
|
||||
AutoMigration(from = 24, to = 25),
|
||||
AutoMigration(from = 26, to = 27, spec = GlobalsMigration::class),
|
||||
],
|
||||
exportSchema = true,
|
||||
)
|
||||
@@ -57,6 +59,8 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun monitoringSettingsDao(): MonitoringSettingsDao
|
||||
|
||||
abstract fun lockdownSettingsDao(): LockdownSettingsDao
|
||||
|
||||
abstract fun dnsSettingsDao(): DnsSettingsDao
|
||||
}
|
||||
|
||||
@@ -112,3 +116,12 @@ class FixProxySettingsMigration : AutoMigrationSpec {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RenameColumn.Entries(
|
||||
RenameColumn(
|
||||
tableName = "general_settings",
|
||||
fromColumnName = "is_tunnel_globals_enabled",
|
||||
toColumnName = "global_split_tunnel_enabled",
|
||||
)
|
||||
)
|
||||
class GlobalsMigration : AutoMigrationSpec
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.LockdownSettings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface LockdownSettingsDao {
|
||||
@Query("SELECT * FROM lockdown_settings LIMIT 1")
|
||||
suspend fun getLockdownSettings(): LockdownSettings?
|
||||
|
||||
@Upsert suspend fun upsert(lockdownSettings: LockdownSettings)
|
||||
|
||||
@Query("SELECT * FROM lockdown_settings LIMIT 1")
|
||||
fun getLockdownSettingsFlow(): Flow<LockdownSettings?>
|
||||
}
|
||||
@@ -11,4 +11,6 @@ data class DnsSettings(
|
||||
@ColumnInfo(name = "dns_protocol", defaultValue = "0")
|
||||
val dnsProtocol: DnsProtocol = DnsProtocol.fromValue(0),
|
||||
@ColumnInfo(name = "dns_endpoint") val dnsEndpoint: String? = null,
|
||||
@ColumnInfo(name = "global_tunnel_dns_enabled", defaultValue = "0")
|
||||
val isGlobalTunnelDnsEnabled: Boolean = false,
|
||||
)
|
||||
|
||||
+2
-4
@@ -14,8 +14,8 @@ data class GeneralSettings(
|
||||
val isRestoreOnBootEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_multi_tunnel_enabled", defaultValue = "0")
|
||||
val isMultiTunnelEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_tunnel_globals_enabled", defaultValue = "0")
|
||||
val isTunnelGlobalsEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "global_split_tunnel_enabled", defaultValue = "0")
|
||||
val isGlobalSplitTunnelEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "app_mode", defaultValue = "0") val appMode: AppMode = AppMode.fromValue(0),
|
||||
@ColumnInfo(name = "theme", defaultValue = "AUTOMATIC") val theme: String = "AUTOMATIC",
|
||||
@ColumnInfo(name = "locale") val locale: String? = null,
|
||||
@@ -26,8 +26,6 @@ data class GeneralSettings(
|
||||
val isPinLockEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_always_on_vpn_enabled", defaultValue = "0")
|
||||
val isAlwaysOnVpnEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_lan_on_kill_switch_enabled", defaultValue = "0")
|
||||
val isLanOnKillSwitchEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "custom_split_packages", defaultValue = "{}")
|
||||
val customSplitPackages: Map<String, String> = emptyMap(),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "lockdown_settings")
|
||||
data class LockdownSettings(
|
||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
@ColumnInfo(name = "bypass_lan", defaultValue = "0") val bypassLan: Boolean = false,
|
||||
@ColumnInfo(name = "metered", defaultValue = "0") val metered: Boolean = false,
|
||||
@ColumnInfo(name = "dual_stack", defaultValue = "0") val dualStack: Boolean = false,
|
||||
)
|
||||
@@ -28,8 +28,8 @@ data class TunnelConfig(
|
||||
@ColumnInfo(name = "position", defaultValue = "0") val position: Int = 0,
|
||||
@ColumnInfo(name = "auto_tunnel_apps", defaultValue = "[]")
|
||||
val autoTunnelApps: Set<String> = emptySet(),
|
||||
@ColumnInfo(name = "is_metered", defaultValue = "true") val isMetered: Boolean = true,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val GLOBAL_CONFIG_NAME = "4675ab06-903a-438b-8485-6ea4187a9512"
|
||||
}
|
||||
|
||||
+12
-2
@@ -4,7 +4,17 @@ import com.zaneschepke.wireguardautotunnel.data.entity.DnsSettings as Entity
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.DnsSettings as Domain
|
||||
|
||||
fun Entity.toDomain(): Domain =
|
||||
Domain(id = id, dnsProtocol = dnsProtocol, dnsEndpoint = dnsEndpoint)
|
||||
Domain(
|
||||
id = id,
|
||||
dnsProtocol = dnsProtocol,
|
||||
dnsEndpoint = dnsEndpoint,
|
||||
isGlobalTunnelDnsEnabled = isGlobalTunnelDnsEnabled,
|
||||
)
|
||||
|
||||
fun Domain.toEntity(): Entity =
|
||||
Entity(id = id, dnsProtocol = dnsProtocol, dnsEndpoint = dnsEndpoint)
|
||||
Entity(
|
||||
id = id,
|
||||
dnsProtocol = dnsProtocol,
|
||||
dnsEndpoint = dnsEndpoint,
|
||||
isGlobalTunnelDnsEnabled = isGlobalTunnelDnsEnabled,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.LockdownSettings as Entity
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.LockdownSettings as Domain
|
||||
|
||||
fun Entity.toDomain(): Domain =
|
||||
Domain(id = id, bypassLan = bypassLan, metered = metered, dualStack = dualStack)
|
||||
|
||||
fun Domain.toEntity(): Entity =
|
||||
Entity(id = id, bypassLan = bypassLan, metered = metered, dualStack = dualStack)
|
||||
@@ -10,7 +10,7 @@ fun Entity.toDomain(): Domain =
|
||||
isShortcutsEnabled = isShortcutsEnabled,
|
||||
isRestoreOnBootEnabled = isRestoreOnBootEnabled,
|
||||
isMultiTunnelEnabled = isMultiTunnelEnabled,
|
||||
isTunnelGlobalsEnabled = isTunnelGlobalsEnabled,
|
||||
isGlobalSplitTunnelEnabled = isGlobalSplitTunnelEnabled,
|
||||
appMode = appMode,
|
||||
theme = Theme.valueOf(theme.uppercase()),
|
||||
locale = locale,
|
||||
@@ -18,8 +18,6 @@ fun Entity.toDomain(): Domain =
|
||||
isRemoteControlEnabled = isRemoteControlEnabled,
|
||||
isPinLockEnabled = isPinLockEnabled,
|
||||
isAlwaysOnVpnEnabled = isAlwaysOnVpnEnabled,
|
||||
isLanOnKillSwitchEnabled = isLanOnKillSwitchEnabled,
|
||||
customSplitPackages = customSplitPackages,
|
||||
)
|
||||
|
||||
fun Domain.toEntity(): Entity =
|
||||
@@ -28,7 +26,7 @@ fun Domain.toEntity(): Entity =
|
||||
isShortcutsEnabled = isShortcutsEnabled,
|
||||
isRestoreOnBootEnabled = isRestoreOnBootEnabled,
|
||||
isMultiTunnelEnabled = isMultiTunnelEnabled,
|
||||
isTunnelGlobalsEnabled = isTunnelGlobalsEnabled,
|
||||
isGlobalSplitTunnelEnabled = isGlobalSplitTunnelEnabled,
|
||||
appMode = appMode,
|
||||
theme = theme.name,
|
||||
locale = locale,
|
||||
@@ -36,6 +34,4 @@ fun Domain.toEntity(): Entity =
|
||||
isRemoteControlEnabled = isRemoteControlEnabled,
|
||||
isPinLockEnabled = isPinLockEnabled,
|
||||
isAlwaysOnVpnEnabled = isAlwaysOnVpnEnabled,
|
||||
isLanOnKillSwitchEnabled = isLanOnKillSwitchEnabled,
|
||||
customSplitPackages = customSplitPackages,
|
||||
)
|
||||
|
||||
+2
@@ -19,6 +19,7 @@ fun Entity.toDomain(): Domain =
|
||||
isIpv4Preferred = isIpv4Preferred,
|
||||
position = position,
|
||||
autoTunnelApps = autoTunnelApps,
|
||||
isMetered = isMetered,
|
||||
)
|
||||
|
||||
fun Domain.toEntity(): Entity =
|
||||
@@ -37,4 +38,5 @@ fun Domain.toEntity(): Entity =
|
||||
isIpv4Preferred = isIpv4Preferred,
|
||||
position = position,
|
||||
autoTunnelApps = autoTunnelApps,
|
||||
isMetered = isMetered,
|
||||
)
|
||||
|
||||
@@ -316,3 +316,98 @@ val MIGRATION_23_24 =
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_25_26 =
|
||||
object : Migration(25, 26) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `lockdown_settings` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`bypass_lan` INTEGER NOT NULL DEFAULT 0,
|
||||
`metered` INTEGER NOT NULL DEFAULT 0,
|
||||
`dual_stack` INTEGER NOT NULL DEFAULT 0
|
||||
)
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
val cursor =
|
||||
db.query("SELECT `is_lan_on_kill_switch_enabled` FROM `general_settings` LIMIT 1")
|
||||
var bypassLan = 0
|
||||
if (cursor.moveToFirst()) {
|
||||
bypassLan = if (cursor.getInt(0) != 0) 1 else 0
|
||||
}
|
||||
cursor.close()
|
||||
|
||||
db.execSQL(
|
||||
"""
|
||||
INSERT INTO `lockdown_settings` (`bypass_lan`, `metered`, `dual_stack`)
|
||||
VALUES (?, 0, 0)
|
||||
"""
|
||||
.trimIndent(),
|
||||
arrayOf(bypassLan),
|
||||
)
|
||||
|
||||
db.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `general_settings_new` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`is_shortcuts_enabled` INTEGER NOT NULL DEFAULT 0,
|
||||
`is_restore_on_boot_enabled` INTEGER NOT NULL DEFAULT 0,
|
||||
`is_multi_tunnel_enabled` INTEGER NOT NULL DEFAULT 0,
|
||||
`is_tunnel_globals_enabled` INTEGER NOT NULL DEFAULT 0,
|
||||
`app_mode` INTEGER NOT NULL DEFAULT 0,
|
||||
`theme` TEXT NOT NULL DEFAULT 'AUTOMATIC',
|
||||
`locale` TEXT,
|
||||
`remote_key` TEXT,
|
||||
`is_remote_control_enabled` INTEGER NOT NULL DEFAULT 0,
|
||||
`is_pin_lock_enabled` INTEGER NOT NULL DEFAULT 0,
|
||||
`is_always_on_vpn_enabled` INTEGER NOT NULL DEFAULT 0,
|
||||
`custom_split_packages` TEXT NOT NULL DEFAULT '{}'
|
||||
)
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
db.execSQL(
|
||||
"""
|
||||
INSERT INTO `general_settings_new` (
|
||||
`id`,
|
||||
`is_shortcuts_enabled`,
|
||||
`is_restore_on_boot_enabled`,
|
||||
`is_multi_tunnel_enabled`,
|
||||
`is_tunnel_globals_enabled`,
|
||||
`app_mode`,
|
||||
`theme`,
|
||||
`locale`,
|
||||
`remote_key`,
|
||||
`is_remote_control_enabled`,
|
||||
`is_pin_lock_enabled`,
|
||||
`is_always_on_vpn_enabled`,
|
||||
`custom_split_packages`
|
||||
)
|
||||
SELECT
|
||||
`id`,
|
||||
`is_shortcuts_enabled`,
|
||||
`is_restore_on_boot_enabled`,
|
||||
`is_multi_tunnel_enabled`,
|
||||
`is_tunnel_globals_enabled`,
|
||||
`app_mode`,
|
||||
`theme`,
|
||||
`locale`,
|
||||
`remote_key`,
|
||||
`is_remote_control_enabled`,
|
||||
`is_pin_lock_enabled`,
|
||||
`is_always_on_vpn_enabled`,
|
||||
`custom_split_packages`
|
||||
FROM `general_settings`
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
db.execSQL("DROP TABLE `general_settings`")
|
||||
|
||||
db.execSQL("ALTER TABLE `general_settings_new` RENAME TO `general_settings`")
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.dao.LockdownSettingsDao
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.LockdownSettings as Entity
|
||||
import com.zaneschepke.wireguardautotunnel.data.mapper.toDomain
|
||||
import com.zaneschepke.wireguardautotunnel.data.mapper.toEntity
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.LockdownSettings as Domain
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.LockdownSettingsRepository
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class RoomLockdownSettingsRepository(
|
||||
private val lockdownSettingsDao: LockdownSettingsDao,
|
||||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : LockdownSettingsRepository {
|
||||
override suspend fun upsert(lockdownSettings: Domain) {
|
||||
withContext(ioDispatcher) { lockdownSettingsDao.upsert(lockdownSettings.toEntity()) }
|
||||
}
|
||||
|
||||
override val flow =
|
||||
lockdownSettingsDao
|
||||
.getLockdownSettingsFlow()
|
||||
.map { (it ?: Entity()).toDomain() }
|
||||
.flowOn(ioDispatcher)
|
||||
|
||||
override suspend fun getLockdownSettings(): Domain {
|
||||
return withContext(ioDispatcher) {
|
||||
(lockdownSettingsDao.getLockdownSettings() ?: Entity()).toDomain()
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
@@ -23,7 +23,6 @@ class RoomProxySettingsRepository(
|
||||
override val flow =
|
||||
proxySettingsDao
|
||||
.getProxySettingsFlow()
|
||||
.flowOn(ioDispatcher)
|
||||
.map { (it ?: Entity()).toDomain() }
|
||||
.flowOn(ioDispatcher)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.zaneschepke.wireguardautotunnel.data.DataStoreManager
|
||||
import com.zaneschepke.wireguardautotunnel.data.DatabaseCallback
|
||||
import com.zaneschepke.wireguardautotunnel.data.dao.*
|
||||
import com.zaneschepke.wireguardautotunnel.data.migrations.MIGRATION_23_24
|
||||
import com.zaneschepke.wireguardautotunnel.data.migrations.MIGRATION_25_26
|
||||
import com.zaneschepke.wireguardautotunnel.data.network.GitHubApi
|
||||
import com.zaneschepke.wireguardautotunnel.data.network.KtorClient
|
||||
import com.zaneschepke.wireguardautotunnel.data.network.KtorGitHubApi
|
||||
@@ -56,6 +57,7 @@ class RepositoryModule {
|
||||
context.getString(R.string.db_name),
|
||||
)
|
||||
.addMigrations(MIGRATION_23_24(dataStoreManager.dataStore))
|
||||
.addMigrations(MIGRATION_25_26)
|
||||
.fallbackToDestructiveMigration(true)
|
||||
.addCallback(callback)
|
||||
.build()
|
||||
@@ -67,6 +69,12 @@ class RepositoryModule {
|
||||
return appDatabase.generalSettingsDao()
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideLockdownDoa(appDatabase: AppDatabase): LockdownSettingsDao {
|
||||
return appDatabase.lockdownSettingsDao()
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideDnsSettingsDao(appDatabase: AppDatabase): DnsSettingsDao {
|
||||
@@ -106,6 +114,15 @@ class RepositoryModule {
|
||||
return RoomTunnelRepository(tunnelConfigDao, ioDispatcher)
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideLockdownSettingsRepository(
|
||||
lockdownSettingsDao: LockdownSettingsDao,
|
||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
): LockdownSettingsRepository {
|
||||
return RoomLockdownSettingsRepository(lockdownSettingsDao, ioDispatcher)
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideGeneralSettingsRepository(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.zaneschepke.wireguardautotunnel.di
|
||||
|
||||
import android.content.Context
|
||||
import android.os.PowerManager
|
||||
import com.wireguard.android.backend.WgQuickBackend
|
||||
import com.wireguard.android.util.RootShell
|
||||
import com.wireguard.android.util.ToolsInstaller
|
||||
@@ -85,8 +86,9 @@ class TunnelModule {
|
||||
@ApplicationScope applicationScope: CoroutineScope,
|
||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
backend: com.wireguard.android.backend.Backend,
|
||||
runConfigHelper: RunConfigHelper,
|
||||
): TunnelProvider {
|
||||
return KernelTunnel(applicationScope, ioDispatcher, backend)
|
||||
return KernelTunnel(applicationScope, ioDispatcher, runConfigHelper, backend)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -94,18 +96,11 @@ class TunnelModule {
|
||||
@Userspace
|
||||
fun provideUserspaceProvider(
|
||||
@ApplicationScope applicationScope: CoroutineScope,
|
||||
proxySettingsRepository: ProxySettingsRepository,
|
||||
dnsSettingsRepository: DnsSettingsRepository,
|
||||
runConfigHelper: RunConfigHelper,
|
||||
@Userspace backend: Backend,
|
||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
): TunnelProvider {
|
||||
return UserspaceTunnel(
|
||||
applicationScope,
|
||||
ioDispatcher,
|
||||
proxySettingsRepository,
|
||||
dnsSettingsRepository,
|
||||
backend,
|
||||
)
|
||||
return UserspaceTunnel(applicationScope, ioDispatcher, backend, runConfigHelper)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -113,18 +108,11 @@ class TunnelModule {
|
||||
@ProxyUserspace
|
||||
fun provideProxyUserspaceProvider(
|
||||
@ApplicationScope applicationScope: CoroutineScope,
|
||||
dnsSettingsRepository: DnsSettingsRepository,
|
||||
proxySettingsRepository: ProxySettingsRepository,
|
||||
runConfigHelper: RunConfigHelper,
|
||||
@ProxyUserspace backend: Backend,
|
||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
): TunnelProvider {
|
||||
return UserspaceTunnel(
|
||||
applicationScope,
|
||||
ioDispatcher,
|
||||
proxySettingsRepository,
|
||||
dnsSettingsRepository,
|
||||
backend,
|
||||
)
|
||||
return UserspaceTunnel(applicationScope, ioDispatcher, backend, runConfigHelper)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -135,6 +123,7 @@ class TunnelModule {
|
||||
@ProxyUserspace proxyTunnel: TunnelProvider,
|
||||
serviceManager: ServiceManager,
|
||||
tunnelRepository: TunnelRepository,
|
||||
lockdownSettingsRepository: LockdownSettingsRepository,
|
||||
settingsRepository: GeneralSettingRepository,
|
||||
autoTunnelSettingsRepository: AutoTunnelSettingsRepository,
|
||||
tunnelMonitor: TunnelMonitor,
|
||||
@@ -148,6 +137,7 @@ class TunnelModule {
|
||||
serviceManager,
|
||||
settingsRepository,
|
||||
autoTunnelSettingsRepository,
|
||||
lockdownSettingsRepository,
|
||||
tunnelRepository,
|
||||
tunnelMonitor,
|
||||
applicationScope,
|
||||
@@ -155,6 +145,22 @@ class TunnelModule {
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTunnelConfigHelper(
|
||||
settingsRepository: GeneralSettingRepository,
|
||||
proxySettingsRepository: ProxySettingsRepository,
|
||||
dnsSettingsRepository: DnsSettingsRepository,
|
||||
tunnelRepository: TunnelRepository,
|
||||
): RunConfigHelper {
|
||||
return RunConfigHelper(
|
||||
settingsRepository,
|
||||
proxySettingsRepository,
|
||||
dnsSettingsRepository,
|
||||
tunnelRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideNetworkMonitor(
|
||||
@@ -200,6 +206,7 @@ class TunnelModule {
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideTunnelMonitor(
|
||||
@ApplicationContext context: Context,
|
||||
networkMonitor: NetworkMonitor,
|
||||
networkUtils: NetworkUtils,
|
||||
logReader: LogReader,
|
||||
@@ -214,6 +221,7 @@ class TunnelModule {
|
||||
networkMonitor,
|
||||
networkUtils,
|
||||
logReader,
|
||||
context.getSystemService(Context.POWER_SERVICE) as PowerManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,9 @@ package com.zaneschepke.wireguardautotunnel.domain.enums
|
||||
sealed class BackendMode {
|
||||
data object Inactive : BackendMode()
|
||||
|
||||
data class KillSwitch(val allowedIps: Set<String>) : BackendMode()
|
||||
data class KillSwitch(
|
||||
val allowedIps: Set<String>,
|
||||
val isMetered: Boolean,
|
||||
val dualStack: Boolean,
|
||||
) : BackendMode()
|
||||
}
|
||||
|
||||
@@ -6,4 +6,5 @@ data class DnsSettings(
|
||||
val id: Int = 0,
|
||||
val dnsProtocol: DnsProtocol = DnsProtocol.fromValue(0),
|
||||
val dnsEndpoint: String? = null,
|
||||
val isGlobalTunnelDnsEnabled: Boolean = false,
|
||||
)
|
||||
|
||||
+2
-3
@@ -8,7 +8,7 @@ data class GeneralSettings(
|
||||
val isShortcutsEnabled: Boolean = false,
|
||||
val isRestoreOnBootEnabled: Boolean = false,
|
||||
val isMultiTunnelEnabled: Boolean = false,
|
||||
val isTunnelGlobalsEnabled: Boolean = false,
|
||||
val isGlobalSplitTunnelEnabled: Boolean = false,
|
||||
val appMode: AppMode = AppMode.fromValue(0),
|
||||
val theme: Theme = Theme.AUTOMATIC,
|
||||
val locale: String? = null,
|
||||
@@ -16,6 +16,5 @@ data class GeneralSettings(
|
||||
val isRemoteControlEnabled: Boolean = false,
|
||||
val isPinLockEnabled: Boolean = false,
|
||||
val isAlwaysOnVpnEnabled: Boolean = false,
|
||||
val isLanOnKillSwitchEnabled: Boolean = false,
|
||||
val customSplitPackages: Map<String, String> = emptyMap(),
|
||||
val isKillSwitchMetered: Boolean = true,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||
|
||||
data class LockdownSettings(
|
||||
val id: Long = 0L,
|
||||
val bypassLan: Boolean = false,
|
||||
val metered: Boolean = false,
|
||||
val dualStack: Boolean = false,
|
||||
)
|
||||
+10
-52
@@ -27,6 +27,7 @@ data class TunnelConfig(
|
||||
val isIpv4Preferred: Boolean = true,
|
||||
val position: Int = 0,
|
||||
val autoTunnelApps: Set<String> = setOf(),
|
||||
val isMetered: Boolean = true,
|
||||
) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -42,7 +43,8 @@ data class TunnelConfig(
|
||||
pingTarget == other.pingTarget &&
|
||||
restartOnPingFailure == other.restartOnPingFailure &&
|
||||
tunnelNetworks == other.tunnelNetworks &&
|
||||
isIpv4Preferred == other.isIpv4Preferred
|
||||
isIpv4Preferred == other.isIpv4Preferred &&
|
||||
isMetered == other.isMetered
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@@ -65,7 +67,11 @@ data class TunnelConfig(
|
||||
return configFromWgQuick(wgQuick)
|
||||
}
|
||||
|
||||
fun copyWithGlobalValues(globalTunnel: TunnelConfig): TunnelConfig {
|
||||
fun copyWithGlobalValues(
|
||||
globalTunnel: TunnelConfig,
|
||||
includeDns: Boolean,
|
||||
includeSpitTunneling: Boolean,
|
||||
): TunnelConfig {
|
||||
val existingConfig = toAmConfig()
|
||||
val globalConfig = globalTunnel.toAmConfig()
|
||||
|
||||
@@ -114,62 +120,14 @@ data class TunnelConfig(
|
||||
setPreDown(existingConfig.`interface`.preDown)
|
||||
setPostDown(existingConfig.`interface`.postDown)
|
||||
|
||||
globalConfig.`interface`.mtu.ifPresent { setMtu(it) }
|
||||
if (globalConfig.`interface`.dnsServers.isNotEmpty()) {
|
||||
if (includeDns) {
|
||||
setDnsServers(globalConfig.`interface`.dnsServers)
|
||||
}
|
||||
if (globalConfig.`interface`.dnsSearchDomains.isNotEmpty()) {
|
||||
setDnsSearchDomains(globalConfig.`interface`.dnsSearchDomains)
|
||||
}
|
||||
|
||||
if (globalConfig.`interface`.excludedApplications.isNotEmpty()) {
|
||||
if (includeSpitTunneling) {
|
||||
setExcludedApplications(globalConfig.`interface`.excludedApplications)
|
||||
}
|
||||
if (!globalConfig.`interface`.includedApplications.isEmpty()) {
|
||||
setIncludedApplications(globalConfig.`interface`.includedApplications)
|
||||
}
|
||||
|
||||
if (globalConfig.`interface`.preUp.isNotEmpty()) {
|
||||
setPreUp(globalConfig.`interface`.preUp)
|
||||
}
|
||||
if (globalConfig.`interface`.postUp.isNotEmpty()) {
|
||||
setPostUp(globalConfig.`interface`.postUp)
|
||||
}
|
||||
if (globalConfig.`interface`.preDown.isNotEmpty()) {
|
||||
setPreDown(globalConfig.`interface`.preDown)
|
||||
}
|
||||
if (globalConfig.`interface`.postDown.isNotEmpty()) {
|
||||
setPostDown(globalConfig.`interface`.postDown)
|
||||
}
|
||||
|
||||
globalConfig.`interface`.junkPacketCount.ifPresent { setJunkPacketCount(it) }
|
||||
globalConfig.`interface`.junkPacketMinSize.ifPresent { setJunkPacketMinSize(it) }
|
||||
globalConfig.`interface`.junkPacketMaxSize.ifPresent { setJunkPacketMaxSize(it) }
|
||||
globalConfig.`interface`.initPacketJunkSize.ifPresent { setInitPacketJunkSize(it) }
|
||||
globalConfig.`interface`.responsePacketJunkSize.ifPresent {
|
||||
setResponsePacketJunkSize(it)
|
||||
}
|
||||
globalConfig.`interface`.initPacketMagicHeader.ifPresent {
|
||||
setInitPacketMagicHeader(it)
|
||||
}
|
||||
globalConfig.`interface`.responsePacketMagicHeader.ifPresent {
|
||||
setResponsePacketMagicHeader(it)
|
||||
}
|
||||
globalConfig.`interface`.underloadPacketMagicHeader.ifPresent {
|
||||
setUnderloadPacketMagicHeader(it)
|
||||
}
|
||||
globalConfig.`interface`.transportPacketMagicHeader.ifPresent {
|
||||
setTransportPacketMagicHeader(it)
|
||||
}
|
||||
globalConfig.`interface`.i1.ifPresent { setI1(it) }
|
||||
globalConfig.`interface`.i2.ifPresent { setI2(it) }
|
||||
globalConfig.`interface`.i3.ifPresent { setI3(it) }
|
||||
globalConfig.`interface`.i4.ifPresent { setI4(it) }
|
||||
globalConfig.`interface`.i5.ifPresent { setI5(it) }
|
||||
globalConfig.`interface`.j1.ifPresent { setJ1(it) }
|
||||
globalConfig.`interface`.j2.ifPresent { setJ2(it) }
|
||||
globalConfig.`interface`.j3.ifPresent { setJ3(it) }
|
||||
globalConfig.`interface`.itime.ifPresent { setItime(it) }
|
||||
}
|
||||
val newInterface = newInterfaceBuilder.build()
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.LockdownSettings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface LockdownSettingsRepository {
|
||||
suspend fun upsert(lockdownSettings: LockdownSettings)
|
||||
|
||||
val flow: Flow<LockdownSettings>
|
||||
|
||||
suspend fun getLockdownSettings(): LockdownSettings
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.common.button
|
||||
|
||||
import android.R.attr.onClick
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ExpandMore
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.VerticalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
|
||||
@Composable
|
||||
fun SheetButtonWithDivider(
|
||||
showDivider: Boolean = true,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.height(IntrinsicSize.Min),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
if (showDivider) {
|
||||
VerticalDivider(
|
||||
modifier = Modifier.fillMaxHeight().padding(horizontal = 8.dp, vertical = 8.dp),
|
||||
color = MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
}
|
||||
Box(modifier = Modifier.pointerInput(Unit) { detectTapGestures {} }) {
|
||||
IconButton(onClick = onClick, modifier) {
|
||||
Icon(
|
||||
Icons.Outlined.ExpandMore,
|
||||
contentDescription = stringResource(R.string.select),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ sealed class Route : NavKey {
|
||||
|
||||
@Keep @Serializable data object Tunnels : Route()
|
||||
|
||||
@Keep @Serializable data class TunnelOptions(val id: Int) : Route()
|
||||
@Keep @Serializable data class TunnelSettings(val id: Int) : Route()
|
||||
|
||||
@Keep @Serializable data class Config(val id: Int?) : Route()
|
||||
|
||||
@@ -42,8 +42,6 @@ sealed class Route : NavKey {
|
||||
|
||||
@Keep @Serializable data class ConfigGlobal(val id: Int?) : Route()
|
||||
|
||||
@Keep @Serializable data class TunnelGlobals(val id: Int) : Route()
|
||||
|
||||
@Keep @Serializable data class SplitTunnelGlobal(val id: Int) : Route()
|
||||
|
||||
@Keep @Serializable data object Sort : Route()
|
||||
@@ -58,6 +56,8 @@ sealed class Route : NavKey {
|
||||
|
||||
@Keep @Serializable data object ProxySettings : Route()
|
||||
|
||||
@Keep @Serializable data object LockdownSettings : Route()
|
||||
|
||||
@Keep @Serializable data object AutoTunnel : Route()
|
||||
|
||||
@Keep @Serializable data object AdvancedAutoTunnel : Route()
|
||||
@@ -107,7 +107,7 @@ enum class Tab(
|
||||
when (route) {
|
||||
is Route.Tunnels,
|
||||
Route.Sort,
|
||||
is Route.TunnelOptions,
|
||||
is Route.TunnelSettings,
|
||||
is Route.Config,
|
||||
is Route.Lock,
|
||||
is Route.SplitTunnel -> TUNNELS
|
||||
@@ -121,14 +121,14 @@ enum class Tab(
|
||||
Route.TunnelMonitoring,
|
||||
Route.AndroidIntegrations,
|
||||
Route.Dns,
|
||||
is Route.TunnelGlobals,
|
||||
is Route.ConfigGlobal,
|
||||
is Route.SplitTunnelGlobal,
|
||||
Route.ProxySettings,
|
||||
Route.LockdownSettings,
|
||||
Route.Appearance,
|
||||
Route.Language,
|
||||
Route.Display,
|
||||
Route.PingTarget,
|
||||
is Route.ConfigGlobal,
|
||||
Route.Logs -> SETTINGS
|
||||
is Route.Support,
|
||||
Route.License,
|
||||
|
||||
+25
-65
@@ -113,6 +113,19 @@ fun currentRouteAsNavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.language),
|
||||
)
|
||||
LockdownSettings ->
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
IconButton(onClick = { navController.pop() }) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Rounded.ArrowBack,
|
||||
stringResource(R.string.back),
|
||||
)
|
||||
}
|
||||
},
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.lockdown_settings),
|
||||
)
|
||||
License ->
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
@@ -211,8 +224,11 @@ fun currentRouteAsNavbarState(
|
||||
}
|
||||
},
|
||||
)
|
||||
is Config -> {
|
||||
val tunnelName = sharedState.tunnels.find { it.id == route.id }?.name
|
||||
is Config,
|
||||
is ConfigGlobal -> {
|
||||
val tunnelName =
|
||||
if (route is Config) sharedState.tunnels.find { it.id == route.id }?.name
|
||||
else context.getString(R.string.global_dns_servers)
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
IconButton(onClick = { navController.pop() }) {
|
||||
@@ -236,8 +252,12 @@ fun currentRouteAsNavbarState(
|
||||
},
|
||||
)
|
||||
}
|
||||
is SplitTunnel -> {
|
||||
val tunnelName = sharedState.tunnels.find { it.id == route.id }?.name
|
||||
is SplitTunnel,
|
||||
is SplitTunnelGlobal -> {
|
||||
val tunnelName =
|
||||
if (route is SplitTunnel)
|
||||
sharedState.tunnels.find { it.id == route.id }?.name
|
||||
else context.getString(R.string.global_split_tunneling)
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
IconButton(onClick = { navController.pop() }) {
|
||||
@@ -260,52 +280,6 @@ fun currentRouteAsNavbarState(
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
is SplitTunnelGlobal -> {
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
IconButton(onClick = { navController.pop() }) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Rounded.ArrowBack,
|
||||
stringResource(R.string.back),
|
||||
)
|
||||
}
|
||||
},
|
||||
topTitle = context.getString(R.string.splt_tunneling),
|
||||
topTrailing = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Rounded.Save, stringResource(R.string.save))
|
||||
}
|
||||
},
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
is ConfigGlobal -> {
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
IconButton(onClick = { navController.pop() }) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Rounded.ArrowBack,
|
||||
stringResource(R.string.back),
|
||||
)
|
||||
}
|
||||
},
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.configuration),
|
||||
topTrailing = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Rounded.Save, stringResource(R.string.save))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Support ->
|
||||
NavbarState(
|
||||
topTitle = context.getString(R.string.support),
|
||||
@@ -337,7 +311,7 @@ fun currentRouteAsNavbarState(
|
||||
topTitle = context.getString(R.string.ping_monitor),
|
||||
showBottomItems = true,
|
||||
)
|
||||
is TunnelOptions -> {
|
||||
is TunnelSettings -> {
|
||||
val tunnelName = sharedState.tunnels.find { it.id == route.id }?.name
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
@@ -497,20 +471,6 @@ fun currentRouteAsNavbarState(
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
is TunnelGlobals -> {
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
IconButton(onClick = { navController.pop() }) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Rounded.ArrowBack,
|
||||
stringResource(R.string.back),
|
||||
)
|
||||
}
|
||||
},
|
||||
topTitle = context.getString(R.string.global_overrides),
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
is WifiPreferences -> {
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
|
||||
+31
-57
@@ -7,11 +7,12 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.CallSplit
|
||||
import androidx.compose.material.icons.automirrored.outlined.ViewQuilt
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -30,6 +31,7 @@ import com.zaneschepke.wireguardautotunnel.data.model.AppMode
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SheetButtonWithDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SwitchWithDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
@@ -64,14 +66,9 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
val appMode = settingsState.settings.appMode
|
||||
val dnsEnabled by rememberSaveable(appMode) { mutableStateOf(appMode != AppMode.KERNEL) }
|
||||
|
||||
val showProxySettings by
|
||||
val showModeDivider by
|
||||
remember(appMode) {
|
||||
derivedStateOf {
|
||||
when (appMode) {
|
||||
AppMode.PROXY -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
derivedStateOf { appMode == AppMode.PROXY || appMode == AppMode.LOCK_DOWN }
|
||||
}
|
||||
|
||||
fun performBackupRestore(action: () -> Unit) {
|
||||
@@ -119,11 +116,8 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
leading = {
|
||||
Icon(ImageVector.vectorResource(R.drawable.sdk), contentDescription = null)
|
||||
},
|
||||
trailing = {
|
||||
Icon(
|
||||
Icons.Outlined.ExpandMore,
|
||||
contentDescription = stringResource(R.string.select),
|
||||
)
|
||||
trailing = { modifier ->
|
||||
SheetButtonWithDivider(showModeDivider, modifier) { showAppModeSheet = true }
|
||||
},
|
||||
title = stringResource(R.string.backend_mode),
|
||||
description = {
|
||||
@@ -131,34 +125,15 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
stringResource(R.string.current_template, appMode.asTitleString(context))
|
||||
)
|
||||
},
|
||||
onClick = { showAppModeSheet = true },
|
||||
onClick = {
|
||||
when (appMode) {
|
||||
AppMode.PROXY -> navController.push(Route.ProxySettings)
|
||||
AppMode.LOCK_DOWN -> navController.push(Route.LockdownSettings)
|
||||
AppMode.KERNEL,
|
||||
AppMode.VPN -> showAppModeSheet = true
|
||||
}
|
||||
},
|
||||
)
|
||||
if (appMode == AppMode.LOCK_DOWN) {
|
||||
SurfaceRow(
|
||||
leading = { Icon(Icons.Outlined.Lan, contentDescription = null) },
|
||||
title = stringResource(R.string.allow_lan_traffic),
|
||||
description = {
|
||||
Text(
|
||||
text = stringResource(R.string.bypass_lan_for_kill_switch),
|
||||
style =
|
||||
MaterialTheme.typography.bodySmall.copy(
|
||||
MaterialTheme.colorScheme.outline
|
||||
),
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = settingsState.settings.isLanOnKillSwitchEnabled,
|
||||
onClick = { viewModel.setLanKillSwitchEnabled(it) },
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.setLanKillSwitchEnabled(
|
||||
!settingsState.settings.isLanOnKillSwitchEnabled
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
SurfaceRow(
|
||||
leading = {
|
||||
Icon(
|
||||
@@ -182,29 +157,22 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
)
|
||||
SurfaceRow(
|
||||
leading = {
|
||||
Icon(ImageVector.vectorResource(R.drawable.globe), contentDescription = null)
|
||||
Icon(Icons.AutoMirrored.Outlined.CallSplit, contentDescription = null)
|
||||
},
|
||||
title = stringResource(R.string.global_overrides),
|
||||
title = stringResource(R.string.global_split_tunneling),
|
||||
trailing = { modifier ->
|
||||
SwitchWithDivider(
|
||||
checked = settingsState.settings.isTunnelGlobalsEnabled,
|
||||
onClick = { viewModel.setTunnelGlobals(it) },
|
||||
checked = settingsState.settings.isGlobalSplitTunnelEnabled,
|
||||
onClick = { viewModel.setGlobalSplitTunneling(it) },
|
||||
modifier = modifier,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
settingsState.globalTunnelConfig?.let {
|
||||
navController.push(Route.TunnelGlobals(it.id))
|
||||
navController.push(Route.SplitTunnelGlobal(id = it.id))
|
||||
}
|
||||
},
|
||||
)
|
||||
if (showProxySettings) {
|
||||
SurfaceRow(
|
||||
leading = { Icon(ImageVector.vectorResource(R.drawable.proxy), null) },
|
||||
title = stringResource(R.string.proxy_settings),
|
||||
onClick = { navController.push(Route.ProxySettings) },
|
||||
)
|
||||
}
|
||||
SurfaceRow(
|
||||
leading = { Icon(Icons.Outlined.Android, null) },
|
||||
title = stringResource(R.string.android_integrations),
|
||||
@@ -228,6 +196,10 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
},
|
||||
title = stringResource(R.string.ping_monitor),
|
||||
enabled = isPingMonitoringAvailable,
|
||||
description =
|
||||
if (!isPingMonitoringAvailable) {
|
||||
{ DescriptionText(stringResource(R.string.unavailable_in_mode)) }
|
||||
} else null,
|
||||
trailing = {
|
||||
SwitchWithDivider(
|
||||
checked = settingsState.monitoring.isPingEnabled,
|
||||
@@ -289,11 +261,13 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
leading = { Icon(Icons.Outlined.SettingsBackupRestore, contentDescription = null) },
|
||||
title = stringResource(R.string.backup_and_restore),
|
||||
onClick = { showBackupSheet = true },
|
||||
trailing = {
|
||||
Icon(
|
||||
Icons.Outlined.ExpandMore,
|
||||
contentDescription = stringResource(R.string.select),
|
||||
)
|
||||
trailing = { modifier ->
|
||||
IconButton(modifier = modifier, onClick = { showBackupSheet = true }) {
|
||||
Icon(
|
||||
Icons.Outlined.ExpandMore,
|
||||
contentDescription = stringResource(R.string.select),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+36
@@ -4,6 +4,7 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
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.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -12,25 +13,37 @@ import androidx.compose.material.icons.outlined.Dns
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.DnsProtocol
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.DnsProvider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SwitchWithDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.dropdown.LabelledDropdown
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.capitalize
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.DnsViewModel
|
||||
import java.util.*
|
||||
|
||||
@Composable
|
||||
fun DnsSettingsScreen(viewModel: DnsViewModel = hiltViewModel()) {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val dnsUiState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
if (dnsUiState.isLoading) return
|
||||
val locale = remember { Locale.getDefault() }
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
@@ -38,6 +51,7 @@ fun DnsSettingsScreen(viewModel: DnsViewModel = hiltViewModel()) {
|
||||
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Column {
|
||||
GroupLabel(stringResource(R.string.endpoint), Modifier.padding(horizontal = 16.dp))
|
||||
LabelledDropdown(
|
||||
title = stringResource(R.string.dns_protocol),
|
||||
leading = { Icon(Icons.Outlined.Dns, contentDescription = null) },
|
||||
@@ -59,5 +73,27 @@ fun DnsSettingsScreen(viewModel: DnsViewModel = hiltViewModel()) {
|
||||
)
|
||||
}
|
||||
}
|
||||
Column {
|
||||
GroupLabel(
|
||||
stringResource(R.string.tunnel).capitalize(locale),
|
||||
Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
SurfaceRow(
|
||||
leading = {
|
||||
Icon(ImageVector.vectorResource(R.drawable.host), contentDescription = null)
|
||||
},
|
||||
title = stringResource(R.string.global_dns_servers),
|
||||
trailing = { modifier ->
|
||||
SwitchWithDivider(
|
||||
checked = dnsUiState.dnsSettings.isGlobalTunnelDnsEnabled,
|
||||
onClick = { viewModel.setGlobalTunnelDnsEnabled(it) },
|
||||
modifier = modifier,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
dnsUiState.globalConfig?.let { navController.push(Route.ConfigGlobal(it.id)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.settings.globals
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.CallSplit
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
|
||||
@Composable
|
||||
fun TunnelGlobalsScreen(globalTunnelId: Int) {
|
||||
val navController = LocalNavController.current
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Top),
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()).fillMaxSize(),
|
||||
) {
|
||||
Column {
|
||||
SurfaceRow(
|
||||
leading = { Icon(Icons.Outlined.Settings, contentDescription = null) },
|
||||
title = stringResource(R.string.configuration),
|
||||
onClick = { navController.push(Route.ConfigGlobal(globalTunnelId)) },
|
||||
)
|
||||
SurfaceRow(
|
||||
leading = {
|
||||
Icon(Icons.AutoMirrored.Outlined.CallSplit, contentDescription = null)
|
||||
},
|
||||
title = stringResource(R.string.splt_tunneling),
|
||||
onClick = { navController.push(Route.SplitTunnelGlobal(id = globalTunnelId)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.settings.lockdown
|
||||
|
||||
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.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.DataUsage
|
||||
import androidx.compose.material.icons.outlined.Lan
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.LockdownViewModel
|
||||
|
||||
@Composable
|
||||
fun LockdownSettingsScreen(viewModel: LockdownViewModel = hiltViewModel()) {
|
||||
|
||||
val uiState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
if (uiState.isLoading) return
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Column {
|
||||
GroupLabel(
|
||||
stringResource(R.string.configuration),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
SurfaceRow(
|
||||
leading = { Icon(Icons.Outlined.Lan, contentDescription = null) },
|
||||
title = stringResource(R.string.allow_lan_traffic),
|
||||
description = {
|
||||
Text(
|
||||
text = stringResource(R.string.bypass_lan_for_kill_switch),
|
||||
style =
|
||||
MaterialTheme.typography.bodySmall.copy(
|
||||
MaterialTheme.colorScheme.outline
|
||||
),
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = uiState.lockdownSettings.bypassLan,
|
||||
onClick = { viewModel.setBypassLan(it) },
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setBypassLan(!uiState.lockdownSettings.bypassLan) },
|
||||
)
|
||||
SurfaceRow(
|
||||
leading = { Icon(Icons.Outlined.DataUsage, contentDescription = null) },
|
||||
title = stringResource(R.string.metered_tunnel),
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = uiState.lockdownSettings.metered,
|
||||
onClick = { viewModel.setMetered(it) },
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setMetered(!uiState.lockdownSettings.metered) },
|
||||
)
|
||||
SurfaceRow(
|
||||
leading = {
|
||||
Icon(ImageVector.vectorResource(R.drawable.host), contentDescription = null)
|
||||
},
|
||||
title = "Dual-stack",
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = uiState.lockdownSettings.dualStack,
|
||||
onClick = { viewModel.setDualStack(it) },
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setDualStack(!uiState.lockdownSettings.dualStack) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -94,7 +94,7 @@ fun TunnelList(
|
||||
if (sharedState.selectedTunnels.isNotEmpty()) {
|
||||
viewModel.toggleSelectedTunnel(tunnel.id)
|
||||
} else {
|
||||
navController.push(Route.TunnelOptions(tunnel.id))
|
||||
navController.push(Route.TunnelSettings(tunnel.id))
|
||||
viewModel.clearSelectedTunnels()
|
||||
}
|
||||
},
|
||||
|
||||
+9
-8
@@ -165,14 +165,15 @@ fun InterfaceFields(
|
||||
.lowercase(locale),
|
||||
modifier = Modifier.weight(3f),
|
||||
)
|
||||
ConfigurationTextBox(
|
||||
value = interfaceState.mtu,
|
||||
onValueChange = { onInterfaceChange(interfaceState.copy(mtu = it)) },
|
||||
label = stringResource(R.string.mtu),
|
||||
hint = stringResource(R.string.auto).lowercase(locale),
|
||||
modifier = Modifier.weight(2f),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
)
|
||||
if (!isGlobalConfig)
|
||||
ConfigurationTextBox(
|
||||
value = interfaceState.mtu,
|
||||
onValueChange = { onInterfaceChange(interfaceState.copy(mtu = it)) },
|
||||
label = stringResource(R.string.mtu),
|
||||
hint = stringResource(R.string.auto).lowercase(locale),
|
||||
modifier = Modifier.weight(2f),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
)
|
||||
}
|
||||
if (showScripts) {
|
||||
ConfigurationTextBox(
|
||||
|
||||
+48
-47
@@ -70,56 +70,57 @@ fun InterfaceSection(
|
||||
stringResource(R.string.interface_),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
Row {
|
||||
if (isTv && !isGlobalConfig) {
|
||||
IconButton(onClick = { showPrivateKey = !showPrivateKey }) {
|
||||
Icon(
|
||||
Icons.Outlined.RemoveRedEye,
|
||||
stringResource(R.string.show_password),
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
enabled = true,
|
||||
onClick = {
|
||||
val keypair = KeyPair()
|
||||
onInterfaceChange(
|
||||
configProxy.`interface`.copy(
|
||||
privateKey = keypair.privateKey.toBase64(),
|
||||
publicKey = keypair.publicKey.toBase64(),
|
||||
)
|
||||
if (!isGlobalConfig)
|
||||
Row {
|
||||
if (isTv) {
|
||||
IconButton(onClick = { showPrivateKey = !showPrivateKey }) {
|
||||
Icon(
|
||||
Icons.Outlined.RemoveRedEye,
|
||||
stringResource(R.string.show_password),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.Refresh,
|
||||
stringResource(R.string.rotate_keys),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
enabled = true,
|
||||
onClick = {
|
||||
val keypair = KeyPair()
|
||||
onInterfaceChange(
|
||||
configProxy.`interface`.copy(
|
||||
privateKey = keypair.privateKey.toBase64(),
|
||||
publicKey = keypair.publicKey.toBase64(),
|
||||
)
|
||||
)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.Refresh,
|
||||
stringResource(R.string.rotate_keys),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
InterfaceDropdown(
|
||||
expanded = isDropDownExpanded,
|
||||
onExpandedChange = { isDropDownExpanded = it },
|
||||
showScripts = showScripts,
|
||||
showAmneziaValues = showAmneziaValues,
|
||||
isAmneziaCompatibilitySet = isAmneziaCompatibilitySet,
|
||||
onToggleScripts = { showScripts = !showScripts },
|
||||
onToggleAmneziaValues = { showAmneziaValues = !showAmneziaValues },
|
||||
onToggleAmneziaCompatibility = { toggleAmneziaCompat() },
|
||||
onMimicQuic = {
|
||||
showAmneziaValues = true
|
||||
onMimicQuic()
|
||||
},
|
||||
onMimicDns = {
|
||||
showAmneziaValues = true
|
||||
onMimicDns()
|
||||
},
|
||||
onMimicSip = {
|
||||
showAmneziaValues = true
|
||||
onMimicSip()
|
||||
},
|
||||
)
|
||||
}
|
||||
InterfaceDropdown(
|
||||
expanded = isDropDownExpanded,
|
||||
onExpandedChange = { isDropDownExpanded = it },
|
||||
showScripts = showScripts,
|
||||
showAmneziaValues = showAmneziaValues,
|
||||
isAmneziaCompatibilitySet = isAmneziaCompatibilitySet,
|
||||
onToggleScripts = { showScripts = !showScripts },
|
||||
onToggleAmneziaValues = { showAmneziaValues = !showAmneziaValues },
|
||||
onToggleAmneziaCompatibility = { toggleAmneziaCompat() },
|
||||
onMimicQuic = {
|
||||
showAmneziaValues = true
|
||||
onMimicQuic()
|
||||
},
|
||||
onMimicDns = {
|
||||
showAmneziaValues = true
|
||||
onMimicDns()
|
||||
},
|
||||
onMimicSip = {
|
||||
showAmneziaValues = true
|
||||
onMimicSip()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
|
||||
+14
-5
@@ -1,4 +1,4 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.tunneloptions
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -8,6 +8,7 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.CallSplit
|
||||
import androidx.compose.material.icons.outlined.DataUsage
|
||||
import androidx.compose.material.icons.outlined.Dns
|
||||
import androidx.compose.material.icons.outlined.Star
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -33,13 +34,13 @@ import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.text.DescriptionText
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.tunneloptions.components.QrCodeDialog
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.settings.components.QrCodeDialog
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.TunnelViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@Composable
|
||||
fun TunnelOptionsScreen(viewModel: TunnelViewModel) {
|
||||
fun TunnelSettingsScreen(viewModel: TunnelViewModel) {
|
||||
val navController = LocalNavController.current
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
|
||||
@@ -123,10 +124,18 @@ fun TunnelOptionsScreen(viewModel: TunnelViewModel) {
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = !tunnel.isIpv4Preferred,
|
||||
onClick = { viewModel.toggleIpv4Preferred() },
|
||||
onClick = { viewModel.setIpv4Preferred(!it) },
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.toggleIpv4Preferred() },
|
||||
onClick = { viewModel.setIpv4Preferred(!tunnel.isIpv4Preferred) },
|
||||
)
|
||||
SurfaceRow(
|
||||
leading = { Icon(Icons.Outlined.DataUsage, contentDescription = null) },
|
||||
title = stringResource(R.string.metered_tunnel),
|
||||
trailing = {
|
||||
ScaledSwitch(checked = tunnel.isMetered, onClick = { viewModel.setMetered(it) })
|
||||
},
|
||||
onClick = { viewModel.setMetered(!tunnel.isMetered) },
|
||||
)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.tunneloptions.components
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.settings.components
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.DnsSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
||||
|
||||
data class DnsUiState(val isLoading: Boolean = true, val dnsSettings: DnsSettings = DnsSettings())
|
||||
data class DnsUiState(
|
||||
val isLoading: Boolean = true,
|
||||
val dnsSettings: DnsSettings = DnsSettings(),
|
||||
val globalConfig: TunnelConfig? = null,
|
||||
)
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.LockdownSettings
|
||||
|
||||
data class LockdownSettingsUiState(
|
||||
val lockdownSettings: LockdownSettings = LockdownSettings(),
|
||||
val isLoading: Boolean = true,
|
||||
)
|
||||
+4
-2
@@ -60,7 +60,8 @@ fun Config.defaultName(): String {
|
||||
|
||||
fun Backend.BackendMode.asBackendMode(): BackendMode {
|
||||
return when (val status = this) {
|
||||
is Backend.BackendMode.KillSwitch -> BackendMode.KillSwitch(status.allowedIps)
|
||||
is Backend.BackendMode.KillSwitch ->
|
||||
BackendMode.KillSwitch(status.allowedIps, status.isMetered, status.isDualStack)
|
||||
else -> BackendMode.Inactive
|
||||
}
|
||||
}
|
||||
@@ -68,7 +69,8 @@ fun Backend.BackendMode.asBackendMode(): BackendMode {
|
||||
fun BackendMode.asAmBackendMode(): Backend.BackendMode {
|
||||
return when (val status = this) {
|
||||
is BackendMode.Inactive -> Backend.BackendMode.Inactive.INSTANCE
|
||||
is BackendMode.KillSwitch -> Backend.BackendMode.KillSwitch(status.allowedIps)
|
||||
is BackendMode.KillSwitch ->
|
||||
Backend.BackendMode.KillSwitch(status.allowedIps, status.isMetered, status.dualStack)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,25 +3,39 @@ package com.zaneschepke.wireguardautotunnel.viewmodel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.DnsProtocol
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.DnsProvider
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.DnsSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.DnsUiState
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
|
||||
@HiltViewModel
|
||||
class DnsViewModel @Inject constructor(private val dnsSettingsRepository: DnsSettingsRepository) :
|
||||
ContainerHost<DnsUiState, Nothing>, ViewModel() {
|
||||
class DnsViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val dnsSettingsRepository: DnsSettingsRepository,
|
||||
private val tunnelRepository: TunnelRepository,
|
||||
) : ContainerHost<DnsUiState, Nothing>, ViewModel() {
|
||||
|
||||
override val container =
|
||||
container<DnsUiState, Nothing>(
|
||||
DnsUiState(),
|
||||
buildSettings = { repeatOnSubscribedStopTimeout = 5000L },
|
||||
) {
|
||||
dnsSettingsRepository.flow.collect {
|
||||
reduce { state.copy(dnsSettings = it, isLoading = false) }
|
||||
}
|
||||
combine(dnsSettingsRepository.flow, tunnelRepository.globalTunnelFlow) {
|
||||
dnsSettings,
|
||||
globalTunnel ->
|
||||
state.copy(
|
||||
dnsSettings = dnsSettings,
|
||||
isLoading = false,
|
||||
globalConfig = globalTunnel,
|
||||
)
|
||||
}
|
||||
.collect { reduce { it } }
|
||||
}
|
||||
|
||||
fun setDnsProtocol(to: DnsProtocol) = intent {
|
||||
@@ -35,4 +49,10 @@ class DnsViewModel @Inject constructor(private val dnsSettingsRepository: DnsSet
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun setGlobalTunnelDnsEnabled(to: Boolean) = intent {
|
||||
dnsSettingsRepository.upsert(state.dnsSettings.copy(isGlobalTunnelDnsEnabled = to))
|
||||
if (state.globalConfig == null)
|
||||
tunnelRepository.save(TunnelConfig.generateDefaultGlobalConfig())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.zaneschepke.wireguardautotunnel.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.LockdownSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.LockdownSettingsUiState
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
|
||||
@HiltViewModel
|
||||
class LockdownViewModel
|
||||
@Inject
|
||||
constructor(private val lockdownSettingsRepository: LockdownSettingsRepository) :
|
||||
ContainerHost<LockdownSettingsUiState, Nothing>, ViewModel() {
|
||||
|
||||
override val container =
|
||||
container<LockdownSettingsUiState, Nothing>(
|
||||
LockdownSettingsUiState(),
|
||||
buildSettings = { repeatOnSubscribedStopTimeout = 5000L },
|
||||
) {
|
||||
lockdownSettingsRepository.flow.collect {
|
||||
reduce { state.copy(lockdownSettings = it, isLoading = false) }
|
||||
}
|
||||
}
|
||||
|
||||
fun setBypassLan(to: Boolean) = intent {
|
||||
lockdownSettingsRepository.upsert(state.lockdownSettings.copy(bypassLan = to))
|
||||
}
|
||||
|
||||
fun setMetered(to: Boolean) = intent {
|
||||
lockdownSettingsRepository.upsert(state.lockdownSettings.copy(metered = to))
|
||||
}
|
||||
|
||||
fun setDualStack(to: Boolean) = intent {
|
||||
lockdownSettingsRepository.upsert(state.lockdownSettings.copy(dualStack = to))
|
||||
}
|
||||
}
|
||||
+2
-6
@@ -71,12 +71,8 @@ constructor(
|
||||
settingsRepository.upsert(state.settings.copy(isRestoreOnBootEnabled = to))
|
||||
}
|
||||
|
||||
fun setLanKillSwitchEnabled(to: Boolean) = intent {
|
||||
settingsRepository.upsert(state.settings.copy(isLanOnKillSwitchEnabled = to))
|
||||
}
|
||||
|
||||
fun setTunnelGlobals(to: Boolean) = intent {
|
||||
settingsRepository.upsert(state.settings.copy(isTunnelGlobalsEnabled = to))
|
||||
fun setGlobalSplitTunneling(to: Boolean) = intent {
|
||||
settingsRepository.upsert(state.settings.copy(isGlobalSplitTunnelEnabled = to))
|
||||
if (state.globalTunnelConfig == null)
|
||||
tunnelsRepository.save(TunnelConfig.generateDefaultGlobalConfig())
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ constructor(
|
||||
},
|
||||
tunnelManager.activeTunnels.map { it.containsKey(tunnelId) },
|
||||
) { tunnel, active ->
|
||||
state.copy(tunnel = tunnel, isActive = active, isLoading = tunnel == null)
|
||||
state.copy(tunnel = tunnel, isActive = active, isLoading = false)
|
||||
}
|
||||
.collect { reduce { it } }
|
||||
}
|
||||
@@ -49,9 +49,14 @@ constructor(
|
||||
tunnelRepository.updatePrimaryTunnel(update)
|
||||
}
|
||||
|
||||
fun toggleIpv4Preferred() = intent {
|
||||
val latestTunnel = state.tunnel ?: return@intent
|
||||
tunnelRepository.save(latestTunnel.copy(isIpv4Preferred = !latestTunnel.isIpv4Preferred))
|
||||
fun setIpv4Preferred(to: Boolean) = intent {
|
||||
val tunnel = state.tunnel ?: return@intent
|
||||
tunnelRepository.save(tunnel.copy(isIpv4Preferred = to))
|
||||
}
|
||||
|
||||
fun setMetered(to: Boolean) = intent {
|
||||
val tunnel = state.tunnel ?: return@intent
|
||||
tunnelRepository.save(tunnel.copy(isMetered = to))
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
||||
@@ -375,7 +375,6 @@
|
||||
the Play Store version of this app. Please browse the project\'s webpages to find where to donate.
|
||||
</string>
|
||||
<string name="delete_active_message">Cannot delete active tunnel.</string>
|
||||
<string name="global_overrides">Global overrides</string>
|
||||
<string name="back">Back</string>
|
||||
<string name="configuration">Configuration</string>
|
||||
<string name="about">About</string>
|
||||
@@ -418,4 +417,9 @@
|
||||
<string name="kernel_name_dots">Tunnel name cannot be \'.\' or \'..\' in kernel mode</string>
|
||||
<string name="kernel_name_special_characters">Tunnel name in kernel mode cannot have spaces or certain special characters (allowed: alphanumeric, _, =, +, ., -)</string>
|
||||
<string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string>
|
||||
<string name="metered_tunnel">Metered tunnel</string>
|
||||
<string name="lockdown_settings">Lockdown settings</string>
|
||||
<string name="unavailable_in_mode">Unavailable in this app mode</string>
|
||||
<string name="global_split_tunneling">Global split tunneling</string>
|
||||
<string name="global_dns_servers">Global DNS servers</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[versions]
|
||||
accompanist = "0.37.3"
|
||||
activityCompose = "1.11.0"
|
||||
amneziawgAndroid = "2.1.13"
|
||||
amneziawgAndroid = "2.2.0"
|
||||
androidx-junit = "1.3.0"
|
||||
icmp4a = "1.0.0"
|
||||
ipaddress = "5.5.1"
|
||||
|
||||
Reference in New Issue
Block a user