diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 53a5ba9..ea6dde3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -157,7 +157,7 @@ android:value="true" /> - Timber.d("Tunnels or settings changed!") - autoTunnelStateFlow.value.copy( - settings = settings, - tunnels = tunnels, - ) - }.collect { - Timber.d("got new settings: ${it.settings}") - manageJobsBySettings(it.settings) - autoTunnelStateFlow.emit(it) + Pair(settings, tunnels) + }.collect { pair -> + manageJobsBySettings(pair.first) + autoTunnelStateFlow.update { + it.copy( + settings = pair.first, + tunnels = pair.second, + ) + } } } } @@ -287,12 +288,12 @@ class AutoTunnelService : LifecycleService() { private suspend fun watchForVpnStateChanges() { Timber.i("Starting vpn state watcher") withContext(ioDispatcher) { - tunnelService.get().vpnState.distinctUntilChanged { old, new -> - old.tunnelConfig?.id == new.tunnelConfig?.id - }.collect { state -> + tunnelService.get().vpnState.collect { state -> autoTunnelStateFlow.update { it.copy(vpnState = state) } + // TODO think about this + // What happens if we change the pinger setting while vpn is active? state.tunnelConfig?.let { val settings = appDataRepository.settings.getSettings() if (it.isPingEnabled && !settings.isPingEnabled) { @@ -455,100 +456,23 @@ class AutoTunnelService : LifecycleService() { } } - private suspend fun getMobileDataTunnel(): TunnelConfig? { - return appDataRepository.tunnels.findByMobileDataTunnel().firstOrNull() - } - private suspend fun handleNetworkEventChanges() { withContext(ioDispatcher) { - Timber.i("Starting network event watcher") - autoTunnelStateFlow.collect { watcherState -> - val autoTunnel = "Auto-tunnel watcher" - // delay for rapid network state changes and then collect latest - delay(Constants.WATCHER_COLLECTION_DELAY) - val activeTunnel = watcherState.vpnState.tunnelConfig - val defaultTunnel = appDataRepository.getPrimaryOrFirstTunnel() - val isTunnelDown = tunnelService.get().getState() == TunnelState.DOWN - when { - watcherState.isEthernetConditionMet() -> { - Timber.i("$autoTunnel - tunnel on on ethernet condition met") - if (isTunnelDown) { - defaultTunnel?.let { - tunnelService.get().startTunnel(it) - } - } + Timber.i("Starting auto-tunnel network event watcher") + // allow manual overrides + autoTunnelStateFlow.distinctUntilChanged { old, new -> + old.copy(vpnState = new.vpnState) == new + }.collect { watcherState -> + when (val event = watcherState.asAutoTunnelEvent()) { + is AutoTunnelEvent.Start -> { + Timber.d("Start tunnel ${event.tunnelConfig?.name}") + tunnelService.get().startTunnel(event.tunnelConfig ?: appDataRepository.getPrimaryOrFirstTunnel()) } - - 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 == null || watcherState.isCurrentSSIDActiveTunnelNetwork() == false || - isTunnelDown - ) { - 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 (isTunnelDown || activeTunnel?.id != it.id) { - 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() - } - } - - watcherState.isTrustedWifiConditionMet() -> { - Timber.i( - "$autoTunnel - tunnel off on trusted 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 (!isTunnelDown) activeTunnel?.let { tunnelService.get().stopTunnel(it) } - } -// TODO disable for this now -// watcherState.isTunnelOffOnNoConnectivityMet() -> { -// Timber.i( -// "$autoTunnel - tunnel off on no connectivity met, turning vpn off", -// ) -// if (!isTunnelDown) activeTunnel?.let { tunnelService.get().stopTunnel(it) } -// } - - else -> { - Timber.i("$autoTunnel - no condition met") + is AutoTunnelEvent.Stop -> { + Timber.d("Stop tunnel") + tunnelService.get().stopTunnel() } + AutoTunnelEvent.DoNothing -> Timber.i("Auto-tunneling: no condition met") } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/autotunnel/AutoTunnelState.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/autotunnel/AutoTunnelState.kt new file mode 100644 index 0000000..b1c9b24 --- /dev/null +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/autotunnel/AutoTunnelState.kt @@ -0,0 +1,118 @@ +package com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel + +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, + val currentNetworkSSID: String = "", + val settings: Settings = Settings(), + val tunnels: TunnelConfigs = emptyList(), +) { + + private fun isMobileDataActive(): Boolean { + return !isEthernetConnected && !isWifiConnected && isMobileDataConnected + } + + private fun isMobileTunnelDataChangeNeeded(): Boolean { + val preferredTunnel = preferredMobileDataTunnel() + return preferredTunnel != null && + vpnState.status.isUp() && preferredTunnel.id != vpnState.tunnelConfig?.id + } + + private fun preferredMobileDataTunnel(): TunnelConfig? { + return tunnels.firstOrNull { it.isMobileDataTunnel } ?: tunnels.firstOrNull { it.isPrimaryTunnel } + } + + private fun preferredWifiTunnel(): TunnelConfig? { + return getTunnelWithMatchingTunnelNetwork() ?: tunnels.firstOrNull { it.isPrimaryTunnel } + } + + private fun isWifiActive(): Boolean { + return !isEthernetConnected && isWifiConnected + } + + private fun startOnEthernet(): Boolean { + return isEthernetConnected && settings.isTunnelOnEthernetEnabled && vpnState.status.isDown() + } + + private fun stopOnMobileData(): Boolean { + return isMobileDataActive() && !settings.isTunnelOnMobileDataEnabled && vpnState.status.isUp() + } + + private fun startOnMobileData(): Boolean { + return isMobileDataActive() && settings.isTunnelOnMobileDataEnabled && vpnState.status.isDown() + } + + private fun changeOnMobileData(): Boolean { + return isMobileDataActive() && settings.isTunnelOnMobileDataEnabled && isMobileTunnelDataChangeNeeded() + } + + private fun stopOnWifi(): Boolean { + return isWifiActive() && !settings.isTunnelOnWifiEnabled && vpnState.status.isUp() + } + + private fun stopOnTrustedWifi(): Boolean { + return isWifiActive() && settings.isTunnelOnWifiEnabled && vpnState.status.isUp() && isCurrentSSIDTrusted() + } + + private fun startOnUntrustedWifi(): Boolean { + return isWifiActive() && settings.isTunnelOnWifiEnabled && vpnState.status.isDown() && !isCurrentSSIDTrusted() + } + + private fun changeOnUntrustedWifi(): Boolean { + return isWifiActive() && settings.isTunnelOnWifiEnabled && vpnState.status.isUp() && !isCurrentSSIDTrusted() && !isWifiTunnelPreferred() + } + + private fun isWifiTunnelPreferred(): Boolean { + val preferred = preferredWifiTunnel() + val vpnTunnel = vpnState.tunnelConfig + return if (preferred != null && vpnTunnel != null) { + preferred.id == vpnTunnel.id + } else { + true + } + } + + // TODO add shutdown on no connectivity + fun asAutoTunnelEvent(): AutoTunnelEvent { + return when { + // ethernet scenarios + startOnEthernet() -> AutoTunnelEvent.Start() + // mobile data scenarios + stopOnMobileData() -> AutoTunnelEvent.Stop(vpnState.tunnelConfig) + startOnMobileData() -> AutoTunnelEvent.Start(tunnels.firstOrNull { it.isMobileDataTunnel }) + changeOnMobileData() -> AutoTunnelEvent.Start(preferredMobileDataTunnel()) + // wifi scenarios + stopOnWifi() -> AutoTunnelEvent.Stop(vpnState.tunnelConfig) + stopOnTrustedWifi() -> AutoTunnelEvent.Stop(vpnState.tunnelConfig) + startOnUntrustedWifi() -> AutoTunnelEvent.Start(preferredWifiTunnel()) + changeOnUntrustedWifi() -> AutoTunnelEvent.Start(preferredWifiTunnel()) + else -> AutoTunnelEvent.DoNothing + } + } + + private fun isCurrentSSIDTrusted(): Boolean { + return if (settings.isWildcardsEnabled) { + settings.trustedNetworkSSIDs.isMatchingToWildcardList(currentNetworkSSID) + } else { + settings.trustedNetworkSSIDs.contains(currentNetworkSSID) + } + } + + private 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/shortcut/ShortcutsActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/shortcut/ShortcutsActivity.kt index 7377a3a..a520bb2 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 @@ -4,9 +4,8 @@ import android.os.Bundle import androidx.activity.ComponentActivity 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.foreground.autotunnel.AutoTunnelService import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope @@ -47,7 +46,7 @@ class ShortcutsActivity : ComponentActivity() { tunnelConfig?.let { when (intent.action) { Action.START.name -> tunnelService.get().startTunnel(it, true) - Action.STOP.name -> tunnelService.get().stopTunnel(it) + Action.STOP.name -> tunnelService.get().stopTunnel() else -> Unit } } @@ -64,6 +63,11 @@ class ShortcutsActivity : ComponentActivity() { finish() } + enum class Action { + START, + STOP, + } + companion object { const val LEGACY_TUNNEL_SERVICE_NAME = "WireGuardTunnelService" const val LEGACY_AUTO_TUNNEL_SERVICE_NAME = "WireGuardConnectivityWatcherService" 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 32512a1..e672f9e 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 @@ -72,7 +72,7 @@ class TunnelControlTile : TileService(), LifecycleOwner { val lastActive = appDataRepository.getStartTunnelConfig() lastActive?.let { tunnel -> if (tunnel.isActive) { - tunnelService.get().stopTunnel(tunnel) + tunnelService.get().stopTunnel() } else { tunnelService.get().startTunnel(tunnel, true) } 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 41ebf7e..1cb55ab 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,11 +5,12 @@ 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, background: Boolean = false): Result - suspend fun stopTunnel(tunnelConfig: TunnelConfig): Result + suspend fun startTunnel(tunnelConfig: TunnelConfig?, background: Boolean = false) - suspend fun bounceTunnel(tunnelConfig: TunnelConfig): Result + suspend fun stopTunnel() + + suspend fun bounceTunnel() val vpnState: StateFlow diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/TunnelState.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/TunnelState.kt index af2f738..59f968c 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/TunnelState.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/TunnelState.kt @@ -24,6 +24,14 @@ enum class TunnelState { } } + fun isDown(): Boolean { + return this == DOWN + } + + fun isUp(): Boolean { + return this == UP + } + companion object { fun from(state: Tunnel.State): TunnelState { return when (state) { 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 8070450..dd7ebf6 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 @@ -21,11 +21,13 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.amnezia.awg.backend.Tunnel import timber.log.Timber -import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import javax.inject.Provider @@ -53,7 +55,7 @@ constructor( private var statsJob: Job? = null - private val runningHandle = AtomicBoolean(false) + private val mutex = Mutex() private suspend fun backend(): Any { val settings = appDataRepository.settings.getSettings() @@ -92,77 +94,58 @@ constructor( } } - override suspend fun startTunnel(tunnelConfig: TunnelConfig, background: Boolean): Result { - return withContext(ioDispatcher) { - if (runningHandle.get() && tunnelConfig == vpnState.value.tunnelConfig) { - Timber.w("Tunnel already running") - return@withContext Result.success(vpnState.value.status) - } - runningHandle.set(true) - onBeforeStart(tunnelConfig) - val settings = appDataRepository.settings.getSettings() - if (background || settings.isKernelEnabled) startBackgroundService() - setState(tunnelConfig, TunnelState.UP).onSuccess { - updateTunnelState(it) - }.onFailure { - Timber.e(it) - onStartFailed() + private fun isTunnelAlreadyRunning(tunnelConfig: TunnelConfig): Boolean { + val isRunning = tunnelConfig == _vpnState.value.tunnelConfig && _vpnState.value.status.isUp() + if (isRunning) Timber.w("Tunnel already running") + return isRunning + } + + override suspend fun startTunnel(tunnelConfig: TunnelConfig?, background: Boolean) { + if (tunnelConfig == null) return + withContext(ioDispatcher) { + mutex.withLock { + if (isTunnelAlreadyRunning(tunnelConfig)) return@withContext + onBeforeStart(background) + setState(tunnelConfig, TunnelState.UP).onSuccess { + startStatsJob() + if (it.isUp()) appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true)) + updateTunnelState(it, tunnelConfig) + }.onFailure { + Timber.e(it) + } } } } - override suspend fun stopTunnel(tunnelConfig: TunnelConfig): Result { - return withContext(ioDispatcher) { - onBeforeStop(tunnelConfig) - setState(tunnelConfig, TunnelState.DOWN).onSuccess { - updateTunnelState(it) - }.onFailure { - Timber.e(it) - onStopFailed() - }.also { - stopBackgroundService() - runningHandle.set(false) + override suspend fun stopTunnel() { + withContext(ioDispatcher) { + mutex.withLock { + if (_vpnState.value.status.isDown()) return@withContext + with(_vpnState.value) { + if (tunnelConfig == null) return@withContext + setState(tunnelConfig, TunnelState.DOWN).onSuccess { + updateTunnelState(it, null) + onStop(tunnelConfig) + stopBackgroundService() + }.onFailure { + Timber.e(it) + } + } } } } - // use this when we just want to bounce tunnel and not change tunnelConfig active state - override suspend fun bounceTunnel(tunnelConfig: TunnelConfig): Result { - toggleTunnel(tunnelConfig) - delay(VPN_RESTART_DELAY) - return toggleTunnel(tunnelConfig) + override suspend fun bounceTunnel() { + if (_vpnState.value.tunnelConfig == null) return + val config = _vpnState.value.tunnelConfig + stopTunnel() + startTunnel(config) } - private suspend fun toggleTunnel(tunnelConfig: TunnelConfig): Result { - return withContext(ioDispatcher) { - setState(tunnelConfig, TunnelState.TOGGLE).onSuccess { - updateTunnelState(it) - resetBackendStatistics() - }.onFailure { - Timber.e(it) - } - } - } - - private suspend fun onStopFailed() { - _vpnState.value.tunnelConfig?.let { - appDataRepository.tunnels.save(it.copy(isActive = true)) - } - } - - private suspend fun onStartFailed() { - _vpnState.value.tunnelConfig?.let { - appDataRepository.tunnels.save(it.copy(isActive = false)) - } - cancelStatsJob() - resetBackendStatistics() - runningHandle.set(false) - } - - private suspend fun shutDownActiveTunnel(config: TunnelConfig) { + private suspend fun shutDownActiveTunnel() { with(_vpnState.value) { - if (status == TunnelState.UP && tunnelConfig != config) { - tunnelConfig?.let { stopTunnel(it) } + if (status.isUp()) { + stopTunnel() } } } @@ -177,51 +160,35 @@ constructor( serviceManager.requestTunnelTileUpdate() } - private suspend fun onBeforeStart(tunnelConfig: TunnelConfig) { - shutDownActiveTunnel(tunnelConfig) - appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true)) - emitVpnStateConfig(tunnelConfig) + private suspend fun onBeforeStart(background: Boolean) { + shutDownActiveTunnel() resetBackendStatistics() - startStatsJob() + val settings = appDataRepository.settings.getSettings() + if (background || settings.isKernelEnabled) startBackgroundService() } - private suspend fun onBeforeStop(tunnelConfig: TunnelConfig) { + private suspend fun onStop(tunnelConfig: TunnelConfig) { appDataRepository.tunnels.save(tunnelConfig.copy(isActive = false)) cancelStatsJob() resetBackendStatistics() } - private fun updateTunnelState(state: TunnelState) { - _vpnState.tryEmit( - _vpnState.value.copy( - status = state, - ), - ) - serviceManager.requestTunnelTileUpdate() + private fun updateTunnelState(state: TunnelState, tunnelConfig: TunnelConfig?) { + _vpnState.update { + it.copy(status = state, tunnelConfig = tunnelConfig) + } } private fun emitBackendStatistics(statistics: TunnelStatistics) { - _vpnState.tryEmit( - _vpnState.value.copy( - statistics = statistics, - ), - ) - } - - private fun emitVpnStateConfig(tunnelConfig: TunnelConfig) { - _vpnState.tryEmit( - _vpnState.value.copy( - tunnelConfig = tunnelConfig, - ), - ) + _vpnState.update { + it.copy(statistics = statistics) + } } private fun resetBackendStatistics() { - _vpnState.tryEmit( - _vpnState.value.copy( - statistics = null, - ), - ) + _vpnState.update { + it.copy(statistics = null) + } } override suspend fun getState(): TunnelState { @@ -265,16 +232,21 @@ constructor( } override fun onStateChange(newState: Tunnel.State) { - updateTunnelState(TunnelState.from(newState)) + _vpnState.update { + it.copy(status = TunnelState.from(newState)) + } + serviceManager.requestTunnelTileUpdate() } override fun onStateChange(state: State) { - updateTunnelState(TunnelState.from(state)) + _vpnState.update { + it.copy(status = TunnelState.from(state)) + } + serviceManager.requestTunnelTileUpdate() } companion object { const val STATS_START_DELAY = 1_000L const val VPN_STATISTIC_CHECK_INTERVAL = 1_000L - const val VPN_RESTART_DELAY = 1_000L } } 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 f8477b2..818227c 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt @@ -150,7 +150,7 @@ class MainActivity : AppCompatActivity() { navController, enterTransition = { fadeIn(tween(Constants.TRANSITION_ANIMATION_TIME)) }, exitTransition = { fadeOut(tween(Constants.TRANSITION_ANIMATION_TIME)) }, - startDestination = (if (appUiState.generalState.isPinLockEnabled == true) Route.Lock else Route.Main), + startDestination = (if (appUiState.generalState.isPinLockEnabled) Route.Lock else Route.Main), ) { composable { MainScreen( 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 a9728ce..bf6b126 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 @@ -155,7 +155,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState) fun onTunnelToggle(checked: Boolean, tunnel: TunnelConfig) { val intent = if (uiState.settings.isKernelEnabled) null else VpnService.prepare(context) if (intent != null) return vpnActivity.launch(intent) - if (!checked) viewModel.onTunnelStop(tunnel).also { return } + if (!checked) viewModel.onTunnelStop().also { return } viewModel.onTunnelStart(tunnel, uiState.settings.isKernelEnabled) } @@ -249,8 +249,8 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState) key = { tunnel -> tunnel.id }, ) { tunnel -> val isActive = uiState.tunnels.any { - it.id == tunnel.id && - it.isActive + it.id == uiState.vpnState.tunnelConfig?.id && + uiState.vpnState.status.isUp() } val expanded = uiState.generalState.isTunnelStatsExpanded TunnelRowItem( 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 c6394fe..9c821c2 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 @@ -72,9 +72,9 @@ constructor( tunnelService.get().startTunnel(tunnelConfig, background) } - fun onTunnelStop(tunnel: TunnelConfig) = viewModelScope.launch { + fun onTunnelStop() = viewModelScope.launch { Timber.i("Stopping active tunnel") - tunnelService.get().stopTunnel(tunnel) + tunnelService.get().stopTunnel() } private fun generateQrCodeDefaultName(config: String): String {