Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 186f063e23 | |||
| 2c78e9fcbd | |||
| 8bdeff515e | |||
| fa89715ede | |||
| 4a94905893 | |||
| cf184f2042 | |||
| e0ddb8730d | |||
| c8c041b872 | |||
| 7c8adb380b | |||
| 614f97fd14 | |||
| fbd470f5d2 | |||
| 5f89b2ed31 | |||
| 9503a3284b | |||
| 68c1a19bd3 | |||
| f3bb6667c3 | |||
| 244a990c37 | |||
| cbf07600b4 | |||
| ec8d90d13d | |||
| 85acca8604 | |||
| 0a9773d202 | |||
| 3cb4480a65 | |||
| a7f3255a76 | |||
| 7d7b99f448 | |||
| 74e9e462bb |
@@ -8,6 +8,7 @@ plugins {
|
|||||||
alias(libs.plugins.compose.compiler)
|
alias(libs.plugins.compose.compiler)
|
||||||
alias(libs.plugins.grgit)
|
alias(libs.plugins.grgit)
|
||||||
alias(libs.plugins.licensee)
|
alias(libs.plugins.licensee)
|
||||||
|
alias(libs.plugins.aboutlibraries)
|
||||||
}
|
}
|
||||||
|
|
||||||
ksp {
|
ksp {
|
||||||
@@ -17,9 +18,15 @@ ksp {
|
|||||||
licensee {
|
licensee {
|
||||||
allowedLicenses().forEach { allow(it) }
|
allowedLicenses().forEach { allow(it) }
|
||||||
allowedLicenseUrls().forEach { allowUrl(it) }
|
allowedLicenseUrls().forEach { allowUrl(it) }
|
||||||
// foss, but missing licenses
|
allowDependency("com.github.T8RIN.QuickieExtended", "quickie-foss", "1.18.1") {
|
||||||
ignoreDependencies("com.github.T8RIN.QuickieExtended")
|
because("FOSS library, but JitPack doesn't publish license metadata")
|
||||||
ignoreDependencies("com.github.topjohnwu.libsu")
|
allow("Apache-2.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
allowDependency("com.github.topjohnwu.libsu", "core", "6.0.0") {
|
||||||
|
because("FOSS library, but JitPack doesn't publish license metadata")
|
||||||
|
allow("Apache-2.0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configure<ApplicationExtension> {
|
configure<ApplicationExtension> {
|
||||||
@@ -94,21 +101,24 @@ configure<ApplicationExtension> {
|
|||||||
"proguard-rules.pro",
|
"proguard-rules.pro",
|
||||||
)
|
)
|
||||||
signingConfig = signingConfigs.getByName(Constants.RELEASE)
|
signingConfig = signingConfigs.getByName(Constants.RELEASE)
|
||||||
resValue("string", "provider", "\"${Constants.APP_NAME}.provider\"")
|
manifestPlaceholders["providerAuthority"] = "${Constants.APP_NAME}.provider"
|
||||||
|
buildConfigField("String", "FILE_PROVIDER_AUTHORITY", "\"${Constants.APP_NAME}.provider\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
debug {
|
debug {
|
||||||
applicationIdSuffix = ".debug"
|
applicationIdSuffix = ".debug"
|
||||||
resValue("string", "app_name", "WG Tunnel Debug")
|
resValue("string", "app_name", "WG Tunnel Debug")
|
||||||
isDebuggable = true
|
isDebuggable = true
|
||||||
resValue("string", "provider", "\"${Constants.APP_NAME}.provider.debug\"")
|
manifestPlaceholders["providerAuthority"] = "${Constants.APP_NAME}.provider.debug"
|
||||||
|
buildConfigField("String", "FILE_PROVIDER_AUTHORITY", "\"${Constants.APP_NAME}.provider.debug\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
create(Constants.NIGHTLY) {
|
create(Constants.NIGHTLY) {
|
||||||
initWith(buildTypes.getByName(Constants.RELEASE))
|
initWith(buildTypes.getByName(Constants.RELEASE))
|
||||||
applicationIdSuffix = ".nightly"
|
applicationIdSuffix = ".nightly"
|
||||||
resValue("string", "app_name", "WG Tunnel Nightly")
|
resValue("string", "app_name", "WG Tunnel Nightly")
|
||||||
resValue("string", "provider", "\"${Constants.APP_NAME}.provider.nightly\"")
|
manifestPlaceholders["providerAuthority"] = "${Constants.APP_NAME}.provider.nightly"
|
||||||
|
buildConfigField("String", "FILE_PROVIDER_AUTHORITY", "\"${Constants.APP_NAME}.provider.nightly\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +241,7 @@ dependencies {
|
|||||||
implementation(libs.bundles.ui.utilities)
|
implementation(libs.bundles.ui.utilities)
|
||||||
implementation(libs.lottie.compose)
|
implementation(libs.lottie.compose)
|
||||||
implementation(libs.sonner)
|
implementation(libs.sonner)
|
||||||
|
implementation(libs.aboutlibraries.compose)
|
||||||
|
|
||||||
// Misc utilities
|
// Misc utilities
|
||||||
implementation(libs.bundles.misc.utilities)
|
implementation(libs.bundles.misc.utilities)
|
||||||
@@ -271,17 +282,6 @@ dependencies {
|
|||||||
implementation(libs.koin.worker)
|
implementation(libs.koin.worker)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register<Copy>("copyLicenseeJsonToAssets") {
|
|
||||||
dependsOn("licensee")
|
|
||||||
val outputAssets = layout.projectDirectory.dir("src/main/assets")
|
|
||||||
from(layout.buildDirectory.file("reports/licensee/androidFdroidRelease/artifacts.json")) {
|
|
||||||
rename("artifacts.json", "licenses.json")
|
|
||||||
}
|
|
||||||
into(outputAssets)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named("preBuild") { dependsOn("copyLicenseeJsonToAssets") }
|
|
||||||
|
|
||||||
// https://gist.github.com/obfusk/61046e09cee352ae6dd109911534b12e#fix-proposed-by-linsui-disable-baseline-profiles
|
// https://gist.github.com/obfusk/61046e09cee352ae6dd109911534b12e#fix-proposed-by-linsui-disable-baseline-profiles
|
||||||
tasks.configureEach {
|
tasks.configureEach {
|
||||||
if (name.contains("ArtProfile")) {
|
if (name.contains("ArtProfile")) {
|
||||||
|
|||||||
@@ -0,0 +1,513 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 31,
|
||||||
|
"identityHash": "1dee3799f1c6526c48723fd2fee58d11",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "tunnel_config",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` 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, `quick_config` TEXT NOT NULL DEFAULT '', `dynamic_dns` INTEGER NOT NULL DEFAULT false, `is_ethernet_tunnel` INTEGER NOT NULL DEFAULT false, `prefer_ipv6` INTEGER NOT NULL DEFAULT false, `position` INTEGER NOT NULL DEFAULT 0, `auto_tunnel_apps` TEXT NOT NULL DEFAULT '[]', `is_metered` INTEGER NOT NULL DEFAULT false, `ipv4_fallback` INTEGER NOT NULL DEFAULT false, `ipv6_restore` INTEGER NOT NULL DEFAULT false)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"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": "quickConfig",
|
||||||
|
"columnName": "quick_config",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dynamicDnsEnabled",
|
||||||
|
"columnName": "dynamic_dns",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isEthernetTunnel",
|
||||||
|
"columnName": "is_ethernet_tunnel",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isIpv6Preferred",
|
||||||
|
"columnName": "prefer_ipv6",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ipv4FallbackEnabled",
|
||||||
|
"columnName": "ipv4_fallback",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ipv6RestoreEnabled",
|
||||||
|
"columnName": "ipv6_restore",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, `already_donated` INTEGER NOT NULL DEFAULT 0, `screen_recording_security` INTEGER NOT NULL DEFAULT 1, `global_amnezia_enabled` INTEGER NOT NULL DEFAULT 0, `tunnel_scripting_enabled` INTEGER NOT NULL DEFAULT 0)",
|
||||||
|
"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": "tunnelMode",
|
||||||
|
"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": "alreadyDonated",
|
||||||
|
"columnName": "already_donated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "screenRecordingSecurityEnabled",
|
||||||
|
"columnName": "screen_recording_security",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isGlobalAmneziaEnabled",
|
||||||
|
"columnName": "global_amnezia_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tunnelScriptingEnabled",
|
||||||
|
"columnName": "tunnel_scripting_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, `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, `disable_on_captive_portal` INTEGER NOT NULL DEFAULT 1)",
|
||||||
|
"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": "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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "disableTunnelOnCaptivePortal",
|
||||||
|
"columnName": "disable_on_captive_portal",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "monitoring_settings",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_local_logs_enabled` INTEGER NOT NULL DEFAULT 0, `tunnel_statistics_enabled` INTEGER NOT NULL DEFAULT 1, `tunnel_statistics_poll_interval` INTEGER NOT NULL DEFAULT 3)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isLocalLogsEnabled",
|
||||||
|
"columnName": "is_local_logs_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tunnelStatisticsEnabled",
|
||||||
|
"columnName": "tunnel_statistics_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tunnelStatisticsPollInterval",
|
||||||
|
"columnName": "tunnel_statistics_poll_interval",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, '1dee3799f1c6526c48723fd2fee58d11')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,520 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 32,
|
||||||
|
"identityHash": "fd4803fc483f41704303be9246dcfb4d",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "tunnel_config",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` 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, `quick_config` TEXT NOT NULL DEFAULT '', `dynamic_dns` INTEGER NOT NULL DEFAULT false, `is_ethernet_tunnel` INTEGER NOT NULL DEFAULT false, `prefer_ipv6` INTEGER NOT NULL DEFAULT false, `position` INTEGER NOT NULL DEFAULT 0, `auto_tunnel_apps` TEXT NOT NULL DEFAULT '[]', `is_metered` INTEGER NOT NULL DEFAULT false, `ipv4_fallback` INTEGER NOT NULL DEFAULT false, `ipv6_restore` INTEGER NOT NULL DEFAULT false)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"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": "quickConfig",
|
||||||
|
"columnName": "quick_config",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dynamicDnsEnabled",
|
||||||
|
"columnName": "dynamic_dns",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isEthernetTunnel",
|
||||||
|
"columnName": "is_ethernet_tunnel",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isIpv6Preferred",
|
||||||
|
"columnName": "prefer_ipv6",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ipv4FallbackEnabled",
|
||||||
|
"columnName": "ipv4_fallback",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ipv6RestoreEnabled",
|
||||||
|
"columnName": "ipv6_restore",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, `already_donated` INTEGER NOT NULL DEFAULT 0, `screen_recording_security` INTEGER NOT NULL DEFAULT 1, `global_amnezia_enabled` INTEGER NOT NULL DEFAULT 0, `tunnel_scripting_enabled` INTEGER NOT NULL DEFAULT 0, `seamless_roaming_enabled` INTEGER NOT NULL DEFAULT 0)",
|
||||||
|
"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": "tunnelMode",
|
||||||
|
"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": "alreadyDonated",
|
||||||
|
"columnName": "already_donated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "screenRecordingSecurityEnabled",
|
||||||
|
"columnName": "screen_recording_security",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isGlobalAmneziaEnabled",
|
||||||
|
"columnName": "global_amnezia_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tunnelScriptingEnabled",
|
||||||
|
"columnName": "tunnel_scripting_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "seamlessRoamingEnabled",
|
||||||
|
"columnName": "seamless_roaming_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, `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, `disable_on_captive_portal` INTEGER NOT NULL DEFAULT 1)",
|
||||||
|
"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": "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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "disableTunnelOnCaptivePortal",
|
||||||
|
"columnName": "disable_on_captive_portal",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "monitoring_settings",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_local_logs_enabled` INTEGER NOT NULL DEFAULT 0, `tunnel_statistics_enabled` INTEGER NOT NULL DEFAULT 1, `tunnel_statistics_poll_interval` INTEGER NOT NULL DEFAULT 3)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isLocalLogsEnabled",
|
||||||
|
"columnName": "is_local_logs_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tunnelStatisticsEnabled",
|
||||||
|
"columnName": "tunnel_statistics_enabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tunnelStatisticsPollInterval",
|
||||||
|
"columnName": "tunnel_statistics_poll_interval",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, 'fd4803fc483f41704303be9246dcfb4d')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,6 @@
|
|||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.App.Start"
|
android:theme="@style/Theme.App.Start"
|
||||||
tools:targetApi="tiramisu">
|
tools:targetApi="tiramisu">
|
||||||
@@ -157,7 +156,7 @@
|
|||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="@string/provider"
|
android:authorities="${providerAuthority}"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
@@ -178,7 +177,7 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".service.tile.TunnelControlTile"
|
android:name=".service.tile.TunnelControlTile"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:icon="@drawable/ic_notification"
|
android:icon="@drawable/qs_logo"
|
||||||
android:label="@string/tunnel_control"
|
android:label="@string/tunnel_control"
|
||||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||||
<meta-data
|
<meta-data
|
||||||
@@ -195,7 +194,7 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".service.tile.AutoTunnelControlTile"
|
android:name=".service.tile.AutoTunnelControlTile"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:icon="@drawable/ic_notification"
|
android:icon="@drawable/qs_logo"
|
||||||
android:label="@string/auto_tunnel"
|
android:label="@string/auto_tunnel"
|
||||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel
|
package com.zaneschepke.wireguardautotunnel
|
||||||
|
|
||||||
import ProxySettingsScreen
|
import ProxySettingsScreen
|
||||||
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.activity.SystemBarStyle
|
import androidx.activity.SystemBarStyle
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
@@ -50,6 +52,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -61,6 +64,7 @@ import androidx.compose.ui.text.intl.Locale
|
|||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||||
@@ -83,6 +87,7 @@ import com.zaneschepke.wireguardautotunnel.ui.LocalIsAndroidTV
|
|||||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.banner.AppAlertBanner
|
import com.zaneschepke.wireguardautotunnel.ui.common.banner.AppAlertBanner
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
|
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.LocalNetworkPermissionDialog
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.VpnDeniedDialog
|
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.VpnDeniedDialog
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.SecureRoute
|
import com.zaneschepke.wireguardautotunnel.ui.navigation.SecureRoute
|
||||||
@@ -132,6 +137,7 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.installApk
|
|||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.restartApp
|
import com.zaneschepke.wireguardautotunnel.util.extensions.restartApp
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.permission.LocalNetworkPermissionHelper
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.ConfigEditViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.ConfigEditViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SharedAppViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.SharedAppViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SplitTunnelViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.SplitTunnelViewModel
|
||||||
@@ -202,6 +208,48 @@ class MainActivity : AppCompatActivity() {
|
|||||||
var requestingTunnelMode by remember {
|
var requestingTunnelMode by remember {
|
||||||
mutableStateOf<Pair<TunnelMode?, TunnelConfig?>>(Pair(null, null))
|
mutableStateOf<Pair<TunnelMode?, TunnelConfig?>>(Pair(null, null))
|
||||||
}
|
}
|
||||||
|
var showLocalNetworkRationale by remember { mutableStateOf(false) }
|
||||||
|
var hasPromptedLocalNetwork by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val localNetworkPermissionLauncher =
|
||||||
|
rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.RequestPermission()
|
||||||
|
) { isGranted ->
|
||||||
|
if (!isGranted) {
|
||||||
|
val canAskAgain =
|
||||||
|
ActivityCompat.shouldShowRequestPermissionRationale(
|
||||||
|
this,
|
||||||
|
Manifest.permission.ACCESS_LOCAL_NETWORK,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!canAskAgain) {
|
||||||
|
val intent =
|
||||||
|
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||||
|
data = Uri.fromParts("package", packageName, null)
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
} else {
|
||||||
|
toaster.show(
|
||||||
|
message =
|
||||||
|
context.getString(R.string.local_network_permission_denied),
|
||||||
|
type = ToastType.Warning,
|
||||||
|
duration = 6000.milliseconds,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(uiState.isAppLoaded) {
|
||||||
|
if (
|
||||||
|
uiState.isAppLoaded &&
|
||||||
|
!hasPromptedLocalNetwork &&
|
||||||
|
LocalNetworkPermissionHelper.shouldRequestPermission() &&
|
||||||
|
!LocalNetworkPermissionHelper.isPermissionGranted(context)
|
||||||
|
) {
|
||||||
|
hasPromptedLocalNetwork = true
|
||||||
|
showLocalNetworkRationale = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val startingStack = buildList {
|
val startingStack = buildList {
|
||||||
add(Route.Tunnels)
|
add(Route.Tunnels)
|
||||||
@@ -292,6 +340,27 @@ class MainActivity : AppCompatActivity() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (showLocalNetworkRationale) {
|
||||||
|
LocalNetworkPermissionDialog(
|
||||||
|
onDismiss = {
|
||||||
|
showLocalNetworkRationale = false
|
||||||
|
toaster.show(
|
||||||
|
message =
|
||||||
|
context.getString(R.string.local_network_permission_denied),
|
||||||
|
type = ToastType.Warning,
|
||||||
|
duration = 6000.milliseconds,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onAttest = {
|
||||||
|
showLocalNetworkRationale = false
|
||||||
|
|
||||||
|
localNetworkPermissionLauncher.launch(
|
||||||
|
Manifest.permission.ACCESS_LOCAL_NETWORK
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
uiState.pendingWgImportUrl?.let { url ->
|
uiState.pendingWgImportUrl?.let { url ->
|
||||||
val host = Uri.parse(url).host ?: url
|
val host = Uri.parse(url).host ?: url
|
||||||
InfoDialog(
|
InfoDialog(
|
||||||
|
|||||||
@@ -48,11 +48,12 @@ class RemoteControlReceiver : BroadcastReceiver(), KoinComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val pendingResult = goAsync()
|
||||||
val action = intent.action ?: return
|
|
||||||
val appAction = Action.fromAction(action) ?: return
|
|
||||||
|
|
||||||
applicationScope.launch {
|
applicationScope.launch {
|
||||||
|
try {
|
||||||
|
val action = intent.action ?: return@launch
|
||||||
|
val appAction = Action.fromAction(action) ?: return@launch
|
||||||
|
|
||||||
val settings = settingsRepository.getGeneralSettings()
|
val settings = settingsRepository.getGeneralSettings()
|
||||||
|
|
||||||
if (!settings.isRemoteControlEnabled) return@launch
|
if (!settings.isRemoteControlEnabled) return@launch
|
||||||
@@ -90,6 +91,9 @@ class RemoteControlReceiver : BroadcastReceiver(), KoinComponent {
|
|||||||
autoTunnelCoordinator.disable()
|
autoTunnelCoordinator.disable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
pendingResult.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,19 +27,31 @@ class RestartReceiver : BroadcastReceiver(), KoinComponent {
|
|||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
Timber.d("RestartReceiver triggered with action: ${intent.action}")
|
Timber.d("RestartReceiver triggered with action: ${intent.action}")
|
||||||
|
|
||||||
|
val pendingResult = goAsync()
|
||||||
|
|
||||||
applicationScope.launch {
|
applicationScope.launch {
|
||||||
|
try {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
Intent.ACTION_BOOT_COMPLETED,
|
Intent.ACTION_BOOT_COMPLETED,
|
||||||
"android.intent.action.QUICKBOOT_POWERON",
|
"android.intent.action.QUICKBOOT_POWERON",
|
||||||
"com.htc.intent.action.QUICKBOOT_POWERON" -> {
|
"com.htc.intent.action.QUICKBOOT_POWERON" -> {
|
||||||
startupCoordinator.applyStartupPolicy()
|
startupCoordinator.applyStartupPolicy()
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent.ACTION_MY_PACKAGE_REPLACED -> {
|
Intent.ACTION_MY_PACKAGE_REPLACED -> {
|
||||||
Timber.i("Restoring state on package upgrade")
|
Timber.i("Restoring state on package upgrade")
|
||||||
startupCoordinator.applyStartupPolicy()
|
startupCoordinator.applyStartupPolicy()
|
||||||
logReader.deleteAndClearLogs()
|
logReader.deleteAndClearLogs()
|
||||||
appStateRepository.setShouldShowDonationSnackbar(true)
|
appStateRepository.setShouldShowDonationSnackbar(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
Timber.w("Unhandled action in RestartReceiver: ${intent.action}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
pendingResult.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class AppBoostrapCoordinator(
|
|||||||
listOf(
|
listOf(
|
||||||
async { bootstrapDns() },
|
async { bootstrapDns() },
|
||||||
async { ensureGlobalConfig() },
|
async { ensureGlobalConfig() },
|
||||||
async { restoreLockdown() },
|
async { restoreBackendConfiguration() },
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -73,9 +73,13 @@ class AppBoostrapCoordinator(
|
|||||||
tunnelRepository.ensureGlobalConfigExists()
|
tunnelRepository.ensureGlobalConfigExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun restoreLockdown() {
|
private suspend fun restoreBackendConfiguration() {
|
||||||
val settings = settingsRepository.getGeneralSettings()
|
val settings = settingsRepository.getGeneralSettings()
|
||||||
|
|
||||||
|
if (settings.seamlessRoamingEnabled) {
|
||||||
|
tunnelProvider.setSeamlessRoaming(true)
|
||||||
|
}
|
||||||
|
|
||||||
when (settings.tunnelMode) {
|
when (settings.tunnelMode) {
|
||||||
TunnelMode.LOCK_DOWN -> {
|
TunnelMode.LOCK_DOWN -> {
|
||||||
val lockdownSettings = lockdownRepository.getLockdownSettings()
|
val lockdownSettings = lockdownRepository.getLockdownSettings()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode
|
|||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.LockdownSettingsRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.LockdownSettingsRepository
|
||||||
|
|
||||||
class TunnelModeCoordinator(
|
class TunnelBackendCoordinator(
|
||||||
private val tunnelProvider: TunnelProvider,
|
private val tunnelProvider: TunnelProvider,
|
||||||
private val settingsRepository: GeneralSettingRepository,
|
private val settingsRepository: GeneralSettingRepository,
|
||||||
private val lockdownRepository: LockdownSettingsRepository,
|
private val lockdownRepository: LockdownSettingsRepository,
|
||||||
@@ -42,7 +42,6 @@ class TunnelModeCoordinator(
|
|||||||
when (newMode) {
|
when (newMode) {
|
||||||
TunnelMode.LOCK_DOWN -> {
|
TunnelMode.LOCK_DOWN -> {
|
||||||
val lockdownSettings = lockdownRepository.getLockdownSettings()
|
val lockdownSettings = lockdownRepository.getLockdownSettings()
|
||||||
|
|
||||||
tunnelProvider.setLockDown(lockdownSettings).getOrThrow()
|
tunnelProvider.setLockDown(lockdownSettings).getOrThrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,4 +49,9 @@ class TunnelModeCoordinator(
|
|||||||
TunnelMode.PROXY -> Unit
|
TunnelMode.PROXY -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun changeSeamlessRoaming(enabled: Boolean) {
|
||||||
|
tunnelProvider.setSeamlessRoaming(enabled).getOrThrow()
|
||||||
|
settingsRepository.updateSeamlessRoaming(enabled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -9,10 +9,12 @@ import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode
|
|||||||
import com.zaneschepke.wireguardautotunnel.domain.events.TunnelActionEvent
|
import com.zaneschepke.wireguardautotunnel.domain.events.TunnelActionEvent
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.model.DnsSettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.DnsSettings
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.model.GeneralSettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.GeneralSettings
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.model.LockdownSettings
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.model.MonitoringSettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.MonitoringSettings
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.model.ProxySettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.ProxySettings
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
||||||
|
import com.zaneschepke.wireguardautotunnel.domain.repository.LockdownSettingsRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.MonitoringSettingsRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.MonitoringSettingsRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.ProxySettingsRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.ProxySettingsRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||||
@@ -44,6 +46,7 @@ class TunnelCoordinator(
|
|||||||
dnsSettingsRepository: RoomDnsSettingsRepository,
|
dnsSettingsRepository: RoomDnsSettingsRepository,
|
||||||
monitoringSettingsRepository: MonitoringSettingsRepository,
|
monitoringSettingsRepository: MonitoringSettingsRepository,
|
||||||
proxyRepository: ProxySettingsRepository,
|
proxyRepository: ProxySettingsRepository,
|
||||||
|
lockdownModeRepository: LockdownSettingsRepository,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -66,6 +69,7 @@ class TunnelCoordinator(
|
|||||||
val dns: DnsSettings,
|
val dns: DnsSettings,
|
||||||
val monitoring: MonitoringSettings,
|
val monitoring: MonitoringSettings,
|
||||||
val proxy: ProxySettings,
|
val proxy: ProxySettings,
|
||||||
|
val lockdown: LockdownSettings,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val runtimeSettingsSnapshot =
|
private val runtimeSettingsSnapshot =
|
||||||
@@ -74,12 +78,14 @@ class TunnelCoordinator(
|
|||||||
dnsSettingsRepository.flow,
|
dnsSettingsRepository.flow,
|
||||||
monitoringSettingsRepository.flow,
|
monitoringSettingsRepository.flow,
|
||||||
proxyRepository.flow,
|
proxyRepository.flow,
|
||||||
) { general, dns, monitoring, proxy ->
|
lockdownModeRepository.flow,
|
||||||
|
) { general, dns, monitoring, proxy, lockdown ->
|
||||||
RuntimeSettingsSnapshot(
|
RuntimeSettingsSnapshot(
|
||||||
general = general,
|
general = general,
|
||||||
dns = dns,
|
dns = dns,
|
||||||
monitoring = monitoring,
|
monitoring = monitoring,
|
||||||
proxy = proxy,
|
proxy = proxy,
|
||||||
|
lockdown = lockdown,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +123,7 @@ class TunnelCoordinator(
|
|||||||
|
|
||||||
// enforce single tunnel, for now
|
// enforce single tunnel, for now
|
||||||
if (backendStatus.value.activeTunnels.isNotEmpty()) {
|
if (backendStatus.value.activeTunnels.isNotEmpty()) {
|
||||||
stopActiveTunnelsInternal()
|
stopActiveTunnelsInternal(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
startTunnelInternal(config, source)
|
startTunnelInternal(config, source)
|
||||||
@@ -131,7 +137,13 @@ class TunnelCoordinator(
|
|||||||
stopTunnelInternal(id, source)
|
stopTunnelInternal(id, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun stopActiveTunnels() = tunnelMutex.withLock { stopActiveTunnelsInternal() }
|
suspend fun stopActiveTunnels(source: TunnelActionSource = TunnelActionSource.USER) =
|
||||||
|
tunnelMutex.withLock {
|
||||||
|
if (source == TunnelActionSource.USER) {
|
||||||
|
_userOverrideFlow.tryEmit(Unit)
|
||||||
|
}
|
||||||
|
stopActiveTunnelsInternal(source)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun startTunnelInternal(
|
private suspend fun startTunnelInternal(
|
||||||
tunnelConfig: TunnelConfig,
|
tunnelConfig: TunnelConfig,
|
||||||
@@ -143,6 +155,7 @@ class TunnelCoordinator(
|
|||||||
val dnsSettings = snapshot.dns
|
val dnsSettings = snapshot.dns
|
||||||
val proxySettings = snapshot.proxy
|
val proxySettings = snapshot.proxy
|
||||||
val monitoringSettings = snapshot.monitoring
|
val monitoringSettings = snapshot.monitoring
|
||||||
|
val lockdownSettings = snapshot.lockdown
|
||||||
|
|
||||||
val config = tunnelConfig.getConfig()
|
val config = tunnelConfig.getConfig()
|
||||||
val policy =
|
val policy =
|
||||||
@@ -178,8 +191,10 @@ class TunnelCoordinator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
TunnelMode.LOCK_DOWN -> {
|
TunnelMode.LOCK_DOWN -> {
|
||||||
|
BackendMode.Proxy.KillSwitchPrimary(
|
||||||
BackendMode.Proxy.KillSwitchPrimary(runConfig)
|
runConfig,
|
||||||
|
lockdownSettings.toKillSwitchConfig(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +233,7 @@ class TunnelCoordinator(
|
|||||||
_actions.emit(TunnelActionEvent.Stopped(tunnelId = id, source = source))
|
_actions.emit(TunnelActionEvent.Stopped(tunnelId = id, source = source))
|
||||||
}
|
}
|
||||||
|
|
||||||
stopActiveTunnelsInternal()
|
stopActiveTunnelsInternal(source)
|
||||||
return@withLock
|
return@withLock
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +258,15 @@ class TunnelCoordinator(
|
|||||||
.onFailure { _errors.emit(TunnelErrorEvent.from(it, id)) }
|
.onFailure { _errors.emit(TunnelErrorEvent.from(it, id)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun stopActiveTunnelsInternal() {
|
private suspend fun stopActiveTunnelsInternal(
|
||||||
|
source: TunnelActionSource = TunnelActionSource.USER
|
||||||
|
) {
|
||||||
|
val active = tunnelProvider.backendStatus.value.activeTunnels
|
||||||
|
|
||||||
|
active.keys.forEach { id ->
|
||||||
|
_actions.emit(TunnelActionEvent.Stopped(tunnelId = id, source = source))
|
||||||
|
}
|
||||||
|
|
||||||
tunnelProvider.stopActiveTunnels()
|
tunnelProvider.stopActiveTunnels()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ class ShortcutsActivity : ComponentActivity() {
|
|||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
applicationScope.launch {
|
|
||||||
shortcutCoordinator.handle(intent)
|
|
||||||
finish()
|
finish()
|
||||||
}
|
|
||||||
|
applicationScope.launch { shortcutCoordinator.handle(intent) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ import com.zaneschepke.tunnel.Tunnel
|
|||||||
import com.zaneschepke.tunnel.backend.Backend
|
import com.zaneschepke.tunnel.backend.Backend
|
||||||
import com.zaneschepke.tunnel.model.BackendMode
|
import com.zaneschepke.tunnel.model.BackendMode
|
||||||
import com.zaneschepke.tunnel.state.BackendStatus
|
import com.zaneschepke.tunnel.state.BackendStatus
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.events.BackendCoreException
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.events.BackendMessage
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.model.LockdownSettings
|
import com.zaneschepke.wireguardautotunnel.domain.model.LockdownSettings
|
||||||
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
@@ -53,9 +50,7 @@ class TunnelBackendProvider(
|
|||||||
return backend.disableKillSwitch()
|
return backend.disableKillSwitch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
override suspend fun setSeamlessRoaming(enabled: Boolean): Result<Unit> {
|
||||||
private val localErrorEvents = MutableSharedFlow<Pair<String?, BackendCoreException>>()
|
return backend.setSeamlessRoaming(enabled)
|
||||||
|
}
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
private val localMessageEvents = MutableSharedFlow<Pair<String?, BackendMessage>>()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ interface TunnelProvider {
|
|||||||
|
|
||||||
suspend fun disableLockDown(): Result<Unit>
|
suspend fun disableLockDown(): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun setSeamlessRoaming(enabled: Boolean): Result<Unit>
|
||||||
|
|
||||||
val backendStatus: StateFlow<BackendStatus>
|
val backendStatus: StateFlow<BackendStatus>
|
||||||
|
|
||||||
val events: Flow<TunnelEvent>
|
val events: Flow<TunnelEvent>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
|||||||
DnsSettings::class,
|
DnsSettings::class,
|
||||||
LockdownSettings::class,
|
LockdownSettings::class,
|
||||||
],
|
],
|
||||||
version = 30,
|
version = 32,
|
||||||
autoMigrations =
|
autoMigrations =
|
||||||
[
|
[
|
||||||
AutoMigration(from = 1, to = 2),
|
AutoMigration(from = 1, to = 2),
|
||||||
@@ -63,6 +63,8 @@ import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
|||||||
AutoMigration(from = 26, to = 27, spec = GlobalsMigration::class),
|
AutoMigration(from = 26, to = 27, spec = GlobalsMigration::class),
|
||||||
AutoMigration(from = 27, to = 28, spec = DonationMigration::class),
|
AutoMigration(from = 27, to = 28, spec = DonationMigration::class),
|
||||||
AutoMigration(from = 29, to = 30, spec = SingleConfigMigration::class),
|
AutoMigration(from = 29, to = 30, spec = SingleConfigMigration::class),
|
||||||
|
AutoMigration(from = 30, to = 31),
|
||||||
|
AutoMigration(from = 31, to = 32),
|
||||||
],
|
],
|
||||||
exportSchema = true,
|
exportSchema = true,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,4 +18,7 @@ interface AutoTunnelSettingsDao {
|
|||||||
|
|
||||||
@Query("UPDATE auto_tunnel_settings SET is_tunnel_enabled = :enabled")
|
@Query("UPDATE auto_tunnel_settings SET is_tunnel_enabled = :enabled")
|
||||||
suspend fun updateAutoTunnelEnabled(enabled: Boolean)
|
suspend fun updateAutoTunnelEnabled(enabled: Boolean)
|
||||||
|
|
||||||
|
@Query("UPDATE auto_tunnel_settings SET disable_on_captive_portal = :enabled")
|
||||||
|
suspend fun updateDisableOnCaptivePortal(enabled: Boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,4 +34,7 @@ interface GeneralSettingsDao {
|
|||||||
|
|
||||||
@Query("UPDATE general_settings SET screen_recording_security = :enabled")
|
@Query("UPDATE general_settings SET screen_recording_security = :enabled")
|
||||||
suspend fun updateScreenRecordingSecurity(enabled: Boolean)
|
suspend fun updateScreenRecordingSecurity(enabled: Boolean)
|
||||||
|
|
||||||
|
@Query("UPDATE general_settings SET seamless_roaming_enabled = :enabled")
|
||||||
|
suspend fun updateSeamlessRoaming(enabled: Boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,6 @@ data class AutoTunnelSettings(
|
|||||||
@ColumnInfo(name = "wifi_detection_method", defaultValue = "0")
|
@ColumnInfo(name = "wifi_detection_method", defaultValue = "0")
|
||||||
val wifiDetectionMethod: WifiDetectionMethod = WifiDetectionMethod.fromValue(0),
|
val wifiDetectionMethod: WifiDetectionMethod = WifiDetectionMethod.fromValue(0),
|
||||||
@ColumnInfo(name = "start_on_boot", defaultValue = "0") val startOnBoot: Boolean = false,
|
@ColumnInfo(name = "start_on_boot", defaultValue = "0") val startOnBoot: Boolean = false,
|
||||||
|
@ColumnInfo(name = "disable_on_captive_portal", defaultValue = "1")
|
||||||
|
val disableTunnelOnCaptivePortal: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,4 +34,6 @@ data class GeneralSettings(
|
|||||||
val isGlobalAmneziaEnabled: Boolean = false,
|
val isGlobalAmneziaEnabled: Boolean = false,
|
||||||
@ColumnInfo(name = "tunnel_scripting_enabled", defaultValue = "0")
|
@ColumnInfo(name = "tunnel_scripting_enabled", defaultValue = "0")
|
||||||
val tunnelScriptingEnabled: Boolean = true,
|
val tunnelScriptingEnabled: Boolean = true,
|
||||||
|
@ColumnInfo(name = "seamless_roaming_enabled", defaultValue = "0")
|
||||||
|
val seamlessRoamingEnabled: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ fun Entity.toDomain(): Domain =
|
|||||||
isTunnelOnUnsecureEnabled = isTunnelOnUnsecureEnabled,
|
isTunnelOnUnsecureEnabled = isTunnelOnUnsecureEnabled,
|
||||||
wifiDetectionMethod = wifiDetectionMethod,
|
wifiDetectionMethod = wifiDetectionMethod,
|
||||||
startOnBoot = startOnBoot,
|
startOnBoot = startOnBoot,
|
||||||
|
disableTunnelOnCaptivePortal = disableTunnelOnCaptivePortal
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Domain.toEntity(): Entity =
|
fun Domain.toEntity(): Entity =
|
||||||
@@ -31,4 +32,5 @@ fun Domain.toEntity(): Entity =
|
|||||||
isTunnelOnUnsecureEnabled = isTunnelOnUnsecureEnabled,
|
isTunnelOnUnsecureEnabled = isTunnelOnUnsecureEnabled,
|
||||||
wifiDetectionMethod = wifiDetectionMethod,
|
wifiDetectionMethod = wifiDetectionMethod,
|
||||||
startOnBoot = startOnBoot,
|
startOnBoot = startOnBoot,
|
||||||
|
disableTunnelOnCaptivePortal = disableTunnelOnCaptivePortal
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ fun Entity.toDomain(): Domain =
|
|||||||
screenRecordingSecurityEnabled = screenRecordingSecurityEnabled,
|
screenRecordingSecurityEnabled = screenRecordingSecurityEnabled,
|
||||||
isGlobalAmneziaEnabled = isGlobalAmneziaEnabled,
|
isGlobalAmneziaEnabled = isGlobalAmneziaEnabled,
|
||||||
tunnelScriptingEnabled = tunnelScriptingEnabled,
|
tunnelScriptingEnabled = tunnelScriptingEnabled,
|
||||||
|
seamlessRoamingEnabled = seamlessRoamingEnabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Domain.toEntity(): Entity =
|
fun Domain.toEntity(): Entity =
|
||||||
@@ -42,4 +43,5 @@ fun Domain.toEntity(): Entity =
|
|||||||
screenRecordingSecurityEnabled = screenRecordingSecurityEnabled,
|
screenRecordingSecurityEnabled = screenRecordingSecurityEnabled,
|
||||||
isGlobalAmneziaEnabled = isGlobalAmneziaEnabled,
|
isGlobalAmneziaEnabled = isGlobalAmneziaEnabled,
|
||||||
tunnelScriptingEnabled = tunnelScriptingEnabled,
|
tunnelScriptingEnabled = tunnelScriptingEnabled,
|
||||||
|
seamlessRoamingEnabled = seamlessRoamingEnabled,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,15 +1,25 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.data.network
|
package com.zaneschepke.wireguardautotunnel.data.network
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.engine.okhttp.OkHttp
|
import io.ktor.client.engine.okhttp.OkHttp
|
||||||
|
import io.ktor.client.plugins.DefaultRequest
|
||||||
import io.ktor.client.plugins.HttpTimeout
|
import io.ktor.client.plugins.HttpTimeout
|
||||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
import io.ktor.serialization.kotlinx.json.json
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
object KtorClient {
|
object KtorClient {
|
||||||
fun create(): HttpClient {
|
fun create(): HttpClient {
|
||||||
return HttpClient(OkHttp) {
|
return HttpClient(OkHttp) {
|
||||||
|
install(DefaultRequest) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.UserAgent, "wgtunnel/${BuildConfig.VERSION_NAME} (Android)")
|
||||||
|
append(HttpHeaders.Accept, "*/*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
json(
|
json(
|
||||||
Json {
|
Json {
|
||||||
@@ -18,10 +28,11 @@ object KtorClient {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
install(HttpTimeout) {
|
install(HttpTimeout) {
|
||||||
requestTimeoutMillis = 15000
|
requestTimeoutMillis = 120_000L
|
||||||
connectTimeoutMillis = 15000
|
connectTimeoutMillis = 30_000L
|
||||||
socketTimeoutMillis = 15000
|
socketTimeoutMillis = 120_000L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,8 @@ class RoomAutoTunnelSettingsRepository(private val autoTunnelSettingsDao: AutoTu
|
|||||||
override suspend fun updateAutoTunnelEnabled(enabled: Boolean) {
|
override suspend fun updateAutoTunnelEnabled(enabled: Boolean) {
|
||||||
autoTunnelSettingsDao.updateAutoTunnelEnabled(enabled)
|
autoTunnelSettingsDao.updateAutoTunnelEnabled(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun updateDisableOnCaptivePortal(enabled: Boolean) {
|
||||||
|
autoTunnelSettingsDao.updateDisableOnCaptivePortal(enabled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,4 +45,8 @@ class RoomSettingsRepository(private val settingsDao: GeneralSettingsDao) :
|
|||||||
override suspend fun updateScreenRecordingSecurity(enabled: Boolean) {
|
override suspend fun updateScreenRecordingSecurity(enabled: Boolean) {
|
||||||
settingsDao.updateScreenRecordingSecurity(enabled)
|
settingsDao.updateScreenRecordingSecurity(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun updateSeamlessRoaming(enabled: Boolean) {
|
||||||
|
settingsDao.updateSeamlessRoaming(enabled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import com.zaneschepke.wireguardautotunnel.util.network.NetworkUtils
|
|||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AutoTunnelViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.AutoTunnelViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.ConfigEditViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.ConfigEditViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.DnsViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.DnsViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.LicenseViewModel
|
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.LockdownViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.LockdownViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.LoggerViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.LoggerViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.MonitoringViewModel
|
import com.zaneschepke.wireguardautotunnel.viewmodel.MonitoringViewModel
|
||||||
@@ -80,7 +79,6 @@ val appModule = module {
|
|||||||
viewModelOf(::AutoTunnelViewModel)
|
viewModelOf(::AutoTunnelViewModel)
|
||||||
viewModel { (id: Int?) -> ConfigEditViewModel(get(), get(), get(), get(), get(), id) }
|
viewModel { (id: Int?) -> ConfigEditViewModel(get(), get(), get(), get(), get(), id) }
|
||||||
viewModelOf(::DnsViewModel)
|
viewModelOf(::DnsViewModel)
|
||||||
viewModelOf(::LicenseViewModel)
|
|
||||||
viewModelOf(::LockdownViewModel)
|
viewModelOf(::LockdownViewModel)
|
||||||
viewModelOf(::LoggerViewModel)
|
viewModelOf(::LoggerViewModel)
|
||||||
viewModelOf(::MonitoringViewModel)
|
viewModelOf(::MonitoringViewModel)
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ import com.zaneschepke.wireguardautotunnel.core.orchestration.AutoTunnelCoordina
|
|||||||
import com.zaneschepke.wireguardautotunnel.core.orchestration.DnsSettingsCoordinator
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.DnsSettingsCoordinator
|
||||||
import com.zaneschepke.wireguardautotunnel.core.orchestration.ShortcutCoordinator
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.ShortcutCoordinator
|
||||||
import com.zaneschepke.wireguardautotunnel.core.orchestration.StartupCoordinator
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.StartupCoordinator
|
||||||
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelBackendCoordinator
|
||||||
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelCoordinator
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelCoordinator
|
||||||
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelModeCoordinator
|
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
import org.koin.core.qualifier.named
|
import org.koin.core.qualifier.named
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val coordinatorModule = module {
|
val coordinatorModule = module {
|
||||||
singleOf(::ShortcutCoordinator)
|
singleOf(::ShortcutCoordinator)
|
||||||
singleOf(::TunnelModeCoordinator)
|
singleOf(::TunnelBackendCoordinator)
|
||||||
singleOf(::StartupCoordinator)
|
singleOf(::StartupCoordinator)
|
||||||
singleOf(::AutoTunnelCoordinator)
|
singleOf(::AutoTunnelCoordinator)
|
||||||
singleOf(::DnsSettingsCoordinator)
|
singleOf(::DnsSettingsCoordinator)
|
||||||
@@ -27,6 +27,7 @@ val coordinatorModule = module {
|
|||||||
get(),
|
get(),
|
||||||
get(),
|
get(),
|
||||||
get(),
|
get(),
|
||||||
|
get(),
|
||||||
get(named(Scope.APPLICATION)),
|
get(named(Scope.APPLICATION)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ data class AutoTunnelSettings(
|
|||||||
val isTunnelOnUnsecureEnabled: Boolean = false,
|
val isTunnelOnUnsecureEnabled: Boolean = false,
|
||||||
val wifiDetectionMethod: WifiDetectionMethod = WifiDetectionMethod.fromValue(0),
|
val wifiDetectionMethod: WifiDetectionMethod = WifiDetectionMethod.fromValue(0),
|
||||||
val startOnBoot: Boolean = false,
|
val startOnBoot: Boolean = false,
|
||||||
|
val disableTunnelOnCaptivePortal: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,5 +20,6 @@ data class GeneralSettings(
|
|||||||
val alreadyDonated: Boolean = false,
|
val alreadyDonated: Boolean = false,
|
||||||
val screenRecordingSecurityEnabled: Boolean = true,
|
val screenRecordingSecurityEnabled: Boolean = true,
|
||||||
val isGlobalAmneziaEnabled: Boolean = false,
|
val isGlobalAmneziaEnabled: Boolean = false,
|
||||||
val tunnelScriptingEnabled: Boolean = true,
|
val tunnelScriptingEnabled: Boolean = false,
|
||||||
|
val seamlessRoamingEnabled: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,4 +11,6 @@ interface AutoTunnelSettingsRepository {
|
|||||||
suspend fun getAutoTunnelSettings(): AutoTunnelSettings
|
suspend fun getAutoTunnelSettings(): AutoTunnelSettings
|
||||||
|
|
||||||
suspend fun updateAutoTunnelEnabled(enabled: Boolean)
|
suspend fun updateAutoTunnelEnabled(enabled: Boolean)
|
||||||
|
|
||||||
|
suspend fun updateDisableOnCaptivePortal(enabled: Boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,6 @@ interface GeneralSettingRepository {
|
|||||||
suspend fun updateGlobalAmneziaEnabled(enabled: Boolean)
|
suspend fun updateGlobalAmneziaEnabled(enabled: Boolean)
|
||||||
|
|
||||||
suspend fun updateScreenRecordingSecurity(enabled: Boolean)
|
suspend fun updateScreenRecordingSecurity(enabled: Boolean)
|
||||||
|
|
||||||
|
suspend fun updateSeamlessRoaming(enabled: Boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,16 +11,20 @@ sealed class ActiveNetwork {
|
|||||||
|
|
||||||
data object Cellular : ActiveNetwork()
|
data object Cellular : ActiveNetwork()
|
||||||
|
|
||||||
data class Wifi(val ssid: String, val isSecure: Boolean?) : ActiveNetwork()
|
data class Wifi(
|
||||||
|
val ssid: String,
|
||||||
|
val isSecure: Boolean?,
|
||||||
|
val requiresCaptivePortalLogin: Boolean,
|
||||||
|
) : ActiveNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class NetworkState(
|
data class NetworkState(
|
||||||
val activeNetwork: ActiveNetwork = ActiveNetwork.Disconnected,
|
val activeNetwork: ActiveNetwork = ActiveNetwork.Disconnected,
|
||||||
val locationServicesEnabled: Boolean = false,
|
val locationServicesEnabled: Boolean = false,
|
||||||
val locationPermissionGranted: Boolean = false,
|
val locationPermissionGranted: Boolean = false,
|
||||||
) {
|
// Has a network that can actually transfer data (not suspended)
|
||||||
fun hasInternet(): Boolean = activeNetwork !is ActiveNetwork.Disconnected
|
val hasUsableNetwork: Boolean = false,
|
||||||
}
|
)
|
||||||
|
|
||||||
fun ConnectivityState.toDomain(): NetworkState {
|
fun ConnectivityState.toDomain(): NetworkState {
|
||||||
val domainNetwork: ActiveNetwork =
|
val domainNetwork: ActiveNetwork =
|
||||||
@@ -33,7 +37,11 @@ fun ConnectivityState.toDomain(): NetworkState {
|
|||||||
null -> null
|
null -> null
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
ActiveNetwork.Wifi(ssid = network.ssid, isSecure = isSecure)
|
ActiveNetwork.Wifi(
|
||||||
|
ssid = network.ssid,
|
||||||
|
isSecure = isSecure,
|
||||||
|
requiresCaptivePortalLogin(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is MonitorActiveNetwork.Cellular -> ActiveNetwork.Cellular
|
is MonitorActiveNetwork.Cellular -> ActiveNetwork.Cellular
|
||||||
is MonitorActiveNetwork.Ethernet -> ActiveNetwork.Ethernet
|
is MonitorActiveNetwork.Ethernet -> ActiveNetwork.Ethernet
|
||||||
@@ -44,5 +52,6 @@ fun ConnectivityState.toDomain(): NetworkState {
|
|||||||
activeNetwork = domainNetwork,
|
activeNetwork = domainNetwork,
|
||||||
locationPermissionGranted = this.locationPermissionsGranted,
|
locationPermissionGranted = this.locationPermissionsGranted,
|
||||||
locationServicesEnabled = this.locationServicesEnabled,
|
locationServicesEnabled = this.locationServicesEnabled,
|
||||||
|
hasUsableNetwork = hasUsableNetwork(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,19 @@ class AutoTunnelEngine {
|
|||||||
|
|
||||||
val activeTunnelIds = backend.activeTunnels.keys.toSet()
|
val activeTunnelIds = backend.activeTunnels.keys.toSet()
|
||||||
|
|
||||||
if (!network.hasInternet()) {
|
val isOnCaptivePortalWifi =
|
||||||
|
network.activeNetwork is ActiveNetwork.Wifi &&
|
||||||
|
network.activeNetwork.requiresCaptivePortalLogin
|
||||||
|
|
||||||
|
if (isOnCaptivePortalWifi && settings.disableTunnelOnCaptivePortal) {
|
||||||
|
return if (activeTunnelIds.isNotEmpty()) {
|
||||||
|
Decision.Sync(start = emptySet(), stop = activeTunnelIds)
|
||||||
|
} else {
|
||||||
|
Decision.None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!network.hasUsableNetwork) {
|
||||||
return if (settings.isStopOnNoInternetEnabled) {
|
return if (settings.isStopOnNoInternetEnabled) {
|
||||||
Decision.StopDueToNoInternet
|
Decision.StopDueToNoInternet
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -192,11 +192,11 @@ class AutoTunnelService : LifecycleService() {
|
|||||||
reconciliationMutex.withLock {
|
reconciliationMutex.withLock {
|
||||||
val currentNetworkState = networkEngine.stableState.value?.state?.toDomain()
|
val currentNetworkState = networkEngine.stableState.value?.state?.toDomain()
|
||||||
|
|
||||||
val stillNoInternet = currentNetworkState?.hasInternet() == false
|
val stillNoUsableNetwork = currentNetworkState?.hasUsableNetwork == false
|
||||||
val stopOnNoInternetEnabled =
|
val stopOnNoInternetEnabled =
|
||||||
autoTunnelRepository.flow.firstOrNull()?.isStopOnNoInternetEnabled == true
|
autoTunnelRepository.flow.firstOrNull()?.isStopOnNoInternetEnabled == true
|
||||||
|
|
||||||
if (stillNoInternet && stopOnNoInternetEnabled) {
|
if (stillNoUsableNetwork && stopOnNoInternetEnabled) {
|
||||||
val currentActiveIds =
|
val currentActiveIds =
|
||||||
tunnelCoordinator.backendStatus.value.activeTunnels.keys
|
tunnelCoordinator.backendStatus.value.activeTunnels.keys
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.ui.common.dialog
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LocalNetworkPermissionDialog(onDismiss: () -> Unit, onAttest: () -> Unit) {
|
||||||
|
InfoDialog(
|
||||||
|
onAttest = onAttest,
|
||||||
|
onDismiss = onDismiss,
|
||||||
|
title = stringResource(R.string.local_network_permission_title),
|
||||||
|
body = {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.local_network_permission_intro),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.local_network_permission_issues_intro),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.local_network_permission_feature_tunnels),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.local_network_permission_feature_autotunnel),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.local_network_permission_feature_proxy),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.local_network_permission_recommendation),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.local_network_permission_nearby_devices),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmText = stringResource(R.string._continue),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Filter1
|
import androidx.compose.material.icons.outlined.Filter1
|
||||||
import androidx.compose.material.icons.outlined.Map
|
import androidx.compose.material.icons.outlined.Map
|
||||||
|
import androidx.compose.material.icons.outlined.PublicOff
|
||||||
import androidx.compose.material.icons.outlined.WifiFind
|
import androidx.compose.material.icons.outlined.WifiFind
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -191,6 +192,21 @@ fun WifiSettingsScreen(viewModel: AutoTunnelViewModel = koinViewModel()) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
SurfaceRow(
|
||||||
|
leading = { Icon(Icons.Outlined.PublicOff, contentDescription = null) },
|
||||||
|
title = stringResource(R.string.stop_while_captive_portal),
|
||||||
|
onClick = {
|
||||||
|
viewModel.setDisabledOnCaptivePortal(
|
||||||
|
!uiState.autoTunnelSettings.disableTunnelOnCaptivePortal
|
||||||
|
)
|
||||||
|
},
|
||||||
|
trailing = {
|
||||||
|
ThemedSwitch(
|
||||||
|
checked = uiState.autoTunnelSettings.disableTunnelOnCaptivePortal,
|
||||||
|
onClick = { viewModel.setDisabledOnCaptivePortal(it) },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
GroupLabel(stringResource(R.string.tunnels), Modifier.padding(horizontal = 16.dp))
|
GroupLabel(stringResource(R.string.tunnels), Modifier.padding(horizontal = 16.dp))
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.ViewQuilt
|
import androidx.compose.material.icons.automirrored.outlined.ViewQuilt
|
||||||
import androidx.compose.material.icons.outlined.Android
|
import androidx.compose.material.icons.outlined.Android
|
||||||
|
import androidx.compose.material.icons.outlined.CellWifi
|
||||||
import androidx.compose.material.icons.outlined.Dns
|
import androidx.compose.material.icons.outlined.Dns
|
||||||
import androidx.compose.material.icons.outlined.ExpandMore
|
import androidx.compose.material.icons.outlined.ExpandMore
|
||||||
import androidx.compose.material.icons.outlined.MonitorHeart
|
import androidx.compose.material.icons.outlined.MonitorHeart
|
||||||
@@ -234,6 +235,23 @@ fun SettingsScreen(
|
|||||||
viewModel.setTunnelScriptedEnabled(!uiState.settings.tunnelScriptingEnabled)
|
viewModel.setTunnelScriptedEnabled(!uiState.settings.tunnelScriptingEnabled)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
SurfaceRow(
|
||||||
|
leading = { Icon(Icons.Outlined.CellWifi, contentDescription = null) },
|
||||||
|
title = stringResource(R.string.seamless_roaming),
|
||||||
|
trailing = { modifier ->
|
||||||
|
ThemedSwitch(
|
||||||
|
checked = uiState.settings.seamlessRoamingEnabled,
|
||||||
|
onClick = { viewModel.setSeamlessNetworkRoaming(enabled = it) },
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
description = {
|
||||||
|
DescriptionText(stringResource(R.string.seamless_roaming_description))
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
viewModel.setSeamlessNetworkRoaming(!uiState.settings.seamlessRoamingEnabled)
|
||||||
|
},
|
||||||
|
)
|
||||||
SurfaceRow(
|
SurfaceRow(
|
||||||
leading = { Icon(Icons.Outlined.MonitorHeart, null) },
|
leading = { Icon(Icons.Outlined.MonitorHeart, null) },
|
||||||
title = stringResource(R.string.tunnel_monitoring),
|
title = stringResource(R.string.tunnel_monitoring),
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.ui.screens.support.license
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.mikepenz.aboutlibraries.Libs
|
||||||
|
import com.mikepenz.aboutlibraries.entity.Developer
|
||||||
|
import com.mikepenz.aboutlibraries.entity.Library
|
||||||
|
import com.mikepenz.aboutlibraries.entity.License
|
||||||
|
import com.mikepenz.aboutlibraries.entity.Scm
|
||||||
|
import com.mikepenz.aboutlibraries.util.withContext
|
||||||
|
|
||||||
|
fun buildLibsWithAdditionalLibraries(context: Context): Libs {
|
||||||
|
val baseLibs = Libs.Builder().withContext(context).build()
|
||||||
|
|
||||||
|
val cleanedBaseLibs =
|
||||||
|
baseLibs.libraries.filterNot { library ->
|
||||||
|
library.uniqueId.contains("com.github.topjohnwu.libsu", ignoreCase = true) ||
|
||||||
|
library.uniqueId.contains("com.github.T8RIN.QuickieExtended", ignoreCase = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val nativeLibraries =
|
||||||
|
listOf(
|
||||||
|
Library(
|
||||||
|
uniqueId = "github.com.wgtunnel:amneziawg-go",
|
||||||
|
artifactVersion = "v0.0.0-20260618075902-e1b699b2104b",
|
||||||
|
name = "AmneziaWG Go (Fork)",
|
||||||
|
description = "WireGuard implementation with Amnezia obfuscation",
|
||||||
|
website = "https://wgtunnel.com",
|
||||||
|
developers =
|
||||||
|
listOf(
|
||||||
|
Developer(
|
||||||
|
name = "Zane Schepke (Fork Maintainer)",
|
||||||
|
organisationUrl = "https://wgtunnel.com",
|
||||||
|
),
|
||||||
|
Developer(
|
||||||
|
name = "Jason A. Donenfeld (Original WireGuard)",
|
||||||
|
organisationUrl = "https://www.wireguard.com/",
|
||||||
|
),
|
||||||
|
Developer(
|
||||||
|
name = "Amnezia VPN Team",
|
||||||
|
organisationUrl = "https://amnezia.org/",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
organization = null,
|
||||||
|
scm = Scm(null, null, "https://github.com/wgtunnel/amneziawg-go"),
|
||||||
|
licenses =
|
||||||
|
setOf(
|
||||||
|
License(
|
||||||
|
name = "MIT License",
|
||||||
|
url = "https://opensource.org/licenses/MIT",
|
||||||
|
spdxId = "MIT",
|
||||||
|
hash = "mit-license-amneziawg-fork",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
funding = emptySet(),
|
||||||
|
tag = "native",
|
||||||
|
),
|
||||||
|
Library(
|
||||||
|
uniqueId = "github.com.wgtunnel:wireproxy-awg",
|
||||||
|
artifactVersion = "v0.0.0-20260309043206-ff4200f20ff2",
|
||||||
|
name = "Wireproxy AWG (Fork)",
|
||||||
|
description = "WireGuard proxy with Amnezia support",
|
||||||
|
website = "https://wgtunnel.com",
|
||||||
|
developers =
|
||||||
|
listOf(
|
||||||
|
Developer(
|
||||||
|
name = "Zane Schepke (Fork Maintainer)",
|
||||||
|
organisationUrl = "https://wgtunnel.com",
|
||||||
|
),
|
||||||
|
Developer(name = "Artem Russkikh (Original)", organisationUrl = null),
|
||||||
|
),
|
||||||
|
organization = null,
|
||||||
|
scm = Scm(null, null, "https://github.com/wgtunnel/wireproxy-awg"),
|
||||||
|
licenses =
|
||||||
|
setOf(
|
||||||
|
License(
|
||||||
|
name = "MIT License",
|
||||||
|
url = "https://opensource.org/licenses/MIT",
|
||||||
|
spdxId = "MIT",
|
||||||
|
hash = "mit-license-wireproxy-fork",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
funding = emptySet(),
|
||||||
|
tag = "native",
|
||||||
|
),
|
||||||
|
Library(
|
||||||
|
uniqueId = "github.com.wgtunnel:go-socks5",
|
||||||
|
artifactVersion = "v0.0.0-20260307052555-86f8d93b9534",
|
||||||
|
name = "go-socks5 (Fork)",
|
||||||
|
description = "SOCKS5 proxy server implementation",
|
||||||
|
website = "https://wgtunnel.com",
|
||||||
|
developers =
|
||||||
|
listOf(
|
||||||
|
Developer(
|
||||||
|
name = "Zane Schepke (Fork Maintainer)",
|
||||||
|
organisationUrl = "https://wgtunnel.com",
|
||||||
|
),
|
||||||
|
Developer(name = "Things-go Team (Original)", organisationUrl = null),
|
||||||
|
),
|
||||||
|
organization = null,
|
||||||
|
scm = Scm(null, null, "https://github.com/wgtunnel/go-socks5"),
|
||||||
|
licenses =
|
||||||
|
setOf(
|
||||||
|
License(
|
||||||
|
name = "MIT License",
|
||||||
|
url = "https://opensource.org/licenses/MIT",
|
||||||
|
spdxId = "MIT",
|
||||||
|
hash = "mit-license-go-socks5-fork",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
funding = emptySet(),
|
||||||
|
tag = "native",
|
||||||
|
),
|
||||||
|
Library(
|
||||||
|
uniqueId = "github.com.miekg:dns",
|
||||||
|
artifactVersion = "v1.1.69",
|
||||||
|
name = "miekg/dns",
|
||||||
|
description = "DNS library for Go",
|
||||||
|
website = "https://github.com/miekg/dns",
|
||||||
|
developers = listOf(Developer(name = "Miek Gieben", organisationUrl = null)),
|
||||||
|
organization = null,
|
||||||
|
scm = Scm(null, null, "https://github.com/miekg/dns"),
|
||||||
|
licenses =
|
||||||
|
setOf(
|
||||||
|
License(
|
||||||
|
name = "BSD 3-Clause \"New\" or \"Revised\" License",
|
||||||
|
url = "https://opensource.org/licenses/BSD-3-Clause",
|
||||||
|
spdxId = "BSD-3-Clause",
|
||||||
|
hash = "bsd3-miekg-dns",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
funding = emptySet(),
|
||||||
|
tag = "go",
|
||||||
|
),
|
||||||
|
Library(
|
||||||
|
uniqueId = "github.com.heiher:hev-socks5-tunnel",
|
||||||
|
artifactVersion = "2.15.0",
|
||||||
|
name = "hev-socks5-tunnel",
|
||||||
|
description = "High performance SOCKS5 tunnel",
|
||||||
|
website = "https://github.com/heiher/hev-socks5-tunnel",
|
||||||
|
developers = listOf(Developer(name = "heiher", organisationUrl = null)),
|
||||||
|
organization = null,
|
||||||
|
scm = Scm(null, null, "https://github.com/heiher/hev-socks5-tunnel"),
|
||||||
|
licenses =
|
||||||
|
setOf(
|
||||||
|
License(
|
||||||
|
name = "MIT License",
|
||||||
|
url = "https://opensource.org/licenses/MIT",
|
||||||
|
spdxId = "MIT",
|
||||||
|
hash = "mit-license-hev",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
funding = emptySet(),
|
||||||
|
tag = "native",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val additionalLibraries =
|
||||||
|
listOf(
|
||||||
|
Library(
|
||||||
|
uniqueId = "com.github.T8RIN.QuickieExtended:quickie-foss",
|
||||||
|
artifactVersion = "1.18.1",
|
||||||
|
name = "QuickieFoss",
|
||||||
|
description = "Camera QR code scanner",
|
||||||
|
website = "https://github.com/T8RIN/QuickieExtended",
|
||||||
|
developers = listOf(Developer(name = "T8RIN", null)),
|
||||||
|
organization = null,
|
||||||
|
scm = Scm(null, null, "https://github.com/T8RIN/QuickieExtended"),
|
||||||
|
licenses =
|
||||||
|
setOf(
|
||||||
|
License(
|
||||||
|
name = "Apache License 2.0",
|
||||||
|
url = "https://www.apache.org/licenses/LICENSE-2.0",
|
||||||
|
spdxId = "Apache-2.0",
|
||||||
|
hash = "apache-2-quickie",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
funding = emptySet(),
|
||||||
|
tag = "ui",
|
||||||
|
),
|
||||||
|
Library(
|
||||||
|
uniqueId = "com.github.topjohnwu.libsu:core",
|
||||||
|
artifactVersion = "6.0.0",
|
||||||
|
name = "libsu",
|
||||||
|
description = "Root shell library for Android",
|
||||||
|
website = "https://github.com/topjohnwu/libsu",
|
||||||
|
developers = listOf(Developer(name = "topjohnwu", null)),
|
||||||
|
organization = null,
|
||||||
|
scm = Scm(null, null, "https://github.com/topjohnwu/libsu"),
|
||||||
|
licenses =
|
||||||
|
setOf(
|
||||||
|
License(
|
||||||
|
name = "Apache License 2.0",
|
||||||
|
url = "https://www.apache.org/licenses/LICENSE-2.0",
|
||||||
|
spdxId = "Apache-2.0",
|
||||||
|
hash = "apache-2-libsu",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
funding = emptySet(),
|
||||||
|
tag = "system",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return Libs(
|
||||||
|
libraries =
|
||||||
|
(cleanedBaseLibs + nativeLibraries + additionalLibraries).sortedBy {
|
||||||
|
it.name.lowercase()
|
||||||
|
},
|
||||||
|
licenses =
|
||||||
|
baseLibs.licenses +
|
||||||
|
nativeLibraries.flatMap { it.licenses } +
|
||||||
|
additionalLibraries.flatMap { it.licenses },
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class LicenseFileEntry(
|
|
||||||
val groupId: String,
|
|
||||||
val artifactId: String,
|
|
||||||
val version: String,
|
|
||||||
val name: String? = null,
|
|
||||||
val spdxLicenses: List<SpdxLicense> = emptyList(),
|
|
||||||
val scm: Scm? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable data class SpdxLicense(val identifier: String, val name: String, val url: String)
|
|
||||||
|
|
||||||
@Serializable data class Scm(val url: String)
|
|
||||||
@@ -1,30 +1,23 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.screens.support.license
|
package com.zaneschepke.wireguardautotunnel.ui.screens.support.license
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material3.CircularWavyProgressIndicator
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.runtime.Composable
|
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.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.license.components.LicenseList
|
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
|
||||||
import com.zaneschepke.wireguardautotunnel.viewmodel.LicenseViewModel
|
import com.mikepenz.aboutlibraries.ui.compose.variant.LibraryDetailMode
|
||||||
import org.koin.androidx.compose.koinViewModel
|
|
||||||
import org.orbitmvi.orbit.compose.collectAsState
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun LicenseScreen(viewModel: LicenseViewModel = koinViewModel()) {
|
fun LicenseScreen() {
|
||||||
val licenseUiState by viewModel.collectAsState()
|
val context = LocalContext.current
|
||||||
|
val libs = remember { buildLibsWithAdditionalLibraries(context) }
|
||||||
|
|
||||||
if (licenseUiState.isLoading) {
|
LibrariesContainer(
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
libraries = libs,
|
||||||
CircularWavyProgressIndicator(waveSpeed = 60.dp, modifier = Modifier.size(48.dp))
|
modifier = Modifier.fillMaxSize(),
|
||||||
}
|
detailMode = LibraryDetailMode.Sheet,
|
||||||
} else {
|
)
|
||||||
LicenseList(licenseUiState.licenses)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.screens.support.license.components
|
|
||||||
|
|
||||||
import LicenseFileEntry
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.automirrored.outlined.Launch
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.scrollbar
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun LicenseList(licenses: List<LicenseFileEntry>) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val lazyListState = rememberLazyListState()
|
|
||||||
|
|
||||||
LazyColumn(
|
|
||||||
modifier =
|
|
||||||
Modifier.fillMaxSize()
|
|
||||||
.scrollbar(
|
|
||||||
state = lazyListState.scrollIndicatorState,
|
|
||||||
orientation = Orientation.Vertical,
|
|
||||||
),
|
|
||||||
state = lazyListState,
|
|
||||||
) {
|
|
||||||
items(licenses) { entry ->
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier =
|
|
||||||
Modifier.clickable(enabled = entry.scm?.url != null) {
|
|
||||||
entry.scm?.url?.let { context.openWebUrl(it) }
|
|
||||||
}
|
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
|
||||||
) {
|
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
|
||||||
Text(
|
|
||||||
text = "${entry.artifactId} (${entry.version})",
|
|
||||||
style = MaterialTheme.typography.titleSmall,
|
|
||||||
)
|
|
||||||
|
|
||||||
entry.spdxLicenses.forEach { license ->
|
|
||||||
Text(
|
|
||||||
text = license.name,
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entry.scm?.url?.let { Icon(Icons.AutoMirrored.Outlined.Launch, null) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
|
||||||
|
|
||||||
import LicenseFileEntry
|
|
||||||
|
|
||||||
data class LicenseUiState(
|
|
||||||
val isLoading: Boolean = true,
|
|
||||||
val licenses: List<LicenseFileEntry> = emptyList(),
|
|
||||||
)
|
|
||||||
@@ -6,15 +6,11 @@ object Constants {
|
|||||||
const val BASE_LOG_FILE_NAME = "wg_tunnel_logs"
|
const val BASE_LOG_FILE_NAME = "wg_tunnel_logs"
|
||||||
|
|
||||||
const val VPN_SETTINGS_PACKAGE = "android.net.vpn.SETTINGS"
|
const val VPN_SETTINGS_PACKAGE = "android.net.vpn.SETTINGS"
|
||||||
const val SYSTEM_EXEMPT_SERVICE_TYPE_ID = 1 shl 10
|
|
||||||
const val SPECIAL_USE_SERVICE_TYPE_ID = 1 shl 30
|
const val SPECIAL_USE_SERVICE_TYPE_ID = 1 shl 30
|
||||||
|
|
||||||
const val QR_CODE_NAME_PROPERTY = "# Name ="
|
|
||||||
|
|
||||||
const val FDROID_FLAVOR = "fdroid"
|
const val FDROID_FLAVOR = "fdroid"
|
||||||
const val GOOGLE_PLAY_FLAVOR = "google"
|
const val GOOGLE_PLAY_FLAVOR = "google"
|
||||||
const val STANDALONE_FLAVOR = "standalone"
|
const val STANDALONE_FLAVOR = "standalone"
|
||||||
const val RELEASE = "release"
|
|
||||||
|
|
||||||
const val BASE_RELEASE_URL = "https://github.com/wgtunnel/wgtunnel/releases/tag/"
|
const val BASE_RELEASE_URL = "https://github.com/wgtunnel/wgtunnel/releases/tag/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ object DnsValidator {
|
|||||||
return Result.Valid
|
return Result.Valid
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateUdp(value: String): DnsValidator.Result {
|
private fun validateUdp(value: String): Result {
|
||||||
val parts = value.split(":")
|
val parts = value.split(":")
|
||||||
|
|
||||||
val host = parts.getOrNull(0)?.trim()
|
val host = parts.getOrNull(0)?.trim()
|
||||||
@@ -93,14 +93,14 @@ object DnsValidator {
|
|||||||
|
|
||||||
// basic IP/hostname sanity check
|
// basic IP/hostname sanity check
|
||||||
if (!isValidHostOrIp(host)) {
|
if (!isValidHostOrIp(host)) {
|
||||||
return DnsValidator.Result.Invalid(DnsError.InvalidIpOrHost)
|
return Result.Invalid(DnsError.InvalidIpOrHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port !in 1..65535) {
|
if (port !in 1..65535) {
|
||||||
return DnsValidator.Result.Invalid(DnsError.InvalidPort)
|
return Result.Invalid(DnsError.InvalidPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
return DnsValidator.Result.Valid
|
return Result.Valid
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isValidHostOrIp(value: String): Boolean {
|
private fun isValidHostOrIp(value: String): Boolean {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.util
|
package com.zaneschepke.wireguardautotunnel.util
|
||||||
|
|
||||||
import LicenseFileEntry
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
@@ -23,7 +22,6 @@ import java.util.zip.ZipInputStream
|
|||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class FileUtils(private val context: Context, private val ioDispatcher: CoroutineDispatcher) {
|
class FileUtils(private val context: Context, private val ioDispatcher: CoroutineDispatcher) {
|
||||||
@@ -293,15 +291,6 @@ class FileUtils(private val context: Context, private val ioDispatcher: Coroutin
|
|||||||
return@withContext itemUri
|
return@withContext itemUri
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun readLibraryLicensesFromAssets(): List<LicenseFileEntry> =
|
|
||||||
withContext(ioDispatcher) {
|
|
||||||
val json = Json { ignoreUnknownKeys = true }
|
|
||||||
|
|
||||||
val jsonResult =
|
|
||||||
context.assets.open("licenses.json").bufferedReader().use { it.readText() }
|
|
||||||
json.decodeFromString(jsonResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CONF_FILE_EXTENSION = ".conf"
|
const val CONF_FILE_EXTENSION = ".conf"
|
||||||
const val ZIP_FILE_EXTENSION = ".zip"
|
const val ZIP_FILE_EXTENSION = ".zip"
|
||||||
|
|||||||
@@ -1,22 +1,9 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.util
|
package com.zaneschepke.wireguardautotunnel.util
|
||||||
|
|
||||||
import com.vdurmont.semver4j.Semver
|
import com.vdurmont.semver4j.Semver
|
||||||
import java.math.BigDecimal
|
|
||||||
import kotlin.math.pow
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
object NumberUtils {
|
object NumberUtils {
|
||||||
private const val BYTES_IN_KB = 1024.0
|
|
||||||
private val BYTES_IN_MB = BYTES_IN_KB.pow(2.0)
|
|
||||||
private val keyValidationRegex = """^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw480]=${'$'}""".toRegex()
|
|
||||||
|
|
||||||
fun bytesToMB(bytes: Long): BigDecimal {
|
|
||||||
return bytes.toBigDecimal().divide(BYTES_IN_MB.toBigDecimal())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isValidKey(key: String): Boolean {
|
|
||||||
return key.matches(keyValidationRegex)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun generateRandomTunnelName(): String {
|
fun generateRandomTunnelName(): String {
|
||||||
return "tunnel${randomFive()}"
|
return "tunnel${randomFive()}"
|
||||||
|
|||||||
@@ -1,26 +1,18 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
package com.zaneschepke.wireguardautotunnel.util.extensions
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Context.POWER_SERVICE
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.content.pm.PackageInfo
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.PowerManager
|
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.service.quicksettings.TileService
|
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.MainActivity
|
import com.zaneschepke.wireguardautotunnel.MainActivity
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tile.AutoTunnelControlTile
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tile.TunnelControlTile
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.splittunnel.state.TunnelApp
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -36,11 +28,6 @@ fun Context.openWebUrl(url: String): Result<Unit> = runCatching {
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.isBatteryOptimizationsDisabled(): Boolean {
|
|
||||||
val pm = getSystemService(POWER_SERVICE) as PowerManager
|
|
||||||
return pm.isIgnoringBatteryOptimizations(packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.launchNotificationSettings() {
|
fun Context.launchNotificationSettings() {
|
||||||
if (isRunningOnTv()) return launchAppSettings()
|
if (isRunningOnTv()) return launchAppSettings()
|
||||||
val settingsIntent: Intent =
|
val settingsIntent: Intent =
|
||||||
@@ -87,21 +74,6 @@ fun Context.hasSAFSupport(mimeType: String): Boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.launchShareFile(file: File) {
|
|
||||||
FileProvider.getUriForFile(this, getString(R.string.provider), file)
|
|
||||||
val shareIntent =
|
|
||||||
Intent().apply {
|
|
||||||
action = Intent.ACTION_SEND
|
|
||||||
type = FileUtils.ALL_FILE_TYPES
|
|
||||||
putExtra(Intent.EXTRA_STREAM, file)
|
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
val chooserIntent =
|
|
||||||
Intent.createChooser(shareIntent, "").apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
|
|
||||||
this.startActivity(chooserIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.launchSupportEmail(): Result<Unit> = runCatching {
|
fun Context.launchSupportEmail(): Result<Unit> = runCatching {
|
||||||
val intent =
|
val intent =
|
||||||
Intent(Intent.ACTION_SENDTO).apply {
|
Intent(Intent.ACTION_SENDTO).apply {
|
||||||
@@ -128,7 +100,7 @@ fun Context.isRunningOnTv(): Boolean {
|
|||||||
fun Context.launchVpnSettings(): Result<Unit> {
|
fun Context.launchVpnSettings(): Result<Unit> {
|
||||||
return kotlin.runCatching {
|
return kotlin.runCatching {
|
||||||
val intent =
|
val intent =
|
||||||
Intent(Constants.VPN_SETTINGS_PACKAGE).apply { setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
|
Intent(Constants.VPN_SETTINGS_PACKAGE).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,14 +119,6 @@ fun Context.launchLocationServicesSettings(): Result<Unit> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.launchSettings(): Result<Unit> {
|
|
||||||
return kotlin.runCatching {
|
|
||||||
val intent =
|
|
||||||
Intent(Settings.ACTION_SETTINGS).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.launchAppSettings() {
|
fun Context.launchAppSettings() {
|
||||||
kotlin
|
kotlin
|
||||||
.runCatching {
|
.runCatching {
|
||||||
@@ -171,48 +135,6 @@ fun Context.launchAppSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.requestTunnelTileServiceStateUpdate() =
|
|
||||||
runCatching {
|
|
||||||
TileService.requestListeningState(
|
|
||||||
this,
|
|
||||||
ComponentName(this, TunnelControlTile::class.java),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.onFailure { Timber.w(it) }
|
|
||||||
|
|
||||||
fun Context.requestAutoTunnelTileServiceUpdate() =
|
|
||||||
runCatching {
|
|
||||||
TileService.requestListeningState(
|
|
||||||
this,
|
|
||||||
ComponentName(this, AutoTunnelControlTile::class.java),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.onFailure { Timber.w(it) }
|
|
||||||
|
|
||||||
fun Context.getAllInternetCapablePackages(): List<PackageInfo> {
|
|
||||||
val permissions = arrayOf(Manifest.permission.INTERNET)
|
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
packageManager.getPackagesHoldingPermissions(
|
|
||||||
permissions,
|
|
||||||
PackageManager.PackageInfoFlags.of(0L),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
packageManager.getPackagesHoldingPermissions(permissions, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.getSplitTunnelApps(): List<TunnelApp> {
|
|
||||||
val packages = getAllInternetCapablePackages()
|
|
||||||
return packages
|
|
||||||
.filter { it.applicationInfo != null }
|
|
||||||
.map { pkg ->
|
|
||||||
TunnelApp(
|
|
||||||
packageManager.getApplicationLabel(pkg.applicationInfo!!).toString(),
|
|
||||||
pkg.packageName,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.canInstallPackages(): Boolean {
|
fun Context.canInstallPackages(): Boolean {
|
||||||
return packageManager.canRequestPackageInstalls()
|
return packageManager.canRequestPackageInstalls()
|
||||||
}
|
}
|
||||||
@@ -227,7 +149,7 @@ fun Context.requestInstallPackagesPermission() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Context.installApk(apkFile: File) {
|
fun Context.installApk(apkFile: File) {
|
||||||
val apkUri = FileProvider.getUriForFile(this, getString(R.string.provider), apkFile)
|
val apkUri = FileProvider.getUriForFile(this, BuildConfig.FILE_PROVIDER_AUTHORITY, apkFile)
|
||||||
val intent =
|
val intent =
|
||||||
Intent(Intent.ACTION_VIEW).apply {
|
Intent(Intent.ACTION_VIEW).apply {
|
||||||
setDataAndType(apkUri, "application/vnd.android.package-archive")
|
setDataAndType(apkUri, "application/vnd.android.package-archive")
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
|
|
||||||
fun <K, V> Flow<Map<K, V>>.distinctByKeys(): Flow<Map<K, V>> {
|
|
||||||
return distinctUntilChanged { old, new -> old.keys == new.keys }
|
|
||||||
}
|
|
||||||
@@ -18,10 +18,6 @@ fun <T, R : Comparable<R>> List<T>.isSortedBy(selector: (T) -> R): Boolean {
|
|||||||
return zipWithNext().all { (a, b) -> selector(a) <= selector(b) }
|
return zipWithNext().all { (a, b) -> selector(a) <= selector(b) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Int.toMillis(): Long {
|
|
||||||
return this * 1_000L
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Double.round(decimals: Int): Double {
|
fun Double.round(decimals: Int): Double {
|
||||||
val factor = 10.0.pow(decimals)
|
val factor = 10.0.pow(decimals)
|
||||||
return (this * factor).roundToInt() / factor
|
return (this * factor).roundToInt() / factor
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.zaneschepke.wireguardautotunnel.util.permission
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
|
object LocalNetworkPermissionHelper {
|
||||||
|
|
||||||
|
fun shouldRequestPermission(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPermissionGranted(context: Context): Boolean {
|
||||||
|
return if (shouldRequestPermission()) {
|
||||||
|
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_LOCAL_NETWORK) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,6 +145,10 @@ class AutoTunnelViewModel(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setDisabledOnCaptivePortal(enabled: Boolean) = intent {
|
||||||
|
autoTunnelRepository.updateDisableOnCaptivePortal(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
fun removeTunnelNetwork(tunnel: TunnelConfig, ssid: String) = intent {
|
fun removeTunnelNetwork(tunnel: TunnelConfig, ssid: String) = intent {
|
||||||
tunnelsRepository.save(
|
tunnelsRepository.save(
|
||||||
tunnel.copy(
|
tunnel.copy(
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.viewmodel
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.state.LicenseUiState
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import org.orbitmvi.orbit.ContainerHost
|
|
||||||
import org.orbitmvi.orbit.viewmodel.container
|
|
||||||
|
|
||||||
class LicenseViewModel(private val fileUtils: FileUtils) :
|
|
||||||
ContainerHost<LicenseUiState, Nothing>, ViewModel() {
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
override val container =
|
|
||||||
container<LicenseUiState, Nothing>(
|
|
||||||
LicenseUiState(),
|
|
||||||
buildSettings = { repeatOnSubscribedStopTimeout = 5000L },
|
|
||||||
) {
|
|
||||||
intent {
|
|
||||||
val licenses = fileUtils.readLibraryLicensesFromAssets()
|
|
||||||
reduce { state.copy(isLoading = false, licenses = licenses) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import com.dokar.sonner.ToastType
|
import com.dokar.sonner.ToastType
|
||||||
import com.zaneschepke.tunnel.util.RootShell
|
import com.zaneschepke.tunnel.util.RootShell
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelBackendCoordinator
|
||||||
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelCoordinator
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelCoordinator
|
||||||
import com.zaneschepke.wireguardautotunnel.core.shortcut.ShortcutManager
|
import com.zaneschepke.wireguardautotunnel.core.shortcut.ShortcutManager
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
||||||
@@ -27,6 +28,7 @@ class SettingsViewModel(
|
|||||||
private val monitoringRepository: MonitoringSettingsRepository,
|
private val monitoringRepository: MonitoringSettingsRepository,
|
||||||
private val globalEffectRepository: GlobalEffectRepository,
|
private val globalEffectRepository: GlobalEffectRepository,
|
||||||
private val tunnelCoordinator: TunnelCoordinator,
|
private val tunnelCoordinator: TunnelCoordinator,
|
||||||
|
private val tunnelBackendCoordinator: TunnelBackendCoordinator,
|
||||||
) : ContainerHost<SettingUiState, Nothing>, ViewModel() {
|
) : ContainerHost<SettingUiState, Nothing>, ViewModel() {
|
||||||
|
|
||||||
override val container =
|
override val container =
|
||||||
@@ -114,6 +116,10 @@ class SettingsViewModel(
|
|||||||
settingsRepository.upsert(state.settings.copy(tunnelScriptingEnabled = to))
|
settingsRepository.upsert(state.settings.copy(tunnelScriptingEnabled = to))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setSeamlessNetworkRoaming(enabled: Boolean) = intent {
|
||||||
|
tunnelBackendCoordinator.changeSeamlessRoaming(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
fun setAlreadyDonated(to: Boolean) = intent {
|
fun setAlreadyDonated(to: Boolean) = intent {
|
||||||
settingsRepository.upsert(state.settings.copy(alreadyDonated = to))
|
settingsRepository.upsert(state.settings.copy(alreadyDonated = to))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.dokar.sonner.ToastType
|
import com.dokar.sonner.ToastType
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelBackendCoordinator
|
||||||
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelCoordinator
|
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelCoordinator
|
||||||
import com.zaneschepke.wireguardautotunnel.core.orchestration.TunnelModeCoordinator
|
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode
|
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
||||||
@@ -61,7 +61,7 @@ class SharedAppViewModel(
|
|||||||
private val settingsRepository: GeneralSettingRepository,
|
private val settingsRepository: GeneralSettingRepository,
|
||||||
private val autoTunnelStateHolder: AutoTunnelStateHolder,
|
private val autoTunnelStateHolder: AutoTunnelStateHolder,
|
||||||
private val selectedTunnelsRepository: SelectedTunnelsRepository,
|
private val selectedTunnelsRepository: SelectedTunnelsRepository,
|
||||||
private val tunnelModeCoordinator: TunnelModeCoordinator,
|
private val tunnelBackendCoordinator: TunnelBackendCoordinator,
|
||||||
private val httpClient: HttpClient,
|
private val httpClient: HttpClient,
|
||||||
private val fileUtils: FileUtils,
|
private val fileUtils: FileUtils,
|
||||||
private val networkUtils: NetworkUtils,
|
private val networkUtils: NetworkUtils,
|
||||||
@@ -172,7 +172,7 @@ class SharedAppViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tunnelModeCoordinator.changeMode(mode)
|
tunnelBackendCoordinator.changeMode(mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setShouldShowDonationSnackbar(to: Boolean) = intent {
|
fun setShouldShowDonationSnackbar(to: Boolean) = intent {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="#FEFEFE" android:pathData="M654.6,237.7C658.8,237.7 663.1,237.7 667.4,237.7C677.4,237.7 687.5,237.7 697.6,237.7C707.9,237.7 718.2,237.7 728.6,237.7C737.5,237.6 746.4,237.6 755.4,237.6C760.7,237.6 766,237.6 771.3,237.6C776.3,237.6 781.3,237.6 786.3,237.6C788.1,237.6 789.9,237.6 791.7,237.6C807.6,237.5 826.2,238.6 838.3,250.2C849.7,262.5 857,276.4 856.4,293.7C855.7,300 853.9,306 852,312C851.7,313.1 851.3,314.1 851,315.3C845.5,331.6 837.9,347.2 830.5,362.7C825.4,373.2 821,383.7 817.2,394.7C815.8,398.5 814.2,402 812.5,405.6C810.4,410.4 808.5,415.1 806.5,419.9C806.1,420.9 805.7,421.9 805.3,422.9C804.5,424.8 803.8,426.7 803,428.6C799.2,438 795.1,447.3 791.1,456.6C787.3,465.3 783.5,474.1 779.8,483C777.8,487.9 775.7,492.8 773.4,497.6C771.5,501.8 769.8,506 768.1,510.3C765.8,516 763.2,521.7 760.8,527.4C757,536.1 753.2,544.8 749.6,553.5C746.9,560 743.9,566.3 740.7,572.6C736.3,581.3 732.6,590.3 729.1,599.4C728.8,600.4 728.8,600.4 728.4,601.4C727.1,604.8 725.8,608.1 724.5,611.5C721.1,620.5 717.2,629.3 713,638C711.6,641.1 710.2,644.1 708.8,647.2C708.4,648 708.1,648.8 707.7,649.7C704.4,656.9 701.3,664.2 698.2,671.6C690.6,689.4 682.8,707.1 674.6,724.6C673.2,727.6 671.8,730.7 670.5,733.8C661.2,755.7 650.6,777.3 627.3,787.1C619.6,789.9 612.7,790.6 604.4,790.5C603.3,790.5 602.2,790.5 601,790.5C589.3,790.5 580.2,787.1 571,780C570.3,779.4 569.5,778.9 568.8,778.3C557.3,768.9 551.3,756.1 545.3,742.9C543,737.9 540.7,732.8 538.3,727.8C537.5,726 536.7,724.2 535.8,722.4C533,716.3 530.1,710.2 527.2,704.1C523.7,696.8 520.4,689.5 517.5,682C516,678.3 514.4,674.7 512.6,671.2C509.4,665 507,658.5 504.6,651.9C502.5,646.6 500.3,641.4 497.7,636.3C493.6,628.4 490.2,620.2 486.9,611.9C486.3,610.4 485.7,608.9 485.1,607.5C483.9,604.6 482.8,601.8 481.6,598.9C477.4,588.5 473,578.2 468.1,568.1C460.7,552.6 454.6,536.7 448.8,520.7C445.8,512.5 442,504.8 438,497.1C432.2,485.5 427.2,473.8 422.8,461.7C420.1,454.6 417.1,447.7 413.8,440.9C411,435 408.4,428.9 405.8,422.9C404.9,420.9 404,418.8 403.1,416.8C399.9,409.5 396.9,402.3 394.1,394.9C392.6,390.9 390.9,387 389.1,383.1C388.8,382.4 388.4,381.6 388.1,380.8C385.6,375.3 383,369.8 380.4,364.3C378.1,359.3 376.1,354.2 374.2,349C373.1,346.3 371.9,343.8 370.6,341.3C367.3,334.9 365.1,328.1 362.8,321.3C362,319 361.1,316.7 360.3,314.5C354.4,298.6 354.5,284.7 361,269C365.9,258.7 375.2,249.3 385.8,244.7C399.3,240.2 417.7,237.5 431,244C455.6,257.2 466.2,288.1 475.7,312.7C477.3,316.9 479.2,320.8 481.1,324.8C487.7,338.7 493.6,352.9 499.3,367.2C503,376.4 507,385.4 511.3,394.3C517.4,407.2 522.6,420.3 527.7,433.6C531,442.1 534.5,450.5 538.5,458.8C540.3,462.6 541.9,466.5 543.5,470.5C545.9,476.3 548.4,482 551.1,487.7C554.6,495.1 557.7,502.6 560.8,510.1C563.6,516.7 566.5,523.3 569.4,529.8C573.7,539.2 577.6,548.7 581.6,558.2C585.6,568 589.7,577.7 593.9,587.4C595.9,592.2 598,596.9 600,601.6C600.4,602.5 600.8,603.4 601.2,604.3C601.5,605.2 601.9,606 602.3,606.9C602.6,607.6 602.9,608.3 603.2,609.1C603.9,610.7 604.4,612.3 605,614C607.7,608.6 610.3,603.2 612.7,597.7C613.1,597 613.4,596.2 613.8,595.4C617.7,586.5 621.4,577.4 625.1,568.4C631.8,551.7 638.9,535.2 646.4,518.9C649.1,513 651.6,507.1 654,501C657.6,491.9 661.3,482.8 665.1,473.7C665.5,472.7 665.9,471.7 666.3,470.7C668.3,465.9 670.3,461.3 672.7,456.7C674.7,452.8 676.1,449 677.4,444.8C679.4,438.4 682.1,432.6 685.2,426.7C687.2,422.6 688.9,418.5 690.6,414.3C692.8,408.9 695.1,403.7 697.7,398.6C700.3,393.3 702.6,387.8 705,382.4C706.1,379.8 707.2,377.2 708.4,374.6C709.2,372.8 710,370.9 710.8,369.1C711.2,368.2 711.6,367.3 712,366.4C715.1,359.2 717.7,352.9 716,345C714.8,345 713.6,345 712.4,345C701.2,344.9 690,344.8 678.7,344.7C673,344.6 667.2,344.6 661.4,344.5C617.2,344.1 617.2,344.1 600.4,343.6C599.3,343.5 598.2,343.5 597.1,343.5C577.7,342.7 563.5,336.2 550.1,322C544.3,315 540.5,305.1 540,296C539.9,295.3 539.9,294.5 539.8,293.8C539.3,280.1 544.1,267.9 553.3,257.8C578.1,231.3 621.5,237.7 654.6,237.7Z"/>
|
||||||
|
|
||||||
|
<path android:fillColor="#FEFEFE" android:pathData="M253,250C253.7,250.5 254.4,250.9 255.1,251.4C269.1,262.1 275.9,283.6 282.8,299.1C284.1,302 285.4,304.8 286.7,307.7C291.1,316.8 294.9,326.1 298.7,335.5C301.8,343.2 305.1,350.9 308.4,358.5C308.8,359.4 309.2,360.3 309.6,361.2C311.5,365.6 313.5,370 315.5,374.4C318.1,380.2 320.6,386 323,391.9C323.6,393.4 323.6,393.4 324.3,394.9C326.4,400 328.5,405.2 330.4,410.5C332.9,417 335.9,423.2 339,429.4C345.8,443.2 351.7,457.2 357.5,471.4C361,479.9 364.6,488.3 368.5,496.6C369.1,497.8 369.7,499.1 370.3,500.3C371.4,502.7 372.5,505 373.6,507.4C374.1,508.5 374.6,509.5 375.1,510.6C375.6,511.5 376,512.5 376.4,513.4C378.3,517.8 380,522.2 381.7,526.6C383.7,531.7 385.8,536.8 387.9,541.8C388.4,542.9 388.9,544 389.3,545.1C392,551.4 394.7,557.7 397.5,564C397.8,564.7 398.1,565.3 398.4,566C398.9,567 398.9,567 399.3,568.1C399.9,569.4 400.5,570.8 401.1,572.2C401.4,572.9 401.7,573.5 402,574.2C406.2,583.8 410.2,593.4 413.9,603.1C416.1,608.9 418.6,614.5 421.2,620.1C424.6,627.2 427.7,634.3 430.9,641.5C431.3,642.5 431.3,642.5 431.8,643.6C436.1,653.4 440.2,663.3 444,673.4C446.5,679.7 449.2,685.9 452.1,692C472.4,734.7 472.4,734.7 465.3,755.8C459.9,769.4 451,781 437.5,787.2C436.4,787.7 435.3,788.2 434.2,788.7C422.2,793.5 408.1,791.5 396.4,787C369.2,774.6 356.6,742.9 346.5,716.7C344.7,712.4 342.8,708.2 340.8,704C333,687.8 326,671.2 319,654.6C316.1,647.6 313.1,640.6 310.1,633.7C307.7,628.1 305.4,622.5 303.3,616.8C301.6,612.4 299.7,608.2 297.5,604C291.1,591.1 285.6,577.7 280,564.4C277.2,557.7 274.3,551.1 271.4,544.4C268.9,538.9 266.7,533.3 264.6,527.7C263.4,524.8 262,522 260.6,519.2C258,514.1 255.8,508.9 253.7,503.6C252.9,501.6 252.1,499.7 251.3,497.8C250.9,496.8 250.6,495.8 250.2,494.8C248.4,490.6 246.7,486.3 244.9,482.1C244.6,481.4 244.3,480.7 244,480C242.3,475.9 240.5,471.9 238.6,467.9C234.7,459.7 231.2,451.4 227.8,443.1C226.9,440.9 226,438.7 225.1,436.5C222.4,430 219.8,423.5 217.3,417C215.4,411.9 213.2,407 210.8,402.1C203.8,387.5 197.4,372.6 191.3,357.6C189.5,353.1 187.5,348.6 185.4,344.1C182.4,337.5 179.7,330.9 176.9,324.3C176.4,322.9 175.8,321.5 175.2,320.1C168.5,304.1 166.4,288.4 173,272C179.5,258.3 189.7,249.1 203.7,243.7C220.2,238.1 238.5,240.3 253,250Z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_banner_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_banner_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
|
||||||
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
|
|
||||||
</adaptive-icon>
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:keep="@raw/aboutlibraries" />
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<color name="ic_banner_background">#21272A</color>
|
||||||
|
</resources>
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources></resources>
|
|
||||||
@@ -537,4 +537,22 @@
|
|||||||
<string name="restore_failed_wrong_password">Restore failed. Wrong password</string>
|
<string name="restore_failed_wrong_password">Restore failed. Wrong password</string>
|
||||||
<string name="restore_failed_invalid_file">Restore failed. Select a valid backup file (.sqlite3 or .sqlite3.aes)</string>
|
<string name="restore_failed_invalid_file">Restore failed. Select a valid backup file (.sqlite3 or .sqlite3.aes)</string>
|
||||||
<string name="error_invalid_config_url">This link returned an invalid config file. Make sure you are using a direct download link</string>
|
<string name="error_invalid_config_url">This link returned an invalid config file. Make sure you are using a direct download link</string>
|
||||||
|
|
||||||
|
<string name="local_network_permission_title">Local Network Access Needed</string>
|
||||||
|
|
||||||
|
<string name="local_network_permission_intro">WG Tunnel needs access to your local network for several features to work properly.</string>
|
||||||
|
|
||||||
|
<string name="local_network_permission_issues_intro">Without this permission, you may experience issues with:</string>
|
||||||
|
|
||||||
|
<string name="local_network_permission_feature_tunnels">- Connection issues with split tunneling, LAN bypass, or servers hosted on your local network</string>
|
||||||
|
<string name="local_network_permission_feature_autotunnel">- Auto-tunneling and split tunneling features</string>
|
||||||
|
<string name="local_network_permission_feature_proxy">- Local proxy and bypass functionality</string>
|
||||||
|
|
||||||
|
<string name="local_network_permission_recommendation">Granting this permission is strongly recommended.</string>
|
||||||
|
<string name="local_network_permission_nearby_devices">Note: Android labels this permission as “nearby devices”.</string>
|
||||||
|
|
||||||
|
<string name="local_network_permission_denied">Local network access denied. Some features may not work properly</string>
|
||||||
|
<string name="stop_while_captive_portal">Stop tunnel while captive portal is present</string>
|
||||||
|
<string name="seamless_roaming">Seamless roaming</string>
|
||||||
|
<string name="seamless_roaming_description">Bind tunnels to the active network to improve reliability on network handoffs</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 871 B |
|
Before Width: | Height: | Size: 762 B |
|
Before Width: | Height: | Size: 735 B After Width: | Height: | Size: 629 B |
|
Before Width: | Height: | Size: 576 B |
|
Before Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 839 B |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="512" android:viewportWidth="512" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="#FEFEFE" android:pathData="M387.85,105.96C390.08,105.99 392.31,106.01 394.54,106.02C425.06,106.26 425.06,106.26 435.06,115.31C440.67,121.68 443.41,128.87 443.28,137.35C442.12,148.45 436.55,159.27 432.19,169.44C431.49,171.08 430.79,172.73 430.09,174.37C428.56,177.97 427.03,181.57 425.49,185.17C422.88,191.28 420.28,197.41 417.68,203.53C353.09,355.51 353.09,355.51 338.06,385.06C337.46,386.28 337.46,386.28 336.85,387.51C331.72,397.31 324.43,403.52 314,407C305.35,407.63 297.48,407.88 290,403C289.35,402.61 288.7,402.21 288.03,401.81C267.22,386.65 255.93,343.57 246.12,320.63C243.84,315.3 241.56,309.97 239.27,304.64C235.16,295.07 231.06,285.5 226.96,275.93C216,250.31 216,250.31 205,224.7C195.27,202.04 185.54,179.37 176.31,156.5C176,155.72 175.68,154.95 175.36,154.15C170.97,143.16 169.24,134.79 173.75,123.58C177.5,115.83 183.04,112.05 191,109C199.93,106.88 206.75,108.16 215,112C223.45,117.42 227.57,127.73 231.75,136.56C232.2,137.51 232.65,138.46 233.12,139.44C244.41,163.45 254.74,187.93 265.15,212.33C268.22,219.52 271.33,226.7 274.44,233.88C274.98,235.13 275.53,236.39 276.07,237.65C278.53,243.32 281,248.99 283.47,254.66C289.07,267.5 294.62,280.36 300,293.3C300.54,294.59 301.08,295.88 301.62,297.17C302.36,298.95 303.1,300.73 303.83,302.52C304.25,303.53 304.67,304.54 305.1,305.59C306,308 306,308 306,310C306.66,310 307.32,310 308,310C308.17,309.38 308.35,308.77 308.52,308.13C310.24,302.19 312.3,296.56 314.77,290.89C315.13,290.05 315.49,289.22 315.86,288.36C317.03,285.65 318.2,282.95 319.38,280.25C320.19,278.35 321.01,276.46 321.83,274.56C323.85,269.88 325.88,265.2 327.9,260.53C329.44,256.98 330.98,253.42 332.52,249.87C334.58,245.1 336.65,240.34 338.71,235.57C341.15,229.96 343.58,224.35 346,218.74C346.35,217.95 346.69,217.15 347.05,216.33C348.78,212.33 350.51,208.33 352.23,204.32C355.22,197.41 358.21,190.5 361.24,183.61C361.71,182.53 362.19,181.45 362.67,180.34C363.53,178.38 364.39,176.42 365.26,174.47C365.63,173.63 366,172.79 366.38,171.93C366.85,170.85 366.85,170.85 367.34,169.75C368.12,168 368.12,168 368,166C366.76,165.99 366.76,165.99 365.49,165.98C357.63,165.9 349.78,165.81 341.92,165.71C337.88,165.66 333.84,165.62 329.81,165.58C325.9,165.54 322,165.49 318.1,165.44C316.62,165.42 315.13,165.41 313.65,165.4C300.1,165.29 288.99,164.82 278.75,155.19C273.15,149.32 271.03,143.02 271,135C271.65,126.87 275.22,120.31 281.34,114.95C296.39,103.76 318.78,106.75 336.69,106.44C338.39,106.41 338.39,106.41 340.12,106.37C373.39,105.77 373.39,105.77 387.85,105.96Z"/>
|
||||||
|
|
||||||
|
<path android:fillColor="#FEFEFE" android:pathData="M114,113C120.76,117.8 124.22,123.2 127.69,130.63C128.19,131.68 128.7,132.73 129.22,133.81C130.84,137.2 132.43,140.59 134,144C134.5,145.08 135,146.16 135.52,147.28C144.69,167.16 153.19,187.35 161.79,207.48C165.04,215.1 168.33,222.71 171.63,230.31C172.14,231.49 172.65,232.67 173.18,233.89C175.39,239 177.61,244.1 179.83,249.21C189.5,271.38 198.88,293.66 208.11,316.01C211.04,323.11 214.01,330.18 217,337.25C217.43,338.26 217.86,339.28 218.3,340.32C220.39,345.24 222.5,350.16 224.63,355.07C225.05,356.04 225.47,357.01 225.91,358C226.69,359.81 227.48,361.62 228.27,363.42C232.37,372.86 233.7,381.34 229.94,391.19C226.56,398.54 222.59,403.08 215.19,406.44C207.09,409.27 200.15,408.78 192.38,405.23C182.8,400.26 178.73,393.43 174,384C173.52,383.06 173.05,382.12 172.56,381.15C162.64,361.39 153.83,341.17 145.1,320.88C143.13,316.31 141.16,311.74 139.17,307.18C130.23,286.6 121.43,265.97 112.65,245.32C107.35,232.86 102.04,220.41 96.69,207.98C90.69,194.05 84.73,180.11 78.92,166.1C78.32,164.64 77.71,163.18 77.1,161.72C67.42,138.54 67.42,138.54 71.56,126.94C74.89,119.43 79.6,114.1 87.11,110.69C96.25,107.35 105.69,107.97 114,113Z"/>
|
||||||
|
|
||||||
|
<path android:fillColor="#FEFEFE" android:pathData="M324,178C331.04,182.91 336.31,189.42 338,198C338.98,206.07 336.97,213.6 332.42,220.34C327.99,225.71 322.98,229.65 316,231C306.84,231.81 299.67,231.33 292,226C289.13,223.5 289.13,223.5 287,221C286.28,220.2 285.56,219.39 284.81,218.56C279.84,211.53 280.29,203.26 281,195C281.85,191.88 283.09,189.59 285,187C285.42,186.39 285.85,185.78 286.29,185.14C295.31,173.35 311.34,170.15 324,178Z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_banner_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_banner_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
|
||||||
</adaptive-icon>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 942 B |
|
Before Width: | Height: | Size: 1000 B After Width: | Height: | Size: 1012 B |
|
After Width: | Height: | Size: 1012 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.1 KiB |