diff --git a/app/schemas/com.zaneschepke.wireguardautotunnel.data.AppDatabase/11.json b/app/schemas/com.zaneschepke.wireguardautotunnel.data.AppDatabase/11.json
new file mode 100644
index 0000000..a477649
--- /dev/null
+++ b/app/schemas/com.zaneschepke.wireguardautotunnel.data.AppDatabase/11.json
@@ -0,0 +1,232 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 11,
+ "identityHash": "4c9418386f72dfac5d28ab96c1e5ea0b",
+ "entities": [
+ {
+ "tableName": "Settings",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_tunnel_enabled` INTEGER NOT NULL, `is_tunnel_on_mobile_data_enabled` INTEGER NOT NULL, `trusted_network_ssids` TEXT NOT NULL, `is_always_on_vpn_enabled` INTEGER NOT NULL, `is_tunnel_on_ethernet_enabled` INTEGER NOT NULL, `is_shortcuts_enabled` INTEGER NOT NULL DEFAULT false, `is_tunnel_on_wifi_enabled` INTEGER NOT NULL DEFAULT false, `is_kernel_enabled` INTEGER NOT NULL DEFAULT false, `is_restore_on_boot_enabled` INTEGER NOT NULL DEFAULT false, `is_multi_tunnel_enabled` INTEGER NOT NULL DEFAULT false, `is_ping_enabled` INTEGER NOT NULL DEFAULT false, `is_amnezia_enabled` INTEGER NOT NULL DEFAULT false, `is_wildcards_enabled` INTEGER NOT NULL DEFAULT false, `is_wifi_by_shell_enabled` INTEGER NOT NULL DEFAULT false)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAutoTunnelEnabled",
+ "columnName": "is_tunnel_enabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isTunnelOnMobileDataEnabled",
+ "columnName": "is_tunnel_on_mobile_data_enabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trustedNetworkSSIDs",
+ "columnName": "trusted_network_ssids",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAlwaysOnVpnEnabled",
+ "columnName": "is_always_on_vpn_enabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isTunnelOnEthernetEnabled",
+ "columnName": "is_tunnel_on_ethernet_enabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isShortcutsEnabled",
+ "columnName": "is_shortcuts_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isTunnelOnWifiEnabled",
+ "columnName": "is_tunnel_on_wifi_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isKernelEnabled",
+ "columnName": "is_kernel_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isRestoreOnBootEnabled",
+ "columnName": "is_restore_on_boot_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isMultiTunnelEnabled",
+ "columnName": "is_multi_tunnel_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isPingEnabled",
+ "columnName": "is_ping_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isAmneziaEnabled",
+ "columnName": "is_amnezia_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isWildcardsEnabled",
+ "columnName": "is_wildcards_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isWifiNameByShellEnabled",
+ "columnName": "is_wifi_by_shell_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TunnelConfig",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `wg_quick` TEXT NOT NULL, `tunnel_networks` TEXT NOT NULL DEFAULT '', `is_mobile_data_tunnel` INTEGER NOT NULL DEFAULT false, `is_primary_tunnel` INTEGER NOT NULL DEFAULT false, `am_quick` TEXT NOT NULL DEFAULT '', `is_Active` INTEGER NOT NULL DEFAULT false, `is_ping_enabled` INTEGER NOT NULL DEFAULT false, `ping_interval` INTEGER DEFAULT null, `ping_cooldown` INTEGER DEFAULT null, `ping_ip` TEXT DEFAULT null)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "wgQuick",
+ "columnName": "wg_quick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "tunnelNetworks",
+ "columnName": "tunnel_networks",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "isMobileDataTunnel",
+ "columnName": "is_mobile_data_tunnel",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isPrimaryTunnel",
+ "columnName": "is_primary_tunnel",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "amQuick",
+ "columnName": "am_quick",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "isActive",
+ "columnName": "is_Active",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "isPingEnabled",
+ "columnName": "is_ping_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "pingInterval",
+ "columnName": "ping_interval",
+ "affinity": "INTEGER",
+ "notNull": false,
+ "defaultValue": "null"
+ },
+ {
+ "fieldPath": "pingCooldown",
+ "columnName": "ping_cooldown",
+ "affinity": "INTEGER",
+ "notNull": false,
+ "defaultValue": "null"
+ },
+ {
+ "fieldPath": "pingIp",
+ "columnName": "ping_ip",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "null"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_TunnelConfig_name",
+ "unique": true,
+ "columnNames": [
+ "name"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TunnelConfig_name` ON `${TABLE_NAME}` (`name`)"
+ }
+ ],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4c9418386f72dfac5d28ab96c1e5ea0b')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 180c821..7c81484 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -186,10 +186,6 @@
-
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/AppDatabase.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/AppDatabase.kt
index cc06085..052a5d7 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/AppDatabase.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/AppDatabase.kt
@@ -11,7 +11,7 @@ import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
@Database(
entities = [Settings::class, TunnelConfig::class],
- version = 10,
+ version = 11,
autoMigrations =
[
AutoMigration(from = 1, to = 2),
@@ -36,6 +36,11 @@ import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
AutoMigration(7, 8),
AutoMigration(8, 9),
AutoMigration(9, 10),
+ AutoMigration(
+ from = 10,
+ to = 11,
+ spec = RemoveTunnelPauseMigration::class,
+ ),
],
exportSchema = true,
)
@@ -55,3 +60,9 @@ abstract class AppDatabase : RoomDatabase() {
columnName = "is_battery_saver_enabled",
)
class RemoveLegacySettingColumnsMigration : AutoMigrationSpec
+
+@DeleteColumn(
+ tableName = "Settings",
+ columnName = "is_auto_tunnel_paused",
+)
+class RemoveTunnelPauseMigration : AutoMigrationSpec
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/datastore/DataStoreManager.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/datastore/DataStoreManager.kt
index 54c80c3..5ed880d 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/datastore/DataStoreManager.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/datastore/DataStoreManager.kt
@@ -26,7 +26,6 @@ class DataStoreManager(
val currentSSID = stringPreferencesKey("CURRENT_SSID")
val pinLockEnabled = booleanPreferencesKey("PIN_LOCK_ENABLED")
val tunnelStatsExpanded = booleanPreferencesKey("TUNNEL_STATS_EXPANDED")
- val wildcardsEnabled = booleanPreferencesKey("WILDCARDS_ENABLED")
val theme = stringPreferencesKey("THEME")
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/GeneralState.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/GeneralState.kt
index db7be88..59e4b15 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/GeneralState.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/GeneralState.kt
@@ -7,14 +7,12 @@ data class GeneralState(
val isBatteryOptimizationDisableShown: Boolean = BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT,
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
val isTunnelStatsExpanded: Boolean = IS_TUNNEL_STATS_EXPANDED,
- val isWildcardsEnabled: Boolean = IS_WILDCARDS_ENABLED,
- val theme: Theme = Theme.AUTOMATIC
+ val theme: Theme = Theme.AUTOMATIC,
) {
companion object {
const val LOCATION_DISCLOSURE_SHOWN_DEFAULT = false
const val BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT = false
const val PIN_LOCK_ENABLED_DEFAULT = false
const val IS_TUNNEL_STATS_EXPANDED = false
- const val IS_WILDCARDS_ENABLED = false
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/Settings.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/Settings.kt
index bec24e9..aceda14 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/Settings.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/Settings.kt
@@ -40,11 +40,6 @@ data class Settings(
defaultValue = "false",
)
val isMultiTunnelEnabled: Boolean = false,
- @ColumnInfo(
- name = "is_auto_tunnel_paused",
- defaultValue = "false",
- )
- val isAutoTunnelPaused: Boolean = false,
@ColumnInfo(
name = "is_ping_enabled",
defaultValue = "false",
@@ -55,4 +50,14 @@ data class Settings(
defaultValue = "false",
)
val isAmneziaEnabled: Boolean = false,
+ @ColumnInfo(
+ name = "is_wildcards_enabled",
+ defaultValue = "false",
+ )
+ val isWildcardsEnabled: Boolean = false,
+ @ColumnInfo(
+ name = "is_wifi_by_shell_enabled",
+ defaultValue = "false",
+ )
+ val isWifiNameByShellEnabled: Boolean = false,
)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/AppStateRepository.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/AppStateRepository.kt
index 142465c..7e5f007 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/AppStateRepository.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/AppStateRepository.kt
@@ -13,10 +13,6 @@ interface AppStateRepository {
suspend fun setPinLockEnabled(enabled: Boolean)
- suspend fun isWildcardsEnabled(): Boolean
-
- suspend fun setWildcardsEnabled(enabled: Boolean)
-
suspend fun isBatteryOptimizationDisableShown(): Boolean
suspend fun setBatteryOptimizationDisableShown(shown: Boolean)
@@ -31,7 +27,7 @@ interface AppStateRepository {
suspend fun setTheme(theme: Theme)
- suspend fun getTheme() : Theme
+ suspend fun getTheme(): Theme
val generalStateFlow: Flow
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/DataStoreAppStateRepository.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/DataStoreAppStateRepository.kt
index 58260a6..39dcfc8 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/DataStoreAppStateRepository.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/DataStoreAppStateRepository.kt
@@ -29,14 +29,6 @@ class DataStoreAppStateRepository(
dataStoreManager.saveToDataStore(DataStoreManager.pinLockEnabled, enabled)
}
- override suspend fun isWildcardsEnabled(): Boolean {
- return dataStoreManager.getFromStore(DataStoreManager.wildcardsEnabled) ?: GeneralState.IS_WILDCARDS_ENABLED
- }
-
- override suspend fun setWildcardsEnabled(enabled: Boolean) {
- dataStoreManager.saveToDataStore(DataStoreManager.wildcardsEnabled, enabled)
- }
-
override suspend fun isBatteryOptimizationDisableShown(): Boolean {
return dataStoreManager.getFromStore(DataStoreManager.batteryDisableShown)
?: GeneralState.BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT
@@ -71,7 +63,7 @@ class DataStoreAppStateRepository(
return dataStoreManager.getFromStore(DataStoreManager.theme)?.let {
try {
Theme.valueOf(it)
- } catch (_ : IllegalArgumentException) {
+ } catch (_: IllegalArgumentException) {
Theme.AUTOMATIC
}
} ?: Theme.AUTOMATIC
@@ -92,8 +84,7 @@ class DataStoreAppStateRepository(
pref[DataStoreManager.pinLockEnabled]
?: GeneralState.PIN_LOCK_ENABLED_DEFAULT,
isTunnelStatsExpanded = pref[DataStoreManager.tunnelStatsExpanded] ?: GeneralState.IS_TUNNEL_STATS_EXPANDED,
- isWildcardsEnabled = pref[DataStoreManager.wildcardsEnabled] ?: GeneralState.IS_WILDCARDS_ENABLED,
- theme = getTheme()
+ theme = getTheme(),
)
} catch (e: IllegalArgumentException) {
Timber.e(e)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/RoomTunnelConfigRepository.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/RoomTunnelConfigRepository.kt
index bf63974..eeae7d5 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/RoomTunnelConfigRepository.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/RoomTunnelConfigRepository.kt
@@ -1,11 +1,9 @@
package com.zaneschepke.wireguardautotunnel.data.repository
-import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.data.TunnelConfigDao
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
-import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
@@ -26,8 +24,6 @@ class RoomTunnelConfigRepository(
override suspend fun save(tunnelConfig: TunnelConfig) {
withContext(ioDispatcher) {
tunnelConfigDao.save(tunnelConfig)
- }.also {
- WireGuardAutoTunnel.instance.requestTunnelTileServiceStateUpdate()
}
}
@@ -60,8 +56,6 @@ class RoomTunnelConfigRepository(
override suspend fun delete(tunnelConfig: TunnelConfig) {
withContext(ioDispatcher) {
tunnelConfigDao.delete(tunnelConfig)
- }.also {
- WireGuardAutoTunnel.instance.requestTunnelTileServiceStateUpdate()
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/module/TunnelModule.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/module/TunnelModule.kt
index b14c118..e8bf218 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/module/TunnelModule.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/module/TunnelModule.kt
@@ -9,6 +9,7 @@ import com.wireguard.android.util.RootShell
import com.wireguard.android.util.ToolsInstaller
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepository
+import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
import com.zaneschepke.wireguardautotunnel.service.tunnel.WireGuardTunnel
import dagger.Module
@@ -65,6 +66,7 @@ class TunnelModule {
tunnelConfigRepository: TunnelConfigRepository,
@ApplicationScope applicationScope: CoroutineScope,
@IoDispatcher ioDispatcher: CoroutineDispatcher,
+ serviceManager: ServiceManager,
): TunnelService {
return WireGuardTunnel(
amneziaBackend,
@@ -73,6 +75,13 @@ class TunnelModule {
appDataRepository,
applicationScope,
ioDispatcher,
+ serviceManager,
)
}
+
+ @Singleton
+ @Provides
+ fun provideServiceManager(@ApplicationContext context: Context): ServiceManager {
+ return ServiceManager(context)
+ }
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/AppUpdateReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/AppUpdateReceiver.kt
index b60e9a4..14f68ed 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/AppUpdateReceiver.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/AppUpdateReceiver.kt
@@ -7,12 +7,12 @@ import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
-import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
+import javax.inject.Provider
@AndroidEntryPoint
class AppUpdateReceiver : BroadcastReceiver() {
@@ -25,7 +25,10 @@ class AppUpdateReceiver : BroadcastReceiver() {
lateinit var appDataRepository: AppDataRepository
@Inject
- lateinit var tunnelService: TunnelService
+ lateinit var tunnelService: Provider
+
+ @Inject
+ lateinit var serviceManager: ServiceManager
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != Intent.ACTION_MY_PACKAGE_REPLACED) return
@@ -33,11 +36,11 @@ class AppUpdateReceiver : BroadcastReceiver() {
val settings = appDataRepository.settings.getSettings()
if (settings.isAutoTunnelEnabled) {
Timber.i("Restarting services after upgrade")
- ServiceManager.startWatcherServiceForeground(context)
+ serviceManager.startAutoTunnel(true)
}
- if (!settings.isAutoTunnelEnabled || settings.isAutoTunnelPaused) {
+ if (!settings.isAutoTunnelEnabled) {
val tunnels = appDataRepository.tunnels.getAll().filter { it.isActive }
- if (tunnels.isNotEmpty()) context.startTunnelBackground(tunnels.first().id)
+ if (tunnels.isNotEmpty()) tunnelService.get().startTunnel(tunnels.first(), true)
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BackgroundActionReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BackgroundActionReceiver.kt
deleted file mode 100644
index aea9c42..0000000
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BackgroundActionReceiver.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.zaneschepke.wireguardautotunnel.receiver
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepository
-import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
-import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
-import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
-import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import timber.log.Timber
-import javax.inject.Inject
-import javax.inject.Provider
-
-@AndroidEntryPoint
-class BackgroundActionReceiver : BroadcastReceiver() {
-
- @Inject
- @ApplicationScope
- lateinit var applicationScope: CoroutineScope
-
- @Inject
- lateinit var tunnelService: Provider
-
- @Inject
- lateinit var tunnelConfigRepository: TunnelConfigRepository
-
- override fun onReceive(context: Context, intent: Intent) {
- val id = intent.getIntExtra(TUNNEL_ID_EXTRA_KEY, 0)
- if (id == 0) return
- when (intent.action) {
- ACTION_CONNECT -> {
- Timber.d("Connect actions")
- applicationScope.launch {
- val tunnel = tunnelConfigRepository.getById(id)
- tunnel?.let {
- ServiceManager.startTunnelBackgroundService(context)
- tunnelService.get().startTunnel(it)
- }
- }
- }
- ACTION_DISCONNECT -> {
- applicationScope.launch {
- val tunnel = tunnelConfigRepository.getById(id)
- tunnel?.let {
- ServiceManager.stopTunnelBackgroundService(context)
- tunnelService.get().stopTunnel(it)
- }
- }
- }
- }
- }
-
- companion object {
- const val ACTION_CONNECT = "ACTION_CONNECT"
- const val ACTION_DISCONNECT = "ACTION_DISCONNECT"
- const val TUNNEL_ID_EXTRA_KEY = "tunnelId"
- }
-}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt
index eb03f3e..f5b37e0 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt
@@ -8,7 +8,6 @@ import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
-import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -28,6 +27,9 @@ class BootReceiver : BroadcastReceiver() {
@ApplicationScope
lateinit var applicationScope: CoroutineScope
+ @Inject
+ lateinit var serviceManager: ServiceManager
+
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED != intent.action) return
applicationScope.launch {
@@ -37,11 +39,11 @@ class BootReceiver : BroadcastReceiver() {
val tunState = tunnelService.get().vpnState.value.status
if (activeTunnels.isNotEmpty() && tunState != TunnelState.UP) {
Timber.i("Starting previously active tunnel")
- context.startTunnelBackground(activeTunnels.first().id)
+ tunnelService.get().startTunnel(activeTunnels.first(), true)
}
if (isAutoTunnelEnabled) {
Timber.i("Starting watcher service from boot")
- ServiceManager.startWatcherServiceForeground(context)
+ serviceManager.startAutoTunnel(true)
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/AutoTunnelService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/AutoTunnelService.kt
index cfbc88d..133382b 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/AutoTunnelService.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/AutoTunnelService.kt
@@ -26,10 +26,11 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.extensions.cancelWithMessage
import com.zaneschepke.wireguardautotunnel.util.extensions.getCurrentWifiName
-import com.zaneschepke.wireguardautotunnel.util.extensions.isMatchingToWildcardList
+import com.zaneschepke.wireguardautotunnel.util.extensions.isDown
import com.zaneschepke.wireguardautotunnel.util.extensions.isReachable
import com.zaneschepke.wireguardautotunnel.util.extensions.onNotRunning
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@@ -74,6 +75,9 @@ class AutoTunnelService : LifecycleService() {
@IoDispatcher
lateinit var ioDispatcher: CoroutineDispatcher
+ @Inject
+ lateinit var serviceManager: ServiceManager
+
@Inject
@MainImmediateDispatcher
lateinit var mainImmediateDispatcher: CoroutineDispatcher
@@ -88,14 +92,11 @@ class AutoTunnelService : LifecycleService() {
private var pingJob: Job? = null
private var networkEventJob: Job? = null
- @get:Synchronized @set:Synchronized
- private var running: Boolean = false
-
override fun onCreate() {
super.onCreate()
lifecycleScope.launch(mainImmediateDispatcher) {
kotlin.runCatching {
- launchNotification()
+ launchWatcherNotification()
}.onFailure {
Timber.e(it)
}
@@ -110,32 +111,14 @@ class AutoTunnelService : LifecycleService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Timber.d("onStartCommand executed with startId: $startId")
- if (intent != null) {
- val action = intent.action
- when (action) {
- Action.START.name,
- Action.START_FOREGROUND.name,
- -> startService()
- Action.STOP.name, Action.STOP_FOREGROUND.name -> stopService()
- }
- }
+ serviceManager.autoTunnelService.complete(this)
return super.onStartCommand(intent, flags, startId)
}
- private suspend fun launchNotification() {
- if (appDataRepository.settings.getSettings().isAutoTunnelPaused) {
- launchWatcherPausedNotification()
- } else {
- launchWatcherNotification()
- }
- }
-
- private fun startService() {
- if (running) return
- running = true
+ fun start() {
kotlin.runCatching {
lifecycleScope.launch(mainImmediateDispatcher) {
- launchNotification()
+ launchWatcherNotification()
initWakeLock()
}
startSettingsJob()
@@ -145,7 +128,7 @@ class AutoTunnelService : LifecycleService() {
}
}
- private fun stopService() {
+ fun stop() {
wakeLock?.let {
if (it.isHeld) {
it.release()
@@ -157,6 +140,7 @@ class AutoTunnelService : LifecycleService() {
override fun onDestroy() {
cancelAndResetNetworkJobs()
cancelAndResetPingJob()
+ serviceManager.autoTunnelService = CompletableDeferred()
super.onDestroy()
}
@@ -176,10 +160,6 @@ class AutoTunnelService : LifecycleService() {
)
}
- private fun launchWatcherPausedNotification() {
- launchWatcherNotification(getString(R.string.watcher_notification_text_paused))
- }
-
private fun initWakeLock() {
wakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
@@ -265,8 +245,7 @@ class AutoTunnelService : LifecycleService() {
runCatching {
do {
val vpnState = tunnelService.get().vpnState.value
- val settings = appDataRepository.settings.getSettings()
- if (vpnState.status == TunnelState.UP && !settings.isAutoTunnelPaused) {
+ if (vpnState.status == TunnelState.UP) {
if (vpnState.tunnelConfig != null) {
val config = TunnelConfig.configFromWgQuick(vpnState.tunnelConfig.wgQuick)
val results = if (vpnState.tunnelConfig.pingIp != null) {
@@ -296,17 +275,6 @@ class AutoTunnelService : LifecycleService() {
}
}
- private fun onAutoTunnelPause(paused: Boolean) {
- if (autoTunnelStateFlow.value.settings.isAutoTunnelPaused
- != paused
- ) {
- when (paused) {
- true -> launchWatcherPausedNotification()
- false -> launchWatcherNotification()
- }
- }
- }
-
private suspend fun watchForSettingsChanges() {
Timber.i("Starting settings watcher")
withContext(ioDispatcher) {
@@ -321,7 +289,7 @@ class AutoTunnelService : LifecycleService() {
tunnels = tunnels,
)
}.collect {
- onAutoTunnelPause(it.settings.isAutoTunnelPaused)
+ Timber.d("got new settings: ${it.settings}")
manageJobsBySettings(it.settings)
autoTunnelStateFlow.emit(it)
}
@@ -331,7 +299,12 @@ class AutoTunnelService : LifecycleService() {
private suspend fun watchForVpnStateChanges() {
Timber.i("Starting vpn state watcher")
withContext(ioDispatcher) {
- tunnelService.get().vpnState.collect { state ->
+ tunnelService.get().vpnState.distinctUntilChanged { old, new ->
+ old.tunnelConfig == new.tunnelConfig && old.status == new.status
+ }.collect { state ->
+ autoTunnelStateFlow.update {
+ it.copy(vpnState = state)
+ }
state.tunnelConfig?.let {
val settings = appDataRepository.settings.getSettings()
if (it.isPingEnabled && !settings.isPingEnabled) {
@@ -475,9 +448,8 @@ class AutoTunnelService : LifecycleService() {
private suspend fun getWifiSSID(networkCapabilities: NetworkCapabilities): String? {
return withContext(ioDispatcher) {
- try {
- rootShell.get().getCurrentWifiName()
- } catch (_: Exception) {
+ with(autoTunnelStateFlow.value.settings) {
+ if (isWifiNameByShellEnabled) return@withContext rootShell.get().getCurrentWifiName()
wifiService.getNetworkName(networkCapabilities)
}
}
@@ -487,100 +459,95 @@ class AutoTunnelService : LifecycleService() {
return appDataRepository.tunnels.findByMobileDataTunnel().firstOrNull()
}
- private fun isTunnelDown(): Boolean {
- return tunnelService.get().vpnState.value.status == TunnelState.DOWN
- }
-
private suspend fun handleNetworkEventChanges() {
withContext(ioDispatcher) {
Timber.i("Starting network event watcher")
autoTunnelStateFlow.collectLatest { watcherState ->
val autoTunnel = "Auto-tunnel watcher"
- if (!watcherState.settings.isAutoTunnelPaused) {
- // delay for rapid network state changes and then collect latest
- delay(Constants.WATCHER_COLLECTION_DELAY)
- val activeTunnel = tunnelService.get().vpnState.value.tunnelConfig
- val defaultTunnel = appDataRepository.getPrimaryOrFirstTunnel()
- when {
- watcherState.isEthernetConditionMet() -> {
- Timber.i("$autoTunnel - tunnel on on ethernet condition met")
- if (isTunnelDown()) {
- defaultTunnel?.let {
+ Timber.d("New watcher state!")
+ // delay for rapid network state changes and then collect latest
+ delay(Constants.WATCHER_COLLECTION_DELAY)
+ val activeTunnel = watcherState.vpnState.tunnelConfig
+ val defaultTunnel = appDataRepository.getPrimaryOrFirstTunnel()
+ when {
+ watcherState.isEthernetConditionMet() -> {
+ Timber.i("$autoTunnel - tunnel on on ethernet condition met")
+ if (watcherState.vpnState.isDown()) {
+ defaultTunnel?.let {
+ tunnelService.get().startTunnel(it)
+ }
+ }
+ }
+
+ watcherState.isMobileDataConditionMet() -> {
+ Timber.i("$autoTunnel - tunnel on mobile data condition met")
+ val mobileDataTunnel = getMobileDataTunnel()
+ val tunnel =
+ mobileDataTunnel ?: defaultTunnel
+ if (watcherState.vpnState.isDown() || activeTunnel?.isMobileDataTunnel == false) {
+ tunnel?.let {
+ tunnelService.get().startTunnel(it)
+ }
+ }
+ }
+
+ watcherState.isTunnelOffOnMobileDataConditionMet() -> {
+ Timber.i("$autoTunnel - tunnel off on mobile data met, turning vpn off")
+ if (!watcherState.vpnState.isDown()) {
+ activeTunnel?.let {
+ tunnelService.get().stopTunnel(it)
+ }
+ }
+ }
+
+ watcherState.isUntrustedWifiConditionMet() -> {
+ Timber.i("Untrusted wifi condition met")
+ if (activeTunnel == null || watcherState.isCurrentSSIDActiveTunnelNetwork() == false ||
+ watcherState.vpnState.isDown()
+ ) {
+ Timber.i(
+ "$autoTunnel - tunnel on ssid not associated with current tunnel condition met",
+ )
+ watcherState.getTunnelWithMatchingTunnelNetwork()?.let {
+ Timber.i("Found tunnel associated with this SSID, bringing tunnel up: ${it.name}")
+ if (watcherState.vpnState.isDown() || activeTunnel?.id != it.id) {
tunnelService.get().startTunnel(it)
}
- }
- }
-
- watcherState.isMobileDataConditionMet() -> {
- Timber.i("$autoTunnel - tunnel on mobile data condition met")
- val mobileDataTunnel = getMobileDataTunnel()
- val tunnel =
- mobileDataTunnel ?: defaultTunnel
- if (isTunnelDown() || activeTunnel?.isMobileDataTunnel == false) {
- tunnel?.let {
- tunnelService.get().startTunnel(it)
- }
- }
- }
-
- watcherState.isTunnelOffOnMobileDataConditionMet() -> {
- Timber.i("$autoTunnel - tunnel off on mobile data met, turning vpn off")
- if (!isTunnelDown()) {
- activeTunnel?.let {
- tunnelService.get().stopTunnel(it)
- }
- }
- }
-
- watcherState.isUntrustedWifiConditionMet() -> {
- Timber.i("Untrusted wifi condition met")
- if (activeTunnel?.tunnelNetworks?.isMatchingToWildcardList(watcherState.currentNetworkSSID) == false ||
- activeTunnel == null || isTunnelDown()
- ) {
- Timber.i(
- "$autoTunnel - tunnel on ssid not associated with current tunnel condition met",
- )
- watcherState.tunnels.firstOrNull { it.tunnelNetworks.isMatchingToWildcardList(watcherState.currentNetworkSSID) }?.let {
- Timber.i("Found tunnel associated with this SSID, bringing tunnel up: ${it.name}")
- if (isTunnelDown() || activeTunnel?.id != it.id) {
+ } ?: suspend {
+ Timber.i("No tunnel associated with this SSID, using defaults")
+ val default = appDataRepository.getPrimaryOrFirstTunnel()
+ if (default?.name != tunnelService.get().name || watcherState.vpnState.isDown()) {
+ default?.let {
tunnelService.get().startTunnel(it)
}
- } ?: suspend {
- Timber.i("No tunnel associated with this SSID, using defaults")
- val default = appDataRepository.getPrimaryOrFirstTunnel()
- if (default?.name != tunnelService.get().name || isTunnelDown()) {
- default?.let {
- tunnelService.get().startTunnel(it)
- }
- }
- }.invoke()
- }
+ }
+ }.invoke()
}
+ }
- watcherState.isTrustedWifiConditionMet() -> {
- Timber.i(
- "$autoTunnel - tunnel off on trusted wifi condition met, turning vpn off",
- )
- if (!isTunnelDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
- }
+ watcherState.isTrustedWifiConditionMet() -> {
+ Timber.i(
+ "$autoTunnel - tunnel off on trusted wifi condition met, turning vpn off",
+ )
+ if (!watcherState.vpnState.isDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
+ }
- watcherState.isTunnelOffOnWifiConditionMet() -> {
- Timber.i(
- "$autoTunnel - tunnel off on wifi condition met, turning vpn off",
- )
- if (!isTunnelDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
- }
+ watcherState.isTunnelOffOnWifiConditionMet() -> {
+ Timber.i(
+ "$autoTunnel - tunnel off on wifi condition met, turning vpn off",
+ )
+ if (!watcherState.vpnState.isDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
+ }
- watcherState.isTunnelOffOnNoConnectivityMet() -> {
- Timber.i(
- "$autoTunnel - tunnel off on no connectivity met, turning vpn off",
- )
- if (!isTunnelDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
- }
+ watcherState.isTunnelOffOnNoConnectivityMet() -> {
+ Timber.i(
+ "$autoTunnel - tunnel off on no connectivity met, turning vpn off",
+ )
+ if (!watcherState.vpnState.isDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
+ }
- else -> {
- Timber.i("$autoTunnel - no condition met")
- }
+ else -> {
+ Timber.i("$autoTunnel - no condition met")
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/AutoTunnelState.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/AutoTunnelState.kt
index adb8391..a6b3a39 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/AutoTunnelState.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/AutoTunnelState.kt
@@ -1,10 +1,13 @@
package com.zaneschepke.wireguardautotunnel.service.foreground
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
+import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
+import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnState
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
import com.zaneschepke.wireguardautotunnel.util.extensions.isMatchingToWildcardList
data class AutoTunnelState(
+ val vpnState: VpnState = VpnState(),
val isWifiConnected: Boolean = false,
val isEthernetConnected: Boolean = false,
val isMobileDataConnected: Boolean = false,
@@ -41,7 +44,7 @@ data class AutoTunnelState(
return (
!isEthernetConnected &&
isWifiConnected &&
- !settings.trustedNetworkSSIDs.isMatchingToWildcardList(currentNetworkSSID) &&
+ !isCurrentSSIDTrusted() &&
settings.isTunnelOnWifiEnabled
)
}
@@ -51,7 +54,7 @@ data class AutoTunnelState(
!isEthernetConnected &&
(
isWifiConnected &&
- settings.trustedNetworkSSIDs.isMatchingToWildcardList(currentNetworkSSID)
+ isCurrentSSIDTrusted()
)
)
}
@@ -73,4 +76,32 @@ data class AutoTunnelState(
!isMobileDataConnected
)
}
+
+ fun isCurrentSSIDTrusted(): Boolean {
+ return if (settings.isWildcardsEnabled) {
+ settings.trustedNetworkSSIDs.isMatchingToWildcardList(currentNetworkSSID)
+ } else {
+ settings.trustedNetworkSSIDs.contains(currentNetworkSSID)
+ }
+ }
+ fun isCurrentSSIDActiveTunnelNetwork(): Boolean {
+ val currentTunnelNetworks = vpnState.tunnelConfig?.tunnelNetworks
+ return (
+ if (settings.isWildcardsEnabled) {
+ currentTunnelNetworks?.isMatchingToWildcardList(currentNetworkSSID)
+ } else {
+ currentTunnelNetworks?.contains(currentNetworkSSID)
+ }
+ ) == true
+ }
+
+ fun getTunnelWithMatchingTunnelNetwork(): TunnelConfig? {
+ return tunnels.firstOrNull {
+ if (settings.isWildcardsEnabled) {
+ it.tunnelNetworks.isMatchingToWildcardList(currentNetworkSSID)
+ } else {
+ it.tunnelNetworks.contains(currentNetworkSSID)
+ }
+ }
+ }
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceManager.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceManager.kt
index 71939ea..08180c1 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceManager.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceManager.kt
@@ -3,69 +3,81 @@ package com.zaneschepke.wireguardautotunnel.service.foreground
import android.app.Service
import android.content.Context
import android.content.Intent
-import android.net.VpnService
+import com.zaneschepke.wireguardautotunnel.util.SingletonHolder
+import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
+import jakarta.inject.Inject
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
import timber.log.Timber
-object ServiceManager {
- private fun actionOnService(action: Action, context: Context, cls: Class, extras: Map? = null) {
- if (VpnService.prepare(context) != null) return
- val intent =
- Intent(context, cls).also {
- it.action = action.name
- extras?.forEach { (k, v) -> it.putExtra(k, v) }
- }
- intent.component?.javaClass
- try {
- when (action) {
- Action.START_FOREGROUND, Action.STOP_FOREGROUND ->
- context.startForegroundService(
- intent,
- )
+@OptIn(ExperimentalCoroutinesApi::class)
+class ServiceManager
+@Inject constructor(private val context: Context) {
- Action.START, Action.STOP -> context.startService(intent)
- }
- } catch (e: Exception) {
- Timber.e(e.message)
+ private val _autoTunnelActive = MutableStateFlow(false)
+
+ val autoTunnelActive = _autoTunnelActive.asStateFlow()
+
+ var autoTunnelService = CompletableDeferred()
+ var backgroundService = CompletableDeferred()
+
+ companion object : SingletonHolder(::ServiceManager)
+
+ private fun startService(cls: Class, background: Boolean) {
+ val intent = Intent(context, cls)
+ if (background) {
+ context.startForegroundService(intent)
+ } else {
+ context.startService(intent)
}
}
- fun startWatcherServiceForeground(context: Context) {
- actionOnService(
- Action.START_FOREGROUND,
- context,
- AutoTunnelService::class.java,
- )
+ suspend fun startAutoTunnel(background: Boolean) {
+ if (autoTunnelService.isCompleted) return _autoTunnelActive.update { true }
+ kotlin.runCatching {
+ startService(AutoTunnelService::class.java, background)
+ autoTunnelService.await()
+ autoTunnelService.getCompleted().start()
+ _autoTunnelActive.update { true }
+ }.onFailure {
+ Timber.e(it)
+ }
}
- fun startWatcherService(context: Context) {
- actionOnService(
- Action.START,
- context,
- AutoTunnelService::class.java,
- )
+ suspend fun startBackgroundService() {
+ if (backgroundService.isCompleted) return
+ kotlin.runCatching {
+ startService(TunnelBackgroundService::class.java, true)
+ backgroundService.await()
+ backgroundService.getCompleted().start()
+ }.onFailure {
+ Timber.e(it)
+ }
}
- fun stopWatcherService(context: Context) {
- actionOnService(
- Action.STOP,
- context,
- AutoTunnelService::class.java,
- )
+ fun stopBackgroundService() {
+ if (!backgroundService.isCompleted) return
+ runCatching {
+ backgroundService.getCompleted().stop()
+ }.onFailure {
+ Timber.e(it)
+ }
}
- fun startTunnelBackgroundService(context: Context) {
- actionOnService(
- Action.START_FOREGROUND,
- context,
- TunnelBackgroundService::class.java,
- )
+ fun stopAutoTunnel() {
+ if (!autoTunnelService.isCompleted) return
+ runCatching {
+ autoTunnelService.getCompleted().stop()
+ _autoTunnelActive.update { false }
+ }.onFailure {
+ Timber.e(it)
+ }
}
- fun stopTunnelBackgroundService(context: Context) {
- actionOnService(
- Action.STOP,
- context,
- TunnelBackgroundService::class.java,
- )
+ fun requestTunnelTileUpdate() {
+ context.requestTunnelTileServiceStateUpdate()
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/TunnelBackgroundService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/TunnelBackgroundService.kt
index b59082f..66ae9c9 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/TunnelBackgroundService.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/TunnelBackgroundService.kt
@@ -7,6 +7,7 @@ import androidx.lifecycle.LifecycleService
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.CompletableDeferred
import javax.inject.Inject
@AndroidEntryPoint
@@ -15,6 +16,9 @@ class TunnelBackgroundService : LifecycleService() {
@Inject
lateinit var notificationService: NotificationService
+ @Inject
+ lateinit var serviceManager: ServiceManager
+
private val foregroundId = 123
override fun onCreate() {
@@ -29,27 +33,24 @@ class TunnelBackgroundService : LifecycleService() {
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- if (intent != null) {
- val action = intent.action
- when (action) {
- Action.START.name,
- Action.START_FOREGROUND.name,
- -> startService()
- Action.STOP.name, Action.STOP_FOREGROUND.name -> stopService()
- }
- }
+ serviceManager.backgroundService.complete(this)
return super.onStartCommand(intent, flags, startId)
}
- private fun startService() {
+ fun start() {
startForeground(foregroundId, createNotification())
}
- private fun stopService() {
+ fun stop() {
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}
+ override fun onDestroy() {
+ serviceManager.backgroundService = CompletableDeferred()
+ super.onDestroy()
+ }
+
private fun createNotification(): Notification {
return notificationService.createNotification(
getString(R.string.vpn_channel_id),
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/shortcut/ShortcutsActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/shortcut/ShortcutsActivity.kt
index d61ea71..7377a3a 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/shortcut/ShortcutsActivity.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/shortcut/ShortcutsActivity.kt
@@ -6,9 +6,8 @@ import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
import com.zaneschepke.wireguardautotunnel.service.foreground.AutoTunnelService
+import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
-import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
-import com.zaneschepke.wireguardautotunnel.util.extensions.stopTunnelBackground
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -24,6 +23,9 @@ class ShortcutsActivity : ComponentActivity() {
@Inject
lateinit var tunnelService: Provider
+ @Inject
+ lateinit var serviceManager: ServiceManager
+
@Inject
@ApplicationScope
lateinit var applicationScope: CoroutineScope
@@ -44,26 +46,16 @@ class ShortcutsActivity : ComponentActivity() {
Timber.d("Shortcut action on name: ${tunnelConfig?.name}")
tunnelConfig?.let {
when (intent.action) {
- Action.START.name -> this@ShortcutsActivity.startTunnelBackground(it.id)
- Action.STOP.name -> this@ShortcutsActivity.stopTunnelBackground(it.id)
+ Action.START.name -> tunnelService.get().startTunnel(it, true)
+ Action.STOP.name -> tunnelService.get().stopTunnel(it)
else -> Unit
}
}
}
AutoTunnelService::class.java.simpleName, LEGACY_AUTO_TUNNEL_SERVICE_NAME -> {
when (intent.action) {
- Action.START.name ->
- appDataRepository.settings.save(
- settings.copy(
- isAutoTunnelPaused = false,
- ),
- )
- Action.STOP.name ->
- appDataRepository.settings.save(
- settings.copy(
- isAutoTunnelPaused = true,
- ),
- )
+ Action.START.name -> serviceManager.startAutoTunnel(true)
+ Action.STOP.name -> serviceManager.stopAutoTunnel()
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/AutoTunnelControlTile.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/AutoTunnelControlTile.kt
index 3bcd9db..0a445e3 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/AutoTunnelControlTile.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/AutoTunnelControlTile.kt
@@ -1,7 +1,6 @@
package com.zaneschepke.wireguardautotunnel.service.tile
import android.content.Intent
-import android.os.Build
import android.os.IBinder
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
@@ -9,9 +8,9 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.lifecycleScope
-import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
+import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -23,51 +22,18 @@ class AutoTunnelControlTile : TileService(), LifecycleOwner {
@Inject
lateinit var appDataRepository: AppDataRepository
+ @Inject
+ lateinit var serviceManager: ServiceManager
+
@Inject
@ApplicationScope
lateinit var applicationScope: CoroutineScope
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
- /* This works around an annoying unsolved frameworks bug some people are hitting. */
- override fun onBind(intent: Intent): IBinder? {
- var ret: IBinder? = null
- try {
- ret = super.onBind(intent)
- } catch (e: Throwable) {
- Timber.e("Failed to bind to AutoTunnelTile")
- }
- return ret
- }
-
override fun onCreate() {
super.onCreate()
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
-
- applicationScope.launch {
- appDataRepository.settings.getSettingsFlow().collect {
- kotlin.runCatching {
- when (it.isAutoTunnelEnabled) {
- true -> {
- if (it.isAutoTunnelPaused) {
- setInactive()
- setTileDescription(this@AutoTunnelControlTile.getString(R.string.paused))
- } else {
- setActive()
- setTileDescription(this@AutoTunnelControlTile.getString(R.string.active))
- }
- }
-
- false -> {
- setTileDescription(this@AutoTunnelControlTile.getString(R.string.disabled))
- setUnavailable()
- }
- }
- }.onFailure {
- Timber.e(it)
- }
- }
- }
}
override fun onStopListening() {
@@ -82,26 +48,28 @@ class AutoTunnelControlTile : TileService(), LifecycleOwner {
override fun onStartListening() {
super.onStartListening()
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ lifecycleScope.launch {
+ if (appDataRepository.tunnels.getAll().isEmpty()) return@launch setUnavailable()
+ updateTileState()
+ }
+ }
+
+ private fun updateTileState() {
+ serviceManager.autoTunnelActive.value.let {
+ if (it) setActive() else setInactive()
+ }
}
override fun onClick() {
super.onClick()
unlockAndRun {
lifecycleScope.launch {
- kotlin.runCatching {
- val settings = appDataRepository.settings.getSettings()
- if (settings.isAutoTunnelPaused) {
- return@launch appDataRepository.settings.save(
- settings.copy(
- isAutoTunnelPaused = false,
- ),
- )
- }
- appDataRepository.settings.save(
- settings.copy(
- isAutoTunnelPaused = true,
- ),
- )
+ if (serviceManager.autoTunnelActive.value) {
+ serviceManager.stopAutoTunnel()
+ setInactive()
+ } else {
+ serviceManager.startAutoTunnel(true)
+ setActive()
}
}
}
@@ -128,16 +96,15 @@ class AutoTunnelControlTile : TileService(), LifecycleOwner {
}
}
- private fun setTileDescription(description: String) {
- kotlin.runCatching {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- qsTile.subtitle = description
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- qsTile.stateDescription = description
- }
- qsTile.updateTile()
+ /* This works around an annoying unsolved frameworks bug some people are hitting. */
+ override fun onBind(intent: Intent): IBinder? {
+ var ret: IBinder? = null
+ try {
+ ret = super.onBind(intent)
+ } catch (_: Throwable) {
+ Timber.e("Failed to bind to TunnelControlTile")
}
+ return ret
}
override val lifecycle: Lifecycle
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt
index 0da6a01..253247d 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt
@@ -1,6 +1,8 @@
package com.zaneschepke.wireguardautotunnel.service.tile
+import android.content.Intent
import android.os.Build
+import android.os.IBinder
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import androidx.lifecycle.Lifecycle
@@ -11,8 +13,6 @@ import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
-import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
-import com.zaneschepke.wireguardautotunnel.util.extensions.stopTunnelBackground
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -36,7 +36,6 @@ class TunnelControlTile : TileService(), LifecycleOwner {
override fun onCreate() {
super.onCreate()
- Timber.d("onCreate for tile service")
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
@@ -52,6 +51,7 @@ class TunnelControlTile : TileService(), LifecycleOwner {
override fun onStartListening() {
super.onStartListening()
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ Timber.d("Updating tile!")
lifecycleScope.launch {
if (appDataRepository.tunnels.getAll().isEmpty()) return@launch setUnavailable()
updateTileState()
@@ -60,6 +60,7 @@ class TunnelControlTile : TileService(), LifecycleOwner {
private suspend fun updateTileState() {
val lastActive = appDataRepository.getStartTunnelConfig()
+ Timber.d("Got config $lastActive")
lastActive?.let {
updateTile(it)
}
@@ -68,13 +69,15 @@ class TunnelControlTile : TileService(), LifecycleOwner {
override fun onClick() {
super.onClick()
unlockAndRun {
- Timber.d("Click")
lifecycleScope.launch {
- val context = this@TunnelControlTile
val lastActive = appDataRepository.getStartTunnelConfig()
lastActive?.let { tunnel ->
- if (tunnel.isActive) return@launch context.stopTunnelBackground(tunnel.id)
- context.startTunnelBackground(tunnel.id)
+ if (tunnel.isActive) {
+ tunnelService.get().stopTunnel(tunnel)
+ } else {
+ tunnelService.get().startTunnel(tunnel, true)
+ }
+ updateTileState()
}
}
}
@@ -124,6 +127,17 @@ class TunnelControlTile : TileService(), LifecycleOwner {
}
}
+ /* This works around an annoying unsolved frameworks bug some people are hitting. */
+ override fun onBind(intent: Intent): IBinder? {
+ var ret: IBinder? = null
+ try {
+ ret = super.onBind(intent)
+ } catch (_: Throwable) {
+ Timber.e("Failed to bind to TunnelControlTile")
+ }
+ return ret
+ }
+
override val lifecycle: Lifecycle
get() = lifecycleRegistry
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/TunnelService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/TunnelService.kt
index d1279dc..41ebf7e 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/TunnelService.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/TunnelService.kt
@@ -5,7 +5,7 @@ import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
import kotlinx.coroutines.flow.StateFlow
interface TunnelService : Tunnel, org.amnezia.awg.backend.Tunnel {
- suspend fun startTunnel(tunnelConfig: TunnelConfig): Result
+ suspend fun startTunnel(tunnelConfig: TunnelConfig, background: Boolean = false): Result
suspend fun stopTunnel(tunnelConfig: TunnelConfig): Result
@@ -18,5 +18,6 @@ interface TunnelService : Tunnel, org.amnezia.awg.backend.Tunnel {
suspend fun getState(): TunnelState
fun cancelStatsJob()
+
fun startStatsJob()
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt
index a511d5d..16948ac 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt
@@ -8,6 +8,7 @@ import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepositor
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
import com.zaneschepke.wireguardautotunnel.module.Kernel
+import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.AmneziaStatistics
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStatistics
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.WireGuardStatistics
@@ -36,6 +37,7 @@ constructor(
private val appDataRepository: AppDataRepository,
@ApplicationScope private val applicationScope: CoroutineScope,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
+ private val serviceManager: ServiceManager,
) : TunnelService {
private val _vpnState = MutableStateFlow(VpnState())
@@ -87,9 +89,9 @@ constructor(
}
}
- override suspend fun startTunnel(tunnelConfig: TunnelConfig): Result {
+ override suspend fun startTunnel(tunnelConfig: TunnelConfig, background: Boolean): Result {
return withContext(ioDispatcher) {
- onBeforeStart(tunnelConfig)
+ onBeforeStart(tunnelConfig, background)
setState(tunnelConfig, TunnelState.UP).onSuccess {
emitTunnelState(it)
}.onFailure {
@@ -143,18 +145,28 @@ constructor(
resetBackendStatistics()
}
- private suspend fun onBeforeStart(tunnelConfig: TunnelConfig) {
- if (_vpnState.value.status == TunnelState.UP) vpnState.value.tunnelConfig?.let { stopTunnel(it) }
+ private suspend fun onBeforeStart(tunnelConfig: TunnelConfig, background: Boolean) {
+ if (_vpnState.value.status == TunnelState.UP &&
+ tunnelConfig != _vpnState.value.tunnelConfig
+ ) {
+ vpnState.value.tunnelConfig?.let { stopTunnel(it) }
+ }
+ if (background) serviceManager.startBackgroundService()
resetBackendStatistics()
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true))
emitVpnStateConfig(tunnelConfig)
startStatsJob()
+ Timber.d("Updating start")
+ serviceManager.requestTunnelTileUpdate()
}
private suspend fun onBeforeStop(tunnelConfig: TunnelConfig) {
cancelStatsJob()
resetBackendStatistics()
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = false))
+ serviceManager.stopBackgroundService()
+ Timber.d("UPdating stop")
+ serviceManager.requestTunnelTileUpdate()
}
private fun emitTunnelState(state: TunnelState) {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppUiState.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppUiState.kt
index 3c4a6be..ae7ab7e 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppUiState.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppUiState.kt
@@ -10,6 +10,5 @@ data class AppUiState(
val tunnels: List = emptyList(),
val vpnState: VpnState = VpnState(),
val generalState: GeneralState = GeneralState(),
- val isKernelAvailable: Boolean = false,
- val isRooted: Boolean = false,
+ val autoTunnelActive: Boolean = false,
)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt
index 468303a..f47149e 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt
@@ -2,8 +2,6 @@ package com.zaneschepke.wireguardautotunnel.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.wireguard.android.backend.WgQuickBackend
-import com.wireguard.android.util.RootShell
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
@@ -11,7 +9,6 @@ import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.util.Constants
-import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
@@ -20,13 +17,10 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onCompletion
-import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.takeWhile
-import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
-import kotlinx.coroutines.withContext
import xyz.teamgravity.pin_lock_compose.PinManager
import javax.inject.Inject
import javax.inject.Provider
@@ -38,6 +32,7 @@ constructor(
private val appDataRepository: AppDataRepository,
private val tunnelService: Provider,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
+ private val serviceManager: ServiceManager,
) : ViewModel() {
val uiState =
@@ -46,12 +41,14 @@ constructor(
appDataRepository.tunnels.getTunnelConfigsFlow(),
tunnelService.get().vpnState,
appDataRepository.appState.generalStateFlow,
- ) { settings, tunnels, tunnelState, generalState ->
+ serviceManager.autoTunnelActive,
+ ) { settings, tunnels, tunnelState, generalState, autoTunnel ->
AppUiState(
settings,
tunnels,
tunnelState,
generalState,
+ autoTunnel,
)
}.stateIn(
viewModelScope + ioDispatcher,
@@ -95,7 +92,7 @@ constructor(
private suspend fun initAutoTunnel() {
val settings = appDataRepository.settings.getSettings()
- if (settings.isAutoTunnelEnabled) ServiceManager.startWatcherService(WireGuardAutoTunnel.instance)
+ if (settings.isAutoTunnelEnabled) serviceManager.startAutoTunnel(false)
}
fun onPinLockDisabled() = viewModelScope.launch(ioDispatcher) {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt
index b8d1583..1e2cddb 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt
@@ -39,9 +39,7 @@ import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.data.datastore.LocaleStorage
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
-import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
-import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavItem
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalFocusRequester
@@ -67,6 +65,7 @@ import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate
import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
import dagger.hilt.android.AndroidEntryPoint
+import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
@@ -100,17 +99,17 @@ class MainActivity : AppCompatActivity() {
val navController = rememberNavController()
val rootItemFocusRequester = remember { FocusRequester() }
- LaunchedEffect(appUiState.vpnState.status) {
- val context = this@MainActivity
- when (appUiState.vpnState.status) {
- TunnelState.DOWN -> ServiceManager.stopTunnelBackgroundService(context)
- else -> Unit
- }
- context.requestTunnelTileServiceStateUpdate()
+ LaunchedEffect(appUiState.tunnels) {
+ Timber.d("Updating launched")
+ requestTunnelTileServiceStateUpdate()
+ }
+
+ LaunchedEffect(appUiState.autoTunnelActive) {
+ requestAutoTunnelTileServiceUpdate()
}
with(appUiState.settings) {
- LaunchedEffect(isAutoTunnelPaused, isAutoTunnelEnabled) {
+ LaunchedEffect(isAutoTunnelEnabled) {
this@MainActivity.requestAutoTunnelTileServiceUpdate()
}
}
@@ -248,4 +247,3 @@ class MainActivity : AppCompatActivity() {
tunnelService.cancelStatsJob()
}
}
-
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ClickableIconButton.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ClickableIconButton.kt
index 5cf74b2..16ba4f3 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ClickableIconButton.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ClickableIconButton.kt
@@ -31,7 +31,7 @@ fun ClickableIconButton(onClick: () -> Unit, onIconClick: () -> Unit, text: Stri
if (enabled) {
onIconClick()
}
- },
+ },
)
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt
index 0109326..d0a1a41 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt
@@ -16,8 +16,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/IconSurfaceButton.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/IconSurfaceButton.kt
index 6ceb0c4..fefc063 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/IconSurfaceButton.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/IconSurfaceButton.kt
@@ -2,7 +2,6 @@ package com.zaneschepke.wireguardautotunnel.ui.common.button
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -31,21 +30,27 @@ import kotlin.let
@androidx.compose.runtime.Composable
fun IconSurfaceButton(title: String, onClick: () -> Unit, selected: Boolean, leadingIcon: ImageVector? = null, description: String? = null) {
val border: BorderStroke? =
- if (selected) BorderStroke(
- 1.dp,
- MaterialTheme.colorScheme.primary
- ) else null
- Card(
- modifier =
- Modifier
- .fillMaxWidth()
- .height(IntrinsicSize.Min),
- shape = RoundedCornerShape(8.dp),
- border = border,
- colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
- ) {
- Box(modifier = Modifier.clickable { onClick() }
- .fillMaxWidth()) {
+ if (selected) {
+ BorderStroke(
+ 1.dp,
+ MaterialTheme.colorScheme.primary,
+ )
+ } else {
+ null
+ }
+ Card(
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .height(IntrinsicSize.Min),
+ shape = RoundedCornerShape(8.dp),
+ border = border,
+ colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
+ ) {
+ Box(
+ modifier = Modifier.clickable { onClick() }
+ .fillMaxWidth(),
+ ) {
Column(
modifier =
Modifier
@@ -61,7 +66,7 @@ fun IconSurfaceButton(title: String, onClick: () -> Unit, selected: Boolean, lea
) {
Row(
horizontalArrangement = Arrangement.spacedBy(
- 16.dp.scaledWidth()
+ 16.dp.scaledWidth(),
),
verticalAlignment = Alignment.Companion.CenterVertically,
modifier = Modifier.padding(vertical = if (description == null) 10.dp.scaledHeight() else 0.dp),
@@ -77,7 +82,7 @@ fun IconSurfaceButton(title: String, onClick: () -> Unit, selected: Boolean, lea
Column {
Text(
title,
- style = MaterialTheme.typography.titleMedium
+ style = MaterialTheme.typography.titleMedium,
)
description?.let {
Text(
@@ -91,5 +96,5 @@ fun IconSurfaceButton(title: String, onClick: () -> Unit, selected: Boolean, lea
}
}
}
- }
+ }
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SelectionItem.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SelectionItem.kt
index 0260594..f92eec4 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SelectionItem.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SelectionItem.kt
@@ -1,7 +1,6 @@
package com.zaneschepke.wireguardautotunnel.ui.common.button.surface
import androidx.compose.runtime.Composable
-import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.vector.ImageVector
data class SelectionItem(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SurfaceSelectionGroupButton.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SurfaceSelectionGroupButton.kt
index 6dcedbb..cdea66c 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SurfaceSelectionGroupButton.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SurfaceSelectionGroupButton.kt
@@ -24,65 +24,63 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@Composable
fun SurfaceSelectionGroupButton(items: List) {
-
- Card(
- modifier = Modifier.fillMaxWidth(),
- shape = RoundedCornerShape(8.dp),
- colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
- ) {
- items.mapIndexed { index, item ->
- Box(
- contentAlignment = Alignment.Center,
- modifier = Modifier
- .then(item.onClick?.let { Modifier.clickable { it() }} ?: Modifier)
- .fillMaxWidth()
+ Card(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(8.dp),
+ colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
+ ) {
+ items.mapIndexed { index, item ->
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .then(item.onClick?.let { Modifier.clickable { it() } } ?: Modifier)
+ .fillMaxWidth(),
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp.scaledHeight()),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp.scaledHeight()),
+ modifier = Modifier
+ .padding(start = 16.dp.scaledWidth())
+ .weight(4f, false)
+ .fillMaxWidth(),
) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier
- .padding(start = 16.dp.scaledWidth())
- .weight(4f, false)
- .fillMaxWidth(),
- ) {
- item.leadingIcon?.let { icon ->
- Icon(
- icon,
- icon.name,
- modifier = Modifier.size(iconSize),
- )
- }
- Column(
- horizontalAlignment = Alignment.Start,
- verticalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterVertically),
- modifier = Modifier
- .fillMaxWidth()
- .padding(start = if (item.leadingIcon != null) 16.dp.scaledWidth() else 0.dp)
- .padding(vertical = if (item.description == null) 16.dp.scaledHeight() else 6.dp.scaledHeight()),
- ) {
- item.title()
- item.description?.let {
- it()
- }
- }
+ item.leadingIcon?.let { icon ->
+ Icon(
+ icon,
+ icon.name,
+ modifier = Modifier.size(iconSize),
+ )
}
- item.trailing?.let {
- Box(
- contentAlignment = Alignment.CenterEnd,
- modifier = Modifier
- .padding(end = 24.dp.scaledWidth(), start = 16.dp.scaledWidth())
- .weight(1f),
- ) {
+ Column(
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterVertically),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = if (item.leadingIcon != null) 16.dp.scaledWidth() else 0.dp)
+ .padding(vertical = if (item.description == null) 16.dp.scaledHeight() else 6.dp.scaledHeight()),
+ ) {
+ item.title()
+ item.description?.let {
it()
}
}
}
+ item.trailing?.let {
+ Box(
+ contentAlignment = Alignment.CenterEnd,
+ modifier = Modifier
+ .padding(end = 24.dp.scaledWidth(), start = 16.dp.scaledWidth())
+ .weight(1f),
+ ) {
+ it()
+ }
+ }
}
- if (index + 1 != items.size) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
}
+ if (index + 1 != items.size) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
}
}
-
+}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/config/SubmitConfigurationTextBox.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/config/SubmitConfigurationTextBox.kt
index bb78fe2..ce23ed9 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/config/SubmitConfigurationTextBox.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/config/SubmitConfigurationTextBox.kt
@@ -18,8 +18,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/GroupLabel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/GroupLabel.kt
index fd2eea4..59fc2ca 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/GroupLabel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/GroupLabel.kt
@@ -19,4 +19,3 @@ fun GroupLabel(title: String) {
)
}
}
-
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/VersionLabel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/VersionLabel.kt
index 16051a7..ba7702b 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/VersionLabel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/VersionLabel.kt
@@ -27,7 +27,7 @@ fun VersionLabel() {
color = MaterialTheme.colorScheme.outline,
modifier = Modifier.clickable {
clipboardManager.setText(AnnotatedString(BuildConfig.VERSION_NAME))
- }
+ },
)
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/BottomNavBar.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/BottomNavBar.kt
index 4155b95..b9f6cb1 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/BottomNavBar.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/BottomNavBar.kt
@@ -8,21 +8,11 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.input.key.Key
-import androidx.compose.ui.input.key.KeyEventType
-import androidx.compose.ui.input.key.key
-import androidx.compose.ui.input.key.onKeyEvent
-import androidx.compose.ui.input.key.type
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.currentBackStackEntryAsState
-import timber.log.Timber
@Composable
fun BottomNavBar(navController: NavController, bottomNavItems: List) {
@@ -34,7 +24,6 @@ fun BottomNavBar(navController: NavController, bottomNavItems: List {
}
val LocalFocusRequester = compositionLocalOf { error("FocusRequester is not provided") }
-
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/TopNavBar.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/TopNavBar.kt
index 9feb69b..494ee5f 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/TopNavBar.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/TopNavBar.kt
@@ -18,12 +18,14 @@ fun TopNavBar(title: String, trailing: @Composable () -> Unit = {}, showBack: Bo
Text(title)
},
navigationIcon = {
- if(showBack) IconButton(onClick = { navController.popBackStack() }) {
- val icon = Icons.AutoMirrored.Outlined.ArrowBack
- Icon(
- imageVector = icon,
- contentDescription = icon.name,
- )
+ if (showBack) {
+ IconButton(onClick = { navController.popBackStack() }) {
+ val icon = Icons.AutoMirrored.Outlined.ArrowBack
+ Icon(
+ imageVector = icon,
+ contentDescription = icon.name,
+ )
+ }
}
},
actions = {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt
index a2a859f..2a6a10c 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt
@@ -234,7 +234,7 @@ fun ConfigScreen(tunnelId: Int) {
hint = stringResource(R.string.tunnel_name).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
OutlinedTextField(
modifier =
@@ -347,7 +347,7 @@ fun ConfigScreen(tunnelId: Int) {
hint = stringResource(R.string.junk_packet_count).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.junkPacketMinSize,
@@ -360,7 +360,7 @@ fun ConfigScreen(tunnelId: Int) {
).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.junkPacketMaxSize,
@@ -373,7 +373,7 @@ fun ConfigScreen(tunnelId: Int) {
).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.initPacketJunkSize,
@@ -383,7 +383,7 @@ fun ConfigScreen(tunnelId: Int) {
hint = stringResource(R.string.init_packet_junk_size).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.responsePacketJunkSize,
@@ -396,7 +396,7 @@ fun ConfigScreen(tunnelId: Int) {
).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.initPacketMagicHeader,
@@ -409,7 +409,7 @@ fun ConfigScreen(tunnelId: Int) {
).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.responsePacketMagicHeader,
@@ -422,7 +422,7 @@ fun ConfigScreen(tunnelId: Int) {
).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.underloadPacketMagicHeader,
@@ -435,7 +435,7 @@ fun ConfigScreen(tunnelId: Int) {
).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.transportPacketMagicHeader,
@@ -448,7 +448,7 @@ fun ConfigScreen(tunnelId: Int) {
).lowercase(),
modifier =
Modifier
- .fillMaxWidth()
+ .fillMaxWidth(),
)
}
Row(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt
index db40bfd..eeffcc8 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt
@@ -1,7 +1,11 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.main
+import android.content.Intent
+import android.net.Uri
import android.net.VpnService
+import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -53,9 +57,9 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.main.components.TunnelImpo
import com.zaneschepke.wireguardautotunnel.ui.screens.main.components.TunnelRowItem
import com.zaneschepke.wireguardautotunnel.ui.screens.main.components.VpnDeniedDialog
import com.zaneschepke.wireguardautotunnel.util.Constants
+import com.zaneschepke.wireguardautotunnel.util.extensions.isBatteryOptimizationsDisabled
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
-import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
@OptIn(ExperimentalFoundationApi::class)
@Composable
@@ -75,13 +79,19 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
NestedScrollListener({ isFabVisible = false }, { isFabVisible = true })
}
- val vpnActivityResultState =
+ val vpnActivity =
rememberLauncherForActivityResult(
ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != RESULT_OK) showVpnPermissionDialog = true
},
)
+ val batteryActivity =
+ rememberLauncherForActivityResult(
+ ActivityResultContracts.StartActivityForResult(),
+ ) { result: ActivityResult ->
+ viewModel.setBatteryOptimizeDisableShown()
+ }
val tunnelFileImportResultLauncher = rememberFileImportLauncherForResult(onNoFileExplorer = {
snackbar.showMessage(
@@ -104,7 +114,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
InfoDialog(
onDismiss = { showDeleteTunnelAlertDialog = false },
onAttest = {
- selectedTunnel?.let { viewModel.onDelete(it, context) }
+ selectedTunnel?.let { viewModel::onDelete }
showDeleteTunnelAlertDialog = false
selectedTunnel = null
},
@@ -114,15 +124,35 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
)
}
+ fun requestBatteryOptimizationsDisabled() {
+ val intent =
+ Intent().apply {
+ action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
+ data = Uri.parse("package:${context.packageName}")
+ }
+ batteryActivity.launch(intent)
+ }
+
+ fun onAutoTunnelToggle() {
+ if (!uiState.generalState.isBatteryOptimizationDisableShown &&
+ !context.isBatteryOptimizationsDisabled() && !isRunningOnTv
+ ) {
+ return requestBatteryOptimizationsDisabled()
+ }
+ val intent = if (!uiState.settings.isKernelEnabled) {
+ VpnService.prepare(context)
+ } else {
+ null
+ }
+ if (intent != null) return vpnActivity.launch(intent)
+ viewModel.onToggleAutoTunnel()
+ }
+
fun onTunnelToggle(checked: Boolean, tunnel: TunnelConfig) {
val intent = if (uiState.settings.isKernelEnabled) null else VpnService.prepare(context)
- if (intent != null) return vpnActivityResultState.launch(intent)
+ if (intent != null) return vpnActivity.launch(intent)
if (!checked) viewModel.onTunnelStop(tunnel).also { return }
- if (uiState.settings.isKernelEnabled) {
- context.startTunnelBackground(tunnel.id)
- } else {
- viewModel.onTunnelStart(tunnel)
- }
+ viewModel.onTunnelStart(tunnel, uiState.settings.isKernelEnabled)
}
Scaffold(
@@ -137,34 +167,38 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
},
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = {
- if(!isRunningOnTv) ScrollDismissFab({
- val icon = Icons.Filled.Add
- Icon(
- imageVector = icon,
- contentDescription = icon.name,
- tint = MaterialTheme.colorScheme.onPrimary,
- )
- }, isVisible = isFabVisible, onClick = {
- showBottomSheet = true
- })
+ if (!isRunningOnTv) {
+ ScrollDismissFab({
+ val icon = Icons.Filled.Add
+ Icon(
+ imageVector = icon,
+ contentDescription = icon.name,
+ tint = MaterialTheme.colorScheme.onPrimary,
+ )
+ }, isVisible = isFabVisible, onClick = {
+ showBottomSheet = true
+ })
+ }
},
topBar = {
- if(isRunningOnTv) TopNavBar(
- showBack = false,
- title = stringResource(R.string.app_name),
- trailing = {
- IconButton(onClick = {
- showBottomSheet = true
- }) {
- val icon = Icons.Outlined.Add
- Icon(
- imageVector = icon,
- contentDescription = icon.name,
- )
- }
- }
- )
- }
+ if (isRunningOnTv) {
+ TopNavBar(
+ showBack = false,
+ title = stringResource(R.string.app_name),
+ trailing = {
+ IconButton(onClick = {
+ showBottomSheet = true
+ }) {
+ val icon = Icons.Outlined.Add
+ Icon(
+ imageVector = icon,
+ contentDescription = icon.name,
+ )
+ }
+ },
+ )
+ }
+ },
) {
TunnelImportSheet(
showBottomSheet,
@@ -196,7 +230,9 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
}
} else {
item {
- AutoTunnelRowItem(uiState.settings, { viewModel.onToggleAutoTunnel(context) })
+ AutoTunnelRowItem(uiState, {
+ onAutoTunnelToggle()
+ })
}
}
items(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainViewModel.kt
index 5454e47..bd3d24b 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainViewModel.kt
@@ -30,26 +30,24 @@ import timber.log.Timber
import java.io.InputStream
import java.util.zip.ZipInputStream
import javax.inject.Inject
+import javax.inject.Provider
@HiltViewModel
class MainViewModel
@Inject
constructor(
private val appDataRepository: AppDataRepository,
- val tunnelService: TunnelService,
+ private val tunnelService: Provider,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
+ private val serviceManager: ServiceManager,
) : ViewModel() {
- private fun stopWatcherService(context: Context) {
- ServiceManager.stopWatcherService(context)
- }
-
- fun onDelete(tunnel: TunnelConfig, context: Context) {
+ fun onDelete(tunnel: TunnelConfig) {
viewModelScope.launch {
val settings = appDataRepository.settings.getSettings()
val isPrimary = tunnel.isPrimaryTunnel
if (appDataRepository.tunnels.count() == 1 || isPrimary) {
- stopWatcherService(context)
+ serviceManager.stopAutoTunnel()
resetTunnelSetting(settings)
}
appDataRepository.tunnels.delete(tunnel)
@@ -69,14 +67,14 @@ constructor(
appDataRepository.appState.setTunnelStatsExpanded(expanded)
}
- fun onTunnelStart(tunnelConfig: TunnelConfig) = viewModelScope.launch {
+ fun onTunnelStart(tunnelConfig: TunnelConfig, background: Boolean) = viewModelScope.launch {
Timber.i("Starting tunnel ${tunnelConfig.name}")
- tunnelService.startTunnel(tunnelConfig)
+ tunnelService.get().startTunnel(tunnelConfig, background)
}
fun onTunnelStop(tunnel: TunnelConfig) = viewModelScope.launch {
Timber.i("Stopping active tunnel")
- tunnelService.stopTunnel(tunnel)
+ tunnelService.get().stopTunnel(tunnel)
}
private fun generateQrCodeDefaultName(config: String): String {
@@ -160,16 +158,17 @@ constructor(
}
}
- fun onToggleAutoTunnel(context: Context) = viewModelScope.launch {
+ fun onToggleAutoTunnel() = viewModelScope.launch {
val settings = appDataRepository.settings.getSettings()
- if (settings.isAutoTunnelEnabled) {
- ServiceManager.stopWatcherService(context)
+ val toggled = !settings.isAutoTunnelEnabled
+ if (toggled) {
+ serviceManager.startAutoTunnel(false)
} else {
- ServiceManager.startWatcherService(context)
+ serviceManager.stopAutoTunnel()
}
appDataRepository.settings.save(
settings.copy(
- isAutoTunnelEnabled = !settings.isAutoTunnelEnabled,
+ isAutoTunnelEnabled = toggled,
),
)
}
@@ -195,6 +194,10 @@ constructor(
}
}
+ fun setBatteryOptimizeDisableShown() = viewModelScope.launch {
+ appDataRepository.appState.setBatteryOptimizationDisableShown(true)
+ }
+
private suspend fun saveTunnelFromConfUri(name: String, uri: Uri, context: Context) {
val stream = getInputStreamFromUri(uri, context) ?: throw FileReadException
saveTunnelConfigFromStream(stream, name)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/AutoTunnelRowItem.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/AutoTunnelRowItem.kt
index bf2fe1e..efff234 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/AutoTunnelRowItem.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/AutoTunnelRowItem.kt
@@ -4,30 +4,25 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Bolt
import androidx.compose.material3.Icon
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import com.zaneschepke.wireguardautotunnel.R
-import com.zaneschepke.wireguardautotunnel.data.domain.Settings
+import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.common.ExpandingRowListItem
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
-import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
@Composable
-fun AutoTunnelRowItem(settings: Settings, onToggle: () -> Unit) {
+fun AutoTunnelRowItem(appUiState: AppUiState, onToggle: () -> Unit) {
val context = LocalContext.current
val itemFocusRequester = remember { FocusRequester() }
ExpandingRowListItem(
@@ -40,7 +35,7 @@ fun AutoTunnelRowItem(settings: Settings, onToggle: () -> Unit) {
Modifier
.size(16.dp.scaledHeight()).scale(1.5f),
tint =
- if (!settings.isAutoTunnelEnabled) {
+ if (!appUiState.autoTunnelActive) {
Color.Gray
} else {
SilverTree
@@ -50,10 +45,10 @@ fun AutoTunnelRowItem(settings: Settings, onToggle: () -> Unit) {
text = stringResource(R.string.auto_tunneling),
trailing = {
ScaledSwitch(
- settings.isAutoTunnelEnabled,
+ appUiState.settings.isAutoTunnelEnabled,
onClick = {
onToggle()
- }
+ },
)
},
onClick = {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/ScrollDismissMultiFab.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/ScrollDismissMultiFab.kt
index dc750fd..b2cf6c9 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/ScrollDismissMultiFab.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/ScrollDismissMultiFab.kt
@@ -9,8 +9,6 @@ import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.unit.dp
@Composable
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelRowItem.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelRowItem.kt
index 35011ff..82d8f55 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelRowItem.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelRowItem.kt
@@ -182,14 +182,14 @@ fun TunnelRowItem(
ScaledSwitch(
modifier = Modifier.focusRequester(itemFocusRequester),
checked = isActive,
- onClick = onSwitchClick
+ onClick = onSwitchClick,
)
}
} else {
ScaledSwitch(
modifier = Modifier.focusRequester(itemFocusRequester),
checked = isActive,
- onClick = onSwitchClick
+ onClick = onSwitchClick,
)
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelStatisticsRow.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelStatisticsRow.kt
index c07fdc4..1d06ec7 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelStatisticsRow.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelStatisticsRow.kt
@@ -46,14 +46,18 @@ fun TunnelStatisticsRow(statistics: TunnelStatistics?, tunnelConfig: TunnelConfi
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
- Text(stringResource(R.string.peer).lowercase() + ": $peerId", style = MaterialTheme.typography.bodySmall)
- Text("tx: $peerTxMB MB", style = MaterialTheme.typography.bodySmall)
+ Text(
+ stringResource(R.string.peer).lowercase() + ": $peerId",
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.outline,
+ )
+ Text("tx: $peerTxMB MB", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.outline)
}
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
- Text(stringResource(R.string.handshake) + ": $handshake", style = MaterialTheme.typography.bodySmall)
- Text("rx: $peerRxMB MB", style = MaterialTheme.typography.bodySmall)
+ Text(stringResource(R.string.handshake) + ": $handshake", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.outline)
+ Text("rx: $peerRxMB MB", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.outline)
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsScreen.kt
index d60c2db..c79c90d 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsScreen.kt
@@ -4,15 +4,10 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.NetworkPing
@@ -27,18 +22,11 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.input.key.Key
-import androidx.compose.ui.input.key.KeyEventType
-import androidx.compose.ui.input.key.key
-import androidx.compose.ui.input.key.onKeyEvent
-import androidx.compose.ui.input.key.type
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@@ -56,7 +44,6 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.compon
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
-import kotlinx.coroutines.delay
@OptIn(ExperimentalLayoutApi::class)
@Composable
@@ -84,7 +71,7 @@ fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiSta
)
}
})
- }
+ },
) {
Column(
horizontalAlignment = Alignment.Start,
@@ -101,7 +88,12 @@ fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiSta
listOf(
SelectionItem(
Icons.Outlined.Star,
- title = { Text(stringResource(R.string.primary_tunnel), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ title = {
+ Text(
+ stringResource(R.string.primary_tunnel),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
description = {
Text(
stringResource(R.string.set_primary_tunnel),
@@ -114,7 +106,7 @@ fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiSta
onClick = { optionsViewModel.onTogglePrimaryTunnel(config) },
)
},
- onClick = { optionsViewModel.onTogglePrimaryTunnel(config) }
+ onClick = { optionsViewModel.onTogglePrimaryTunnel(config) },
),
SelectionItem(
Icons.Outlined.PhoneAndroid,
@@ -131,7 +123,7 @@ fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiSta
onClick = { optionsViewModel.onToggleIsMobileDataTunnel(config) },
)
},
- onClick = { optionsViewModel.onToggleIsMobileDataTunnel(config) }
+ onClick = { optionsViewModel.onToggleIsMobileDataTunnel(config) },
),
SelectionItem(
Icons.Outlined.NetworkPing,
@@ -147,7 +139,7 @@ fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiSta
onClick = { optionsViewModel.onToggleRestartOnPing(config) },
)
},
- onClick = { optionsViewModel.onToggleRestartOnPing(config) }
+ onClick = { optionsViewModel.onToggleRestartOnPing(config) },
),
SelectionItem(
title = {
@@ -180,24 +172,25 @@ fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiSta
style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
)
}
-
}
}
-
},
description = {
TrustedNetworkTextBox(
- config.tunnelNetworks, onDelete = { optionsViewModel.onDeleteRunSSID(it, config) },
+ config.tunnelNetworks,
+ onDelete = { optionsViewModel.onDeleteRunSSID(it, config) },
currentText = currentText,
onSave = { optionsViewModel.onSaveRunSSID(it, config) },
onValueChange = { currentText = it },
- supporting = { if(appUiState.generalState.isWildcardsEnabled) {
- WildcardsLabel()
- }}
+ supporting = {
+ if (appUiState.settings.isWildcardsEnabled) {
+ WildcardsLabel()
+ }
+ },
)
},
- )
- )
+ ),
+ ),
)
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsViewModel.kt
index d7d3236..6fc4f96 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsViewModel.kt
@@ -32,7 +32,7 @@ constructor(
}
fun onSaveRunSSID(ssid: String, tunnelConfig: TunnelConfig) = viewModelScope.launch {
- if(ssid.isBlank()) return@launch
+ if (ssid.isBlank()) return@launch
val trimmed = ssid.trim()
val tunnelsWithName = appDataRepository.tunnels.findByTunnelNetworksName(trimmed)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt
index 09a4067..08027f7 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt
@@ -1,26 +1,13 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.settings
-import android.content.Context.POWER_SERVICE
-import android.content.Intent
-import android.net.Uri
-import android.net.VpnService
-import android.os.PowerManager
-import android.provider.Settings
-import androidx.activity.compose.rememberLauncherForActivityResult
-import androidx.activity.result.ActivityResult
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity.RESULT_OK
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
@@ -49,7 +36,6 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
@@ -63,13 +49,9 @@ import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalFocusReques
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
-import com.zaneschepke.wireguardautotunnel.ui.screens.main.components.VpnDeniedDialog
-import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.BackgroundLocationDialog
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.ForwardButton
-import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.LocationServicesDialog
import com.zaneschepke.wireguardautotunnel.ui.theme.topPadding
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
-import com.zaneschepke.wireguardautotunnel.util.extensions.launchAppSettings
import com.zaneschepke.wireguardautotunnel.util.extensions.launchNotificationSettings
import com.zaneschepke.wireguardautotunnel.util.extensions.launchVpnSettings
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
@@ -91,114 +73,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
val isRunningOnTv = remember { context.isRunningOnTv() }
val interactionSource = remember { MutableInteractionSource() }
-
- val settingsUiState by viewModel.uiState.collectAsStateWithLifecycle()
-
- var showVpnPermissionDialog by remember { mutableStateOf(false) }
- var showLocationServicesAlertDialog by remember { mutableStateOf(false) }
var showAuthPrompt by remember { mutableStateOf(false) }
- var showLocationDialog by remember { mutableStateOf(false) }
-
- val startForResult =
- rememberLauncherForActivityResult(
- ActivityResultContracts.StartActivityForResult(),
- ) { result: ActivityResult ->
- if (result.resultCode == RESULT_OK) {
- result.data
- // Handle the Intent
- }
- viewModel.setBatteryOptimizeDisableShown()
- }
-
- val vpnActivityResultState =
- rememberLauncherForActivityResult(
- ActivityResultContracts.StartActivityForResult(),
- onResult = {
- val accepted = (it.resultCode == RESULT_OK)
- if (!accepted) {
- showVpnPermissionDialog = true
- }
- },
- )
-
- fun isBatteryOptimizationsDisabled(): Boolean {
- val pm = context.getSystemService(POWER_SERVICE) as PowerManager
- return pm.isIgnoringBatteryOptimizations(context.packageName)
- }
-
- fun requestBatteryOptimizationsDisabled() {
- val intent =
- Intent().apply {
- action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- data = Uri.parse("package:${context.packageName}")
- }
- startForResult.launch(intent)
- }
-
-// fun handleAutoTunnelToggle() {
-// if (!uiState.generalState.isBatteryOptimizationDisableShown &&
-// !isBatteryOptimizationsDisabled() && !isRunningOnTv
-// ) {
-// return requestBatteryOptimizationsDisabled()
-// }
-// val intent = if (!uiState.settings.isKernelEnabled) {
-// VpnService.prepare(context)
-// } else {
-// null
-// }
-// if (intent != null) return vpnActivityResultState.launch(intent)
-// viewModel.onToggleAutoTunnel(context)
-// }
-
-
-
-
-// fun checkFineLocationGranted() {
-// isBackgroundLocationGranted =
-// if (!fineLocationState.status.isGranted) {
-// false
-// } else {
-// viewModel.setLocationDisclosureShown()
-// true
-// }
-// }
-
-// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-// if (
-// isRunningOnTv &&
-// Build.VERSION.SDK_INT == Build.VERSION_CODES.Q
-// ) {
-// checkFineLocationGranted()
-// } else {
-// val backgroundLocationState =
-// rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
-// isBackgroundLocationGranted =
-// if (!backgroundLocationState.status.isGranted) {
-// false
-// } else {
-// SideEffect { viewModel.setLocationDisclosureShown() }
-// true
-// }
-// }
-// }
-//
-// if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
-// checkFineLocationGranted()
-// }
-
- BackgroundLocationDialog(
- showLocationDialog,
- onDismiss = { showLocationDialog = false },
- onAttest = { showLocationDialog = false },
- )
-
-// LocationServicesDialog(
-// showLocationServicesAlertDialog,
-// onDismiss = { showVpnPermissionDialog = false },
-// onAttest = { handleAutoTunnelToggle() },
-// )
-
- VpnDeniedDialog(showVpnPermissionDialog, onDismiss = { showVpnPermissionDialog = false })
if (showAuthPrompt) {
AuthorizationPrompt(
@@ -231,12 +106,18 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
.padding(top = topPadding)
.padding(bottom = 40.dp.scaledHeight())
.padding(horizontal = 24.dp.scaledWidth())
- .then(if(!isRunningOnTv) Modifier.clickable(
- indication = null,
- interactionSource = interactionSource,
- ) {
- focusManager.clearFocus()
- } else Modifier)
+ .then(
+ if (!isRunningOnTv) {
+ Modifier.clickable(
+ indication = null,
+ interactionSource = interactionSource,
+ ) {
+ focusManager.clearFocus()
+ }
+ } else {
+ Modifier
+ },
+ ),
) {
SurfaceSelectionGroupButton(
listOf(
@@ -250,73 +131,78 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
)
},
onClick = {
- if(!uiState.generalState.isLocationDisclosureShown) return@SelectionItem navController.navigate(Route.LocationDisclosure)
+ if (!uiState.generalState.isLocationDisclosureShown) return@SelectionItem navController.navigate(Route.LocationDisclosure)
navController.navigate(Route.AutoTunnel)
},
trailing = {
ForwardButton(Modifier.focusable().focusRequester(rootFocusRequester)) { navController.navigate(Route.AutoTunnel) }
},
- )
- )
+ ),
+ ),
)
SurfaceSelectionGroupButton(
buildList {
- if (!isRunningOnTv) addAll(
- listOf(
- SelectionItem(
- Icons.Filled.AppShortcut,
- {
- ScaledSwitch(
- uiState.settings.isShortcutsEnabled,
- onClick = { viewModel.onToggleShortcutsEnabled() },
- )
- },
- title = {
- Text(
- stringResource(R.string.enabled_app_shortcuts),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface))
- },
- onClick = { viewModel.onToggleShortcutsEnabled() }
+ if (!isRunningOnTv) {
+ addAll(
+ listOf(
+ SelectionItem(
+ Icons.Filled.AppShortcut,
+ {
+ ScaledSwitch(
+ uiState.settings.isShortcutsEnabled,
+ onClick = { viewModel.onToggleShortcutsEnabled() },
+ )
+ },
+ title = {
+ Text(
+ stringResource(R.string.enabled_app_shortcuts),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ onClick = { viewModel.onToggleShortcutsEnabled() },
+ ),
+ SelectionItem(
+ Icons.Outlined.VpnLock,
+ {
+ ScaledSwitch(
+ enabled = !(
+ (
+ uiState.settings.isTunnelOnWifiEnabled ||
+ uiState.settings.isTunnelOnEthernetEnabled ||
+ uiState.settings.isTunnelOnMobileDataEnabled
+ ) &&
+ uiState.settings.isAutoTunnelEnabled
+ ),
+ onClick = { viewModel.onToggleAlwaysOnVPN() },
+ checked = uiState.settings.isAlwaysOnVpnEnabled,
+ )
+ },
+ title = {
+ Text(
+ stringResource(R.string.always_on_vpn_support),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ onClick = { viewModel.onToggleAlwaysOnVPN() },
+ ),
+ SelectionItem(
+ Icons.Outlined.AdminPanelSettings,
+ title = {
+ Text(
+ stringResource(R.string.kill_switch),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ onClick = {
+ context.launchVpnSettings()
+ },
+ trailing = {
+ ForwardButton { context.launchVpnSettings() }
+ },
+ ),
),
- SelectionItem(
- Icons.Outlined.VpnLock,
- {
- ScaledSwitch(
- enabled = !(
- (
- uiState.settings.isTunnelOnWifiEnabled ||
- uiState.settings.isTunnelOnEthernetEnabled ||
- uiState.settings.isTunnelOnMobileDataEnabled
- ) &&
- uiState.settings.isAutoTunnelEnabled
- ),
- onClick = { viewModel.onToggleAlwaysOnVPN() },
- checked = uiState.settings.isAlwaysOnVpnEnabled,
- )
- },
- title = {
- Text(
- stringResource(R.string.always_on_vpn_support),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface))
- },
- onClick = { viewModel.onToggleAlwaysOnVPN() }
- ),
- SelectionItem(
- Icons.Outlined.AdminPanelSettings,
- title = {
- Text(
- stringResource(R.string.kill_switch),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface))
- },
- onClick = {
- context.launchVpnSettings()
- },
- trailing = {
- ForwardButton { context.launchVpnSettings() }
- },
- )
)
- )
+ }
add(
SelectionItem(
Icons.Outlined.Restore,
@@ -329,25 +215,27 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
title = {
Text(
stringResource(R.string.restart_at_boot),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface))
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
},
- onClick = { viewModel.onToggleRestartAtBoot() }
- )
+ onClick = { viewModel.onToggleRestartAtBoot() },
+ ),
)
- }
+ },
)
SurfaceSelectionGroupButton(
- listOf(SelectionItem(
- Icons.AutoMirrored.Outlined.ViewQuilt,
- title = { Text(stringResource(R.string.appearance), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
- onClick = {
- navController.navigate(Route.Appearance)
- },
- trailing = {
- ForwardButton { navController.navigate(Route.Appearance) }
- },
- ),
+ listOf(
+ SelectionItem(
+ Icons.AutoMirrored.Outlined.ViewQuilt,
+ title = { Text(stringResource(R.string.appearance), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ onClick = {
+ navController.navigate(Route.Appearance)
+ },
+ trailing = {
+ ForwardButton { navController.navigate(Route.Appearance) }
+ },
+ ),
SelectionItem(
Icons.Outlined.Notifications,
title = { Text(stringResource(R.string.notifications), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
@@ -360,7 +248,12 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
),
SelectionItem(
Icons.Outlined.Pin,
- title = { Text(stringResource(R.string.enable_app_lock), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ title = {
+ Text(
+ stringResource(R.string.enable_app_lock),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
trailing = {
ScaledSwitch(
uiState.generalState.isPinLockEnabled,
@@ -374,370 +267,67 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
},
)
},
- onClick = { if (uiState.generalState.isPinLockEnabled) {
- appViewModel.onPinLockDisabled()
- } else {
- PinManager.initialize(context)
- navController.navigate(Route.Lock)
- } }
- )
- ))
-
- if(!isRunningOnTv) SurfaceSelectionGroupButton(listOf(
- SelectionItem(
- Icons.Outlined.Code,
- title = { Text(stringResource(R.string.kernel), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
- description = {
- Text(
- stringResource(R.string.use_kernel),
- style = MaterialTheme.typography.bodySmall.copy(MaterialTheme.colorScheme.outline),
- )
- },
- trailing = {
- ScaledSwitch(
- uiState.settings.isKernelEnabled,
- onClick = { viewModel.onToggleKernelMode() },
- enabled = !(
- uiState.settings.isAutoTunnelEnabled ||
- uiState.settings.isAlwaysOnVpnEnabled ||
- (uiState.vpnState.status == TunnelState.UP) ||
- !settingsUiState.isKernelAvailable
- ),
- )
- },
- onClick = {
- viewModel.onToggleKernelMode()
- }
- ),
- ))
-
- if(!isRunningOnTv) SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- Icons.Outlined.FolderZip,
- title = { Text(stringResource(R.string.export_configs), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
onClick = {
- if (uiState.tunnels.isEmpty()) return@SelectionItem context.showToast(R.string.tunnel_required)
- showAuthPrompt = true
+ if (uiState.generalState.isPinLockEnabled) {
+ appViewModel.onPinLockDisabled()
+ } else {
+ PinManager.initialize(context)
+ navController.navigate(Route.Lock)
+ }
},
),
- )
+ ),
)
+ if (!isRunningOnTv) {
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.Code,
+ title = { Text(stringResource(R.string.kernel), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ description = {
+ Text(
+ stringResource(R.string.use_kernel),
+ style = MaterialTheme.typography.bodySmall.copy(MaterialTheme.colorScheme.outline),
+ )
+ },
+ trailing = {
+ ScaledSwitch(
+ uiState.settings.isKernelEnabled,
+ onClick = { viewModel.onToggleKernelMode() },
+ enabled = !(
+ uiState.settings.isAutoTunnelEnabled ||
+ uiState.settings.isAlwaysOnVpnEnabled ||
+ (uiState.vpnState.status == TunnelState.UP)
+ ),
+ )
+ },
+ onClick = {
+ viewModel.onToggleKernelMode()
+ },
+ ),
+ ),
+ )
+ }
-
-
-
-// Surface(
-// tonalElevation = 2.dp,
-// shadowElevation = 2.dp,
-// shape = RoundedCornerShape(12.dp),
-// color = MaterialTheme.colorScheme.surface,
-// modifier =
-// (
-// if (isRunningOnTv) {
-// Modifier
-// .height(IntrinsicSize.Min)
-// .fillMaxWidth(fillMaxWidth)
-// .padding(top = 10.dp)
-// } else {
-// Modifier
-// .fillMaxWidth(fillMaxWidth)
-// .padding(top = 20.dp)
-// }
-// )
-// .padding(bottom = 10.dp),
-// ) {
-// Column(
-// horizontalAlignment = Alignment.Start,
-// verticalArrangement = Arrangement.Top,
-// modifier = Modifier.padding(15.dp),
-// ) {
-// SectionTitle(
-// title = stringResource(id = R.string.auto_tunneling),
-// padding = screenPadding,
-// )
-// ConfigurationToggle(
-// stringResource(id = R.string.tunnel_on_wifi),
-// enabled = !uiState.settings.isAlwaysOnVpnEnabled,
-// checked = uiState.settings.isTunnelOnWifiEnabled,
-// onCheckChanged = { checked ->
-// if (!checked || settingsUiState.isRooted) viewModel.onToggleTunnelOnWifi().also { return@ConfigurationToggle }
-// onAutoTunnelWifiChecked()
-// },
-// modifier =
-// if (uiState.settings.isAutoTunnelEnabled) {
-// Modifier
-// } else {
-// Modifier
-// .focusRequester(focusRequester)
-// },
-// )
-// if (uiState.settings.isTunnelOnWifiEnabled) {
-// Column {
-// FlowRow(
-// modifier =
-// Modifier
-// .padding(screenPadding)
-// .fillMaxWidth(),
-// horizontalArrangement = Arrangement.spacedBy(5.dp),
-// ) {
-// uiState.settings.trustedNetworkSSIDs.forEach { ssid ->
-// ClickableIconButton(
-// onClick = {
-// if (isRunningOnTv) {
-// focusRequester.requestFocus()
-// viewModel.onDeleteTrustedSSID(ssid)
-// }
-// },
-// onIconClick = {
-// if (isRunningOnTv) focusRequester.requestFocus()
-// viewModel.onDeleteTrustedSSID(ssid)
-// },
-// text = ssid,
-// icon = Icons.Filled.Close,
-// )
-// }
-// if (uiState.settings.trustedNetworkSSIDs.isEmpty()) {
-// Text(
-// stringResource(R.string.none),
-// fontStyle = FontStyle.Italic,
-// style = MaterialTheme.typography.bodySmall,
-// color = MaterialTheme.colorScheme.onSurface,
-// )
-// }
-// }
-// OutlinedTextField(
-// value = currentText,
-// onValueChange = { currentText = it },
-// label = { Text(stringResource(R.string.add_trusted_ssid)) },
-// modifier =
-// Modifier
-// .padding(
-// start = screenPadding,
-// top = 5.dp,
-// bottom = 10.dp,
-// ),
-// supportingText = { WildcardSupportingLabel { context.openWebUrl(it) } },
-// maxLines = 1,
-// keyboardOptions =
-// KeyboardOptions(
-// capitalization = KeyboardCapitalization.None,
-// imeAction = ImeAction.Done,
-// ),
-// keyboardActions = KeyboardActions(onDone = { saveTrustedSSID() }),
-// trailingIcon = {
-// if (currentText != "") {
-// IconButton(onClick = { saveTrustedSSID() }) {
-// Icon(
-// imageVector = Icons.Outlined.Add,
-// contentDescription =
-// if (currentText == "") {
-// stringResource(
-// id =
-// R.string
-// .trusted_ssid_empty_description,
-// )
-// } else {
-// stringResource(
-// id =
-// R.string
-// .trusted_ssid_value_description,
-// )
-// },
-// tint = MaterialTheme.colorScheme.primary,
-// )
-// }
-// }
-// },
-// )
-// }
-// }
-// ConfigurationToggle(
-// stringResource(R.string.tunnel_mobile_data),
-// enabled = !uiState.settings.isAlwaysOnVpnEnabled,
-// checked = uiState.settings.isTunnelOnMobileDataEnabled,
-// onCheckChanged = { viewModel.onToggleTunnelOnMobileData() },
-// )
-// ConfigurationToggle(
-// stringResource(id = R.string.tunnel_on_ethernet),
-// enabled = !uiState.settings.isAlwaysOnVpnEnabled,
-// checked = uiState.settings.isTunnelOnEthernetEnabled,
-// onCheckChanged = { viewModel.onToggleTunnelOnEthernet() },
-// )
-// ConfigurationToggle(
-// stringResource(R.string.restart_on_ping),
-// checked = uiState.settings.isPingEnabled,
-// onCheckChanged = { viewModel.onToggleRestartOnPing() },
-// )
-// Row(
-// verticalAlignment = Alignment.CenterVertically,
-// modifier =
-// (
-// if (!uiState.settings.isAutoTunnelEnabled) {
-// Modifier
-// } else {
-// Modifier.focusRequester(
-// focusRequester,
-// )
-// }
-// )
-// .fillMaxSize()
-// .padding(top = 5.dp),
-// horizontalArrangement = Arrangement.Center,
-// ) {
-// TextButton(
-// onClick = {
-// if (uiState.tunnels.isEmpty()) return@TextButton context.showToast(R.string.tunnel_required)
-// handleAutoTunnelToggle()
-// },
-// ) {
-// val autoTunnelButtonText =
-// if (uiState.settings.isAutoTunnelEnabled) {
-// stringResource(R.string.disable_auto_tunnel)
-// } else {
-// stringResource(id = R.string.enable_auto_tunnel)
-// }
-// Text(autoTunnelButtonText)
-// }
-// }
-// }
-// }
-// Surface(
-// tonalElevation = 2.dp,
-// shadowElevation = 2.dp,
-// shape = RoundedCornerShape(12.dp),
-// color = MaterialTheme.colorScheme.surface,
-// modifier =
-// Modifier
-// .fillMaxWidth(fillMaxWidth)
-// .padding(vertical = 10.dp),
-// ) {
-// Column(
-// horizontalAlignment = Alignment.Start,
-// verticalArrangement = Arrangement.Top,
-// modifier = Modifier.padding(15.dp),
-// ) {
-// SectionTitle(
-// title = stringResource(id = R.string.backend),
-// padding = screenPadding,
-// )
-// ConfigurationToggle(
-// stringResource(R.string.use_kernel),
-// enabled =
-// !(
-// uiState.settings.isAutoTunnelEnabled ||
-// uiState.settings.isAlwaysOnVpnEnabled ||
-// (uiState.vpnState.status == TunnelState.UP) ||
-// !settingsUiState.isKernelAvailable
-// ),
-// checked = uiState.settings.isKernelEnabled,
-// onCheckChanged = {
-// viewModel.onToggleKernelMode()
-// },
-// )
-// Row(
-// verticalAlignment = Alignment.CenterVertically,
-// modifier =
-// Modifier
-// .fillMaxSize()
-// .padding(top = 5.dp),
-// horizontalArrangement = Arrangement.Center,
-// ) {
-// TextButton(
-// onClick = {
-// viewModel.onRequestRoot()
-// },
-// ) {
-// Text(stringResource(R.string.request_root))
-// }
-// }
-// }
-// }
-// Surface(
-// tonalElevation = 2.dp,
-// shadowElevation = 2.dp,
-// shape = RoundedCornerShape(12.dp),
-// color = MaterialTheme.colorScheme.surface,
-// modifier =
-// Modifier
-// .fillMaxWidth(fillMaxWidth)
-// .padding(vertical = 10.dp)
-// .padding(bottom = 10.dp),
-// ) {
-// Column(
-// horizontalAlignment = Alignment.Start,
-// verticalArrangement = Arrangement.Top,
-// modifier = Modifier.padding(15.dp),
-// ) {
-// SectionTitle(
-// title = stringResource(id = R.string.other),
-// padding = screenPadding,
-// )
-// if (!isRunningOnTv) {
-// ConfigurationToggle(
-// stringResource(R.string.always_on_vpn_support),
-// enabled = !(
-// (
-// uiState.settings.isTunnelOnWifiEnabled ||
-// uiState.settings.isTunnelOnEthernetEnabled ||
-// uiState.settings.isTunnelOnMobileDataEnabled
-// ) &&
-// uiState.settings.isAutoTunnelEnabled
-// ),
-// checked = uiState.settings.isAlwaysOnVpnEnabled,
-// onCheckChanged = { viewModel.onToggleAlwaysOnVPN() },
-// )
-// ConfigurationToggle(
-// stringResource(R.string.enabled_app_shortcuts),
-// enabled = true,
-// checked = uiState.settings.isShortcutsEnabled,
-// onCheckChanged = { viewModel.onToggleShortcutsEnabled() },
-// )
-// }
-// ConfigurationToggle(
-// stringResource(R.string.restart_at_boot),
-// enabled = true,
-// checked = uiState.settings.isRestoreOnBootEnabled,
-// onCheckChanged = {
-// viewModel.onToggleRestartAtBoot()
-// },
-// )
-// ConfigurationToggle(
-// stringResource(R.string.enable_app_lock),
-// enabled = true,
-// checked = uiState.generalState.isPinLockEnabled,
-// onCheckChanged = {
-// if (uiState.generalState.isPinLockEnabled) {
-// appViewModel.onPinLockDisabled()
-// } else {
-// // TODO may want to show a dialog before proceeding in the future
-// PinManager.initialize(WireGuardAutoTunnel.instance)
-// navController.navigate(Route.Lock)
-// }
-// },
-// )
-// if (!isRunningOnTv) {
-// Row(
-// verticalAlignment = Alignment.CenterVertically,
-// modifier =
-// Modifier
-// .fillMaxSize()
-// .padding(top = 5.dp),
-// horizontalArrangement = Arrangement.Center,
-// ) {
-// TextButton(
-// enabled = !didExportFiles,
-// onClick = {
-// if (uiState.tunnels.isEmpty()) return@TextButton context.showToast(R.string.tunnel_required)
-// showAuthPrompt = true
-// },
-// ) {
-// Text(stringResource(R.string.export_configs))
-// }
-// }
-// }
-// }
-// }
+ if (!isRunningOnTv) {
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.FolderZip,
+ title = {
+ Text(
+ stringResource(R.string.export_configs),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ onClick = {
+ if (uiState.tunnels.isEmpty()) return@SelectionItem context.showToast(R.string.tunnel_required)
+ showAuthPrompt = true
+ },
+ ),
+ ),
+ )
+ }
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsUiState.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsUiState.kt
deleted file mode 100644
index f2a3a68..0000000
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsUiState.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.zaneschepke.wireguardautotunnel.ui.screens.settings
-
-data class SettingsUiState(
- val isRooted: Boolean = false,
- val isKernelAvailable: Boolean = false,
-)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt
index a5f25b6..d3ce4e9 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt
@@ -10,19 +10,14 @@ import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
-import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
-import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.FileUtils
import com.zaneschepke.wireguardautotunnel.util.StringValue
import com.zaneschepke.wireguardautotunnel.util.extensions.launchShareFile
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.time.Instant
@@ -39,16 +34,6 @@ constructor(
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {
- private val _uiState = MutableStateFlow(SettingsUiState())
- val uiState = _uiState.onStart {
- _uiState.update {
- it.copy(isKernelAvailable = isKernelSupported(), isRooted = isRooted())
- }
- }.stateIn(
- viewModelScope,
- SharingStarted.WhileSubscribed(Constants.SUBSCRIPTION_TIMEOUT),
- SettingsUiState(),
- )
private val settings = appDataRepository.settings.getSettingsFlow()
.stateIn(viewModelScope, SharingStarted.Eagerly, Settings())
@@ -56,10 +41,6 @@ constructor(
appDataRepository.appState.setLocationDisclosureShown(true)
}
- fun setBatteryOptimizeDisableShown() = viewModelScope.launch {
- appDataRepository.appState.setBatteryOptimizationDisableShown(true)
- }
-
fun onToggleAlwaysOnVPN() = viewModelScope.launch {
with(settings.value) {
appDataRepository.settings.save(
@@ -90,23 +71,11 @@ constructor(
}
}
- fun onToggleAmnezia() = viewModelScope.launch {
- with(settings.value) {
- if (isKernelEnabled) {
- saveKernelMode(false)
- }
- appDataRepository.settings.save(
- copy(
- isAmneziaEnabled = !isAmneziaEnabled,
- ),
- )
- }
- }
-
fun onToggleKernelMode() = viewModelScope.launch {
with(settings.value) {
if (!isKernelEnabled) {
requestRoot().onSuccess {
+ if (!isKernelSupported()) return@onSuccess SnackbarController.showMessage(StringValue.StringResource(R.string.kernel_not_supported))
appDataRepository.settings.save(
copy(
isKernelEnabled = true,
@@ -136,17 +105,6 @@ constructor(
}
}
- private suspend fun isRooted(): Boolean {
- return try {
- withContext(ioDispatcher) {
- rootShell.get().start()
- }
- true
- } catch (_: Exception) {
- false
- }
- }
-
private suspend fun requestRoot(): Result {
return withContext(ioDispatcher) {
kotlin.runCatching {
@@ -158,10 +116,6 @@ constructor(
}
}
- fun onRequestRoot() = viewModelScope.launch {
- requestRoot()
- }
-
fun exportAllConfigs(context: Context) = viewModelScope.launch {
kotlin.runCatching {
val shareFile = fileUtils.createNewShareFile("wg-export_${Instant.now().epochSecond}.zip")
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/AppearanceScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/AppearanceScreen.kt
index e25db8f..3f947d3 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/AppearanceScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/AppearanceScreen.kt
@@ -32,8 +32,8 @@ fun AppearanceScreen() {
Scaffold(
topBar = {
TopNavBar(stringResource(R.string.appearance))
- }
- ){
+ },
+ ) {
Column(
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
@@ -51,7 +51,7 @@ fun AppearanceScreen() {
onClick = { navController.navigate(Route.Language) },
trailing = {
ForwardButton { navController.navigate(Route.Language) }
- }
+ },
),
),
)
@@ -63,7 +63,7 @@ fun AppearanceScreen() {
onClick = { navController.navigate(Route.Display) },
trailing = {
ForwardButton { navController.navigate(Route.Display) }
- }
+ },
),
),
)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayScreen.kt
index d6f9c8a..f6fc0e4 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayScreen.kt
@@ -2,11 +2,8 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.displ
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -24,11 +21,10 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@Composable
fun DisplayScreen(appUiState: AppUiState, viewModel: DisplayViewModel = hiltViewModel()) {
-
Scaffold(
topBar = {
TopNavBar(stringResource(R.string.display_theme))
- }
+ },
) {
Column(
horizontalAlignment = Alignment.Start,
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayViewModel.kt
index c8714c1..cbcaaec 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayViewModel.kt
@@ -12,7 +12,7 @@ import javax.inject.Inject
class DisplayViewModel
@Inject
constructor(
- private val appStateRepository: AppStateRepository
+ private val appStateRepository: AppStateRepository,
) : ViewModel() {
fun onThemeChange(theme: Theme) = viewModelScope.launch {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/language/LanguageScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/language/LanguageScreen.kt
index 7015783..aa7df31 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/language/LanguageScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/language/LanguageScreen.kt
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@@ -69,7 +68,7 @@ fun LanguageScreen(localeStorage: LocaleStorage) {
Scaffold(
topBar = {
TopNavBar(stringResource(R.string.language))
- }
+ },
) {
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelScreen.kt
index ad360c3..a6094c6 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelScreen.kt
@@ -1,6 +1,7 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel
import android.Manifest
+import android.os.Build
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
@@ -11,6 +12,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Code
import androidx.compose.material.icons.outlined.Filter1
import androidx.compose.material.icons.outlined.NetworkPing
import androidx.compose.material.icons.outlined.Security
@@ -31,7 +33,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
@@ -45,9 +46,12 @@ import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelec
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.components.TrustedNetworkTextBox
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.components.WildcardsLabel
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.BackgroundLocationDialog
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.LearnMoreLinkLabel
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.LocationServicesDialog
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
import com.zaneschepke.wireguardautotunnel.util.extensions.isLocationServicesEnabled
+import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@@ -63,11 +67,12 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
var showLocationServicesAlertDialog by remember { mutableStateOf(false) }
var showLocationDialog by remember { mutableStateOf(false) }
- LaunchedEffect(uiState.settings.trustedNetworkSSIDs) {
- currentText = ""
+ fun checkFineLocationGranted() {
+ isBackgroundLocationGranted = fineLocationState.status.isGranted
}
fun onAutoTunnelWifiChecked() {
+ if (uiState.settings.isTunnelOnWifiEnabled) viewModel.onToggleTunnelOnWifi().also { return }
when (false) {
isBackgroundLocationGranted -> showLocationDialog = true
fineLocationState.status.isGranted -> showLocationDialog = true
@@ -79,11 +84,39 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
}
}
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) checkFineLocationGranted()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ if (context.isRunningOnTv() && Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
+ checkFineLocationGranted()
+ } else {
+ val backgroundLocationState = rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ isBackgroundLocationGranted = backgroundLocationState.status.isGranted
+ }
+ }
+
+ LaunchedEffect(uiState.settings.trustedNetworkSSIDs) {
+ currentText = ""
+ }
+
+ LocationServicesDialog(
+ showLocationServicesAlertDialog,
+ onDismiss = { showLocationServicesAlertDialog = false },
+ onAttest = {
+ viewModel.onToggleTunnelOnWifi()
+ },
+ )
+
+ BackgroundLocationDialog(
+ showLocationDialog,
+ onDismiss = { showLocationDialog = false },
+ onAttest = { showLocationDialog = false },
+ )
+
Scaffold(
contentWindowInsets = WindowInsets(0.dp),
topBar = {
TopNavBar(stringResource(R.string.auto_tunneling))
- }
+ },
) {
Column(
horizontalAlignment = Alignment.Start,
@@ -97,34 +130,60 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
) {
SurfaceSelectionGroupButton(
buildList {
- add(
- SelectionItem(
- Icons.Outlined.Wifi,
- title = {
- Text(
- stringResource(R.string.tunnel_on_wifi),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)
- )
- },
- description = {
- },
- trailing = {
- ScaledSwitch(
- enabled = !uiState.settings.isAlwaysOnVpnEnabled,
- checked = uiState.settings.isTunnelOnWifiEnabled,
- onClick = {
- if (!uiState.settings.isTunnelOnWifiEnabled || uiState.isRooted) viewModel.onToggleTunnelOnWifi()
- .also { return@ScaledSwitch }
- onAutoTunnelWifiChecked()
- },
- )
- },
- onClick = {
- if (!uiState.settings.isTunnelOnWifiEnabled || uiState.isRooted) viewModel.onToggleTunnelOnWifi()
- .also { return@SelectionItem }
- onAutoTunnelWifiChecked()
- }
- )
+ addAll(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.Wifi,
+ title = {
+ Text(
+ stringResource(R.string.tunnel_on_wifi),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ description = {
+ },
+ trailing = {
+ ScaledSwitch(
+ enabled = !uiState.settings.isAlwaysOnVpnEnabled,
+ checked = uiState.settings.isTunnelOnWifiEnabled,
+ onClick = {
+ if (uiState.settings.isWifiNameByShellEnabled) viewModel.onToggleTunnelOnWifi().also { return@ScaledSwitch }
+ onAutoTunnelWifiChecked()
+ },
+ )
+ },
+ onClick = {
+ if (uiState.settings.isWifiNameByShellEnabled) viewModel.onToggleTunnelOnWifi().also { return@SelectionItem }
+ onAutoTunnelWifiChecked()
+ },
+ ),
+ SelectionItem(
+ Icons.Outlined.Code,
+ title = {
+ Text(
+ stringResource(R.string.wifi_name_via_shell),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ description = {
+ Text(
+ stringResource(R.string.use_root_shell_for_wifi),
+ style = MaterialTheme.typography.bodySmall.copy(MaterialTheme.colorScheme.outline),
+ )
+ },
+ trailing = {
+ ScaledSwitch(
+ checked = uiState.settings.isWifiNameByShellEnabled,
+ onClick = {
+ viewModel.onRootShellWifiToggle()
+ },
+ )
+ },
+ onClick = {
+ viewModel.onRootShellWifiToggle()
+ },
+ ),
+ ),
)
if (uiState.settings.isTunnelOnWifiEnabled) {
addAll(
@@ -134,15 +193,15 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
title = {
Text(
stringResource(R.string.use_wildcards),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
)
},
description = {
- LearnMoreLinkLabel({context.openWebUrl(it)}, stringResource(id = R.string.docs_wildcards))
+ LearnMoreLinkLabel({ context.openWebUrl(it) }, stringResource(id = R.string.docs_wildcards))
},
trailing = {
ScaledSwitch(
- checked = uiState.generalState.isWildcardsEnabled,
+ checked = uiState.settings.isWildcardsEnabled,
onClick = {
viewModel.onToggleWildcards()
},
@@ -150,7 +209,7 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
},
onClick = {
viewModel.onToggleWildcards()
- }
+ },
),
SelectionItem(
title = {
@@ -183,87 +242,89 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
)
}
-
}
}
-
},
description = {
TrustedNetworkTextBox(
- uiState.settings.trustedNetworkSSIDs, onDelete = viewModel::onDeleteTrustedSSID,
+ uiState.settings.trustedNetworkSSIDs,
+ onDelete = viewModel::onDeleteTrustedSSID,
currentText = currentText,
onSave = viewModel::onSaveTrustedSSID,
onValueChange = { currentText = it },
- supporting = { if(uiState.generalState.isWildcardsEnabled) {
- WildcardsLabel()
- }}
+ supporting = {
+ if (uiState.settings.isWildcardsEnabled) {
+ WildcardsLabel()
+ }
+ },
)
},
- )
- ))
+ ),
+ ),
+ )
}
- }
+ },
)
SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- Icons.Outlined.SignalCellular4Bar,
- title = {
- Text(
- stringResource(R.string.tunnel_mobile_data),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
- )
- },
- trailing = {
- ScaledSwitch(
- enabled = !uiState.settings.isAlwaysOnVpnEnabled,
- checked = uiState.settings.isTunnelOnMobileDataEnabled,
- onClick = { viewModel.onToggleTunnelOnMobileData() },
- )
- },
- onClick = {
- viewModel.onToggleTunnelOnMobileData()
- }
- ),
- SelectionItem(
- Icons.Outlined.SettingsEthernet,
- title = {
- Text(
- stringResource(R.string.tunnel_on_ethernet),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
- )
- },
- trailing = {
- ScaledSwitch(
- enabled = !uiState.settings.isAlwaysOnVpnEnabled,
- checked = uiState.settings.isTunnelOnEthernetEnabled,
- onClick = { viewModel.onToggleTunnelOnEthernet() },
- )
- },
- onClick = {
- viewModel.onToggleTunnelOnEthernet()
- }
- ),
- SelectionItem(
- Icons.Outlined.NetworkPing,
- title = {
- Text(
- stringResource(R.string.restart_on_ping),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
- )
- },
- trailing = {
- ScaledSwitch(
- checked = uiState.settings.isPingEnabled,
- onClick = { viewModel.onToggleRestartOnPing() },
- )
- },
- onClick = {
- viewModel.onToggleRestartOnPing()
- }
+ listOf(
+ SelectionItem(
+ Icons.Outlined.SignalCellular4Bar,
+ title = {
+ Text(
+ stringResource(R.string.tunnel_mobile_data),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
)
- )
- )
+ },
+ trailing = {
+ ScaledSwitch(
+ enabled = !uiState.settings.isAlwaysOnVpnEnabled,
+ checked = uiState.settings.isTunnelOnMobileDataEnabled,
+ onClick = { viewModel.onToggleTunnelOnMobileData() },
+ )
+ },
+ onClick = {
+ viewModel.onToggleTunnelOnMobileData()
+ },
+ ),
+ SelectionItem(
+ Icons.Outlined.SettingsEthernet,
+ title = {
+ Text(
+ stringResource(R.string.tunnel_on_ethernet),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ScaledSwitch(
+ enabled = !uiState.settings.isAlwaysOnVpnEnabled,
+ checked = uiState.settings.isTunnelOnEthernetEnabled,
+ onClick = { viewModel.onToggleTunnelOnEthernet() },
+ )
+ },
+ onClick = {
+ viewModel.onToggleTunnelOnEthernet()
+ },
+ ),
+ SelectionItem(
+ Icons.Outlined.NetworkPing,
+ title = {
+ Text(
+ stringResource(R.string.restart_on_ping),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ScaledSwitch(
+ checked = uiState.settings.isPingEnabled,
+ onClick = { viewModel.onToggleRestartOnPing() },
+ )
+ },
+ onClick = {
+ viewModel.onToggleRestartOnPing()
+ },
+ ),
+ ),
+ )
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelViewModel.kt
index 1449c41..55c787c 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelViewModel.kt
@@ -2,22 +2,29 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.wireguard.android.util.RootShell
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
+import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
import com.zaneschepke.wireguardautotunnel.util.StringValue
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import javax.inject.Inject
+import javax.inject.Provider
@HiltViewModel
class AutoTunnelViewModel
@Inject
constructor(
private val appDataRepository: AppDataRepository,
+ private val rootShell: Provider,
+ @IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {
private val settings = appDataRepository.settings.getSettingsFlow()
@@ -37,29 +44,53 @@ constructor(
with(settings.value) {
appDataRepository.settings.save(
copy(
- isTunnelOnMobileDataEnabled = !this.isTunnelOnMobileDataEnabled,
+ isTunnelOnMobileDataEnabled = !isTunnelOnMobileDataEnabled,
),
)
}
}
fun onToggleWildcards() = viewModelScope.launch {
- val wildcards = appDataRepository.appState.isWildcardsEnabled()
- appDataRepository.appState.setWildcardsEnabled(
- !wildcards
- )
+ with(settings.value) {
+ appDataRepository.settings.save(
+ copy(
+ isWildcardsEnabled = !isWildcardsEnabled,
+ ),
+ )
+ }
}
fun onDeleteTrustedSSID(ssid: String) = viewModelScope.launch {
with(settings.value) {
appDataRepository.settings.save(
copy(
- trustedNetworkSSIDs = (this.trustedNetworkSSIDs - ssid).toMutableList(),
+ trustedNetworkSSIDs = (trustedNetworkSSIDs - ssid).toMutableList(),
),
)
}
}
+ fun onRootShellWifiToggle() = viewModelScope.launch {
+ requestRoot().onSuccess {
+ with(settings.value) {
+ appDataRepository.settings.save(
+ copy(isWifiNameByShellEnabled = !isWifiNameByShellEnabled),
+ )
+ }
+ }
+ }
+
+ private suspend fun requestRoot(): Result {
+ return withContext(ioDispatcher) {
+ kotlin.runCatching {
+ rootShell.get().start()
+ SnackbarController.showMessage(StringValue.StringResource(R.string.root_accepted))
+ }.onFailure {
+ SnackbarController.showMessage(StringValue.StringResource(R.string.error_root_denied))
+ }
+ }
+ }
+
fun onToggleTunnelOnEthernet() = viewModelScope.launch {
with(settings.value) {
appDataRepository.settings.save(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/TrustNetworksTextBox.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/TrustNetworksTextBox.kt
index 635be17..30a7430 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/TrustNetworksTextBox.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/TrustNetworksTextBox.kt
@@ -32,9 +32,16 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@OptIn(ExperimentalLayoutApi::class)
@Composable
-fun TrustedNetworkTextBox(trustedNetworks: List, onDelete: (ssid: String) -> Unit, currentText: String, onSave : (ssid: String) -> Unit, onValueChange: (network: String) -> Unit, supporting: @Composable () -> Unit) {
+fun TrustedNetworkTextBox(
+ trustedNetworks: List,
+ onDelete: (ssid: String) -> Unit,
+ currentText: String,
+ onSave: (ssid: String) -> Unit,
+ onValueChange: (network: String) -> Unit,
+ supporting: @Composable () -> Unit,
+) {
val context = LocalContext.current
- Column(verticalArrangement = Arrangement.spacedBy(10.dp.scaledHeight())){
+ Column(verticalArrangement = Arrangement.spacedBy(10.dp.scaledHeight())) {
FlowRow(
modifier =
Modifier.fillMaxWidth(),
@@ -93,6 +100,5 @@ fun TrustedNetworkTextBox(trustedNetworks: List, onDelete: (ssid: String
}
},
)
-
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/WildcardsLabel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/WildcardsLabel.kt
index 56039be..d52a9d9 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/WildcardsLabel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/WildcardsLabel.kt
@@ -7,7 +7,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import com.zaneschepke.wireguardautotunnel.R
-
@Composable
fun WildcardsLabel() {
Text(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/ForwardButton.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/ForwardButton.kt
index 675b5d3..f217a9e 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/ForwardButton.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/ForwardButton.kt
@@ -14,7 +14,7 @@ import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
fun ForwardButton(modifier: Modifier = Modifier.focusable(), onClick: () -> Unit) {
IconButton(
modifier = modifier,
- onClick = onClick
+ onClick = onClick,
) {
val icon = Icons.AutoMirrored.Outlined.ArrowForward
Icon(icon, icon.name, Modifier.size(iconSize))
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/LearnMoreLinkLabel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/LearnMoreLinkLabel.kt
index 453d368..4724e8d 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/LearnMoreLinkLabel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/LearnMoreLinkLabel.kt
@@ -12,7 +12,7 @@ import androidx.compose.ui.text.withStyle
import com.zaneschepke.wireguardautotunnel.R
@Composable
-fun LearnMoreLinkLabel(onClick: (url: String) -> Unit, url : String) {
+fun LearnMoreLinkLabel(onClick: (url: String) -> Unit, url: String) {
// TODO update link when docs are fully updated
val gettingStarted =
buildAnnotatedString {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/disclosure/LocationDisclosureScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/disclosure/LocationDisclosureScreen.kt
index 63fd324..e21ac68 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/disclosure/LocationDisclosureScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/disclosure/LocationDisclosureScreen.kt
@@ -39,7 +39,7 @@ fun LocationDisclosureScreen(appViewModel: AppViewModel, appUiState: AppUiState)
val navController = LocalNavController.current
LaunchedEffect(Unit, appUiState) {
- if(appUiState.generalState.isLocationDisclosureShown) navController.goFromRoot(Route.AutoTunnel)
+ if (appUiState.generalState.isLocationDisclosureShown) navController.goFromRoot(Route.AutoTunnel)
}
Column(
@@ -56,40 +56,49 @@ fun LocationDisclosureScreen(appViewModel: AppViewModel, appUiState: AppUiState)
.padding(30.dp.scaledHeight())
.size(128.dp.scaledHeight()),
)
- Text(
- stringResource(R.string.prominent_background_location_title),
- style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
- )
- Text(
- stringResource(R.string.prominent_background_location_message),
- style = MaterialTheme.typography.bodyLarge
- )
- SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- Icons.Outlined.LocationOn,
- title = { Text(stringResource(R.string.launch_app_settings), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- onClick = { context.launchAppSettings().also {
+ Text(
+ stringResource(R.string.prominent_background_location_title),
+ style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
+ )
+ Text(
+ stringResource(R.string.prominent_background_location_message),
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.LocationOn,
+ title = {
+ Text(
+ stringResource(R.string.launch_app_settings),
+ style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ onClick = {
+ context.launchAppSettings().also {
appViewModel.setLocationDisclosureShown()
- } },
- trailing = {
- ForwardButton { context.launchAppSettings().also {
+ }
+ },
+ trailing = {
+ ForwardButton {
+ context.launchAppSettings().also {
appViewModel.setLocationDisclosureShown()
- } }
+ }
}
- ),
+ },
),
- )
- SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- title = { Text(stringResource(R.string.skip), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- onClick = { appViewModel.setLocationDisclosureShown() },
- trailing = {
- ForwardButton { appViewModel.setLocationDisclosureShown() }
- }
- ),
+ ),
+ )
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ title = { Text(stringResource(R.string.skip), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
+ onClick = { appViewModel.setLocationDisclosureShown() },
+ trailing = {
+ ForwardButton { appViewModel.setLocationDisclosureShown() }
+ },
),
- )
- }
+ ),
+ )
}
+}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt
index 561d637..30c8bd5 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt
@@ -38,94 +38,118 @@ fun SupportScreen() {
val context = LocalContext.current
val navController = LocalNavController.current
- Column(
- horizontalAlignment = Alignment.Start,
- verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
- modifier =
- Modifier
- .fillMaxSize()
- .padding(top = topPadding)
- .padding(horizontal = 24.dp.scaledWidth()),
- ) {
- GroupLabel(stringResource(R.string.thank_you))
- SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- Icons.Filled.Book,
- title = { Text(stringResource(R.string.docs_description), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
- trailing = {
- ForwardButton { context.openWebUrl(context.getString(R.string.docs_url)) }
- },
- onClick = {
- context.openWebUrl(context.getString(R.string.docs_url))
- }
- ),
- SelectionItem(
- Icons.Filled.LineStyle,
- title = { Text(stringResource(R.string.read_logs),
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
- trailing = {
- ForwardButton {
- navController.navigate(Route.Logs)
- }
- },
- onClick = {
+ Column(
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
+ modifier =
+ Modifier
+ .fillMaxSize()
+ .padding(top = topPadding)
+ .padding(horizontal = 24.dp.scaledWidth()),
+ ) {
+ GroupLabel(stringResource(R.string.thank_you))
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Filled.Book,
+ title = {
+ Text(
+ stringResource(R.string.docs_description),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ForwardButton { context.openWebUrl(context.getString(R.string.docs_url)) }
+ },
+ onClick = {
+ context.openWebUrl(context.getString(R.string.docs_url))
+ },
+ ),
+ SelectionItem(
+ Icons.Filled.LineStyle,
+ title = {
+ Text(
+ stringResource(R.string.read_logs),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ForwardButton {
navController.navigate(Route.Logs)
}
- ),
- SelectionItem(
- Icons.Filled.Policy,
- title = { Text(stringResource(R.string.privacy_policy), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
- trailing = {
- ForwardButton { context.openWebUrl(context.getString(R.string.privacy_policy_url)) }
- },
- onClick = {
- context.openWebUrl(context.getString(R.string.privacy_policy_url))
- }
- ),
+ },
+ onClick = {
+ navController.navigate(Route.Logs)
+ },
+ ),
+ SelectionItem(
+ Icons.Filled.Policy,
+ title = {
+ Text(
+ stringResource(R.string.privacy_policy),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ForwardButton { context.openWebUrl(context.getString(R.string.privacy_policy_url)) }
+ },
+ onClick = {
+ context.openWebUrl(context.getString(R.string.privacy_policy_url))
+ },
+ ),
- )
- )
- SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- ImageVector.vectorResource(R.drawable.telegram),
- title = { Text(stringResource(R.string.chat_description), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
- trailing = {
- ForwardButton {
- context.openWebUrl(context.getString(R.string.telegram_url))
- }
- },
- onClick = {
+ ),
+ )
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ ImageVector.vectorResource(R.drawable.telegram),
+ title = {
+ Text(
+ stringResource(R.string.chat_description),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ForwardButton {
context.openWebUrl(context.getString(R.string.telegram_url))
}
- ),
- SelectionItem(
- ImageVector.vectorResource(R.drawable.github),
- title = { Text(stringResource(R.string.open_issue), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
- trailing = {
- ForwardButton {
- context.openWebUrl(context.getString(R.string.github_url))
- }
- },
- onClick = {
+ },
+ onClick = {
+ context.openWebUrl(context.getString(R.string.telegram_url))
+ },
+ ),
+ SelectionItem(
+ ImageVector.vectorResource(R.drawable.github),
+ title = { Text(stringResource(R.string.open_issue), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ trailing = {
+ ForwardButton {
context.openWebUrl(context.getString(R.string.github_url))
}
- ),
- SelectionItem(
- Icons.Filled.Mail,
- title = { Text(stringResource(R.string.email_description), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
- trailing = {
- ForwardButton {
- context.launchSupportEmail()
- }
- },
- onClick = {
+ },
+ onClick = {
+ context.openWebUrl(context.getString(R.string.github_url))
+ },
+ ),
+ SelectionItem(
+ Icons.Filled.Mail,
+ title = {
+ Text(
+ stringResource(R.string.email_description),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ForwardButton {
context.launchSupportEmail()
}
- ),
- )
- )
- VersionLabel()
- }
+ },
+ onClick = {
+ context.launchSupportEmail()
+ },
+ ),
+ ),
+ )
+ VersionLabel()
}
+}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/logs/LogsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/logs/LogsScreen.kt
index 96c7f40..918826a 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/logs/LogsScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/logs/LogsScreen.kt
@@ -4,11 +4,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Color.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Color.kt
index 1caf5e2..5adc93a 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Color.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Color.kt
@@ -12,8 +12,6 @@ val BalticSea = Color(0xFF1C1B1F)
val Brick = Color(0xFFCE4257)
val Straw = Color(0xFFD4C483)
-
-
sealed class ThemeColors(
val background: Color,
val surface: Color,
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Theme.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Theme.kt
index bb4eeef..e1e30cc 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Theme.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Theme.kt
@@ -40,18 +40,15 @@ enum class Theme {
AUTOMATIC,
LIGHT,
DARK,
- DYNAMIC
+ DYNAMIC,
}
@Composable
-fun WireguardAutoTunnelTheme(
- theme: Theme = Theme.AUTOMATIC,
- content: @Composable () -> Unit,
-) {
+fun WireguardAutoTunnelTheme(theme: Theme = Theme.AUTOMATIC, content: @Composable () -> Unit) {
val context = LocalContext.current
var isDark = isSystemInDarkTheme()
- val autoTheme = if(isDark) DarkColorScheme else LightColorScheme
- val colorScheme = when(theme) {
+ val autoTheme = if (isDark) DarkColorScheme else LightColorScheme
+ val colorScheme = when (theme) {
Theme.AUTOMATIC -> autoTheme
Theme.DARK -> {
isDark = true
@@ -68,7 +65,9 @@ fun WireguardAutoTunnelTheme(
} else {
dynamicLightColorScheme(context)
}
- } else autoTheme
+ } else {
+ autoTheme
+ }
}
}
val view = LocalView.current
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/SingletonHolder.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/SingletonHolder.kt
new file mode 100644
index 0000000..6d695f6
--- /dev/null
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/SingletonHolder.kt
@@ -0,0 +1,26 @@
+package com.zaneschepke.wireguardautotunnel.util
+
+open class SingletonHolder(creator: (A) -> T) {
+ private var creator: ((A) -> T)? = creator
+
+ @Volatile private var instance: T? = null
+
+ fun getInstance(arg: A): T {
+ val i = instance
+ if (i != null) {
+ return i
+ }
+
+ return synchronized(this) {
+ val i2 = instance
+ if (i2 != null) {
+ i2
+ } else {
+ val created = creator!!(arg)
+ instance = created
+ creator = null
+ created
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt
index 18a3d2f..42377e9 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt
@@ -2,10 +2,12 @@ package com.zaneschepke.wireguardautotunnel.util.extensions
import android.content.ComponentName
import android.content.Context
+import android.content.Context.POWER_SERVICE
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.net.Uri
+import android.os.PowerManager
import android.provider.Settings
import android.service.quicksettings.TileService
import android.widget.Toast
@@ -13,7 +15,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.core.location.LocationManagerCompat
import com.zaneschepke.wireguardautotunnel.R
-import com.zaneschepke.wireguardautotunnel.receiver.BackgroundActionReceiver
import com.zaneschepke.wireguardautotunnel.service.tile.AutoTunnelControlTile
import com.zaneschepke.wireguardautotunnel.service.tile.TunnelControlTile
import com.zaneschepke.wireguardautotunnel.util.Constants
@@ -34,6 +35,11 @@ fun Context.openWebUrl(url: String): Result {
}
}
+fun Context.isBatteryOptimizationsDisabled(): Boolean {
+ val pm = getSystemService(POWER_SERVICE) as PowerManager
+ return pm.isIgnoringBatteryOptimizations(packageName)
+}
+
val Context.actionBarSize
get() = theme.obtainStyledAttributes(intArrayOf(android.R.attr.actionBarSize))
.let { attrs -> attrs.getDimension(0, 0F).toInt().also { attrs.recycle() } }
@@ -66,7 +72,7 @@ fun Context.resizeWidth(dp: Dp): Dp {
}
fun Context.launchNotificationSettings() {
- if(isRunningOnTv()) return launchAppSettings()
+ if (isRunningOnTv()) return launchAppSettings()
val settingsIntent: Intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
@@ -159,23 +165,23 @@ fun Context.launchAppSettings() {
}
}
-fun Context.startTunnelBackground(tunnelId: Int) {
- sendBroadcast(
- Intent(this, BackgroundActionReceiver::class.java).apply {
- action = BackgroundActionReceiver.ACTION_CONNECT
- putExtra(BackgroundActionReceiver.TUNNEL_ID_EXTRA_KEY, tunnelId)
- },
- )
-}
-
-fun Context.stopTunnelBackground(tunnelId: Int) {
- sendBroadcast(
- Intent(this, BackgroundActionReceiver::class.java).apply {
- action = BackgroundActionReceiver.ACTION_DISCONNECT
- putExtra(BackgroundActionReceiver.TUNNEL_ID_EXTRA_KEY, tunnelId)
- },
- )
-}
+// fun Context.startTunnelBackground(tunnelId: Int) {
+// sendBroadcast(
+// Intent(this, BackgroundActionReceiver::class.java).apply {
+// action = BackgroundActionReceiver.ACTION_CONNECT
+// putExtra(BackgroundActionReceiver.TUNNEL_ID_EXTRA_KEY, tunnelId)
+// },
+// )
+// }
+//
+// fun Context.stopTunnelBackground(tunnelId: Int) {
+// sendBroadcast(
+// Intent(this, BackgroundActionReceiver::class.java).apply {
+// action = BackgroundActionReceiver.ACTION_DISCONNECT
+// putExtra(BackgroundActionReceiver.TUNNEL_ID_EXTRA_KEY, tunnelId)
+// },
+// )
+// }
fun Context.requestTunnelTileServiceStateUpdate() {
TileService.requestListeningState(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/StringExtensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/StringExtensions.kt
index 7a617c5..48ae90a 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/StringExtensions.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/StringExtensions.kt
@@ -28,9 +28,9 @@ fun String.extractNameAndNumber(): Pair? {
}
fun List.isMatchingToWildcardList(value: String): Boolean {
- val excludeValues = this.filter { it.startsWith("!") }.map { it.removePrefix("!").toRegexWithWildcards() }
+ val excludeValues = this.filter { it.startsWith("!") }.map { it.removePrefix("!").transformWildcardsToRegex() }
Timber.d("Excluded values: $excludeValues")
- val includedValues = this.filter { !it.startsWith("!") }.map { it.toRegexWithWildcards() }
+ val includedValues = this.filter { !it.startsWith("!") }.map { it.transformWildcardsToRegex() }
Timber.d("Included values: $includedValues")
val matches = includedValues.filter { it.matches(value) }
val excludedMatches = excludeValues.filter { it.matches(value) }
@@ -39,6 +39,32 @@ fun List.isMatchingToWildcardList(value: String): Boolean {
return matches.isNotEmpty() && excludedMatches.isEmpty()
}
-fun String.toRegexWithWildcards(): Regex {
- return this.replace("*", ".*").replace("?", ".").toRegex()
+fun String.transformWildcardsToRegex(): Regex {
+ return this.replaceUnescapedChar("*", ".*").replaceUnescapedChar("?", ".").toRegex()
+}
+
+fun String.replaceUnescapedChar(charToReplace: String, replacement: String): String {
+ val escapedChar = Regex.escape(charToReplace)
+ val regex = "(?
+ if (matchResult.range.first == 0 ||
+ this[matchResult.range.first - 1] != '\\' ||
+ (matchResult.range.first > 1 && this[matchResult.range.first - 2] == '\\')
+ ) {
+ replacement.toString()
+ } else {
+ matchResult.value
+ }
+ }
+}
+
+fun String.isCharacterEscaped(index: Int): Boolean {
+ if (index <= 0) return false
+ var backslashCount = 0
+ var currentIndex = index - 1
+ while (currentIndex >= 0 && this[currentIndex] == '\\') {
+ backslashCount++
+ currentIndex--
+ }
+ return backslashCount % 2 != 0
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt
index c39f15c..34405a9 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt
@@ -4,9 +4,11 @@ import androidx.compose.ui.graphics.Color
import com.wireguard.android.util.RootShell
import com.wireguard.config.Peer
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
+import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
+import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnState
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStatistics
-import com.zaneschepke.wireguardautotunnel.ui.theme.Straw
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
+import com.zaneschepke.wireguardautotunnel.ui.theme.Straw
import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
import org.amnezia.awg.config.Config
@@ -21,6 +23,10 @@ fun TunnelStatistics.PeerStats.latestHandshakeSeconds(): Long? {
return NumberUtils.getSecondsBetweenTimestampAndNow(this.latestHandshakeEpochMillis)
}
+fun VpnState.isDown(): Boolean {
+ return this.status == TunnelState.DOWN
+}
+
fun TunnelStatistics.PeerStats.handshakeStatus(): HandshakeStatus {
// TODO add never connected status after duration
return this.latestHandshakeSeconds().let {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 48ebb9d..1777125 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -220,4 +220,9 @@
Use name wildcards
Learn more
Wildcards active
+ Wifi name via shell
+ Use root shell to get wifi name
+ Kernel not supported
+ Start auto-tunnel
+ Stop auto-tunnel
diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml/shortcuts.xml
index c4f61c1..bfd59e9 100644
--- a/app/src/main/res/xml/shortcuts.xml
+++ b/app/src/main/res/xml/shortcuts.xml
@@ -37,8 +37,8 @@
android:enabled="true"
android:icon="@drawable/auto_play"
android:shortcutId="autoOn1"
- android:shortcutLongLabel="@string/auto_on"
- android:shortcutShortLabel="@string/auto_tun_on">
+ android:shortcutLongLabel="@string/start_auto"
+ android:shortcutShortLabel="@string/start_auto">
+ android:shortcutLongLabel="@string/stop_auto"
+ android:shortcutShortLabel="@string/stop_auto">