fix: make ping aware of network availability
add basic tunnel error messages
This commit is contained in:
parent
53c09267df
commit
b1dc6c5d59
|
@ -164,7 +164,7 @@
|
|||
tools:node="merge" />
|
||||
|
||||
<service
|
||||
android:name=".service.foreground.TunnelBackgroundService"
|
||||
android:name=".service.foreground.TunnelForegroundService"
|
||||
android:exported="false"
|
||||
android:persistent="true"
|
||||
android:foregroundServiceType="systemExempted"
|
||||
|
|
|
@ -3,6 +3,9 @@ package com.zaneschepke.wireguardautotunnel
|
|||
import android.app.Application
|
||||
import android.os.StrictMode
|
||||
import android.os.StrictMode.ThreadPolicy
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import com.zaneschepke.logcatter.LogReader
|
||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
||||
import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository
|
||||
|
@ -52,6 +55,7 @@ class WireGuardAutoTunnel : Application() {
|
|||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
StrictMode.setThreadPolicy(
|
||||
|
@ -88,7 +92,25 @@ class WireGuardAutoTunnel : Application() {
|
|||
super.onTerminate()
|
||||
}
|
||||
|
||||
class AppLifecycleObserver : DefaultLifecycleObserver {
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
Timber.d("Application entered foreground")
|
||||
foreground = true
|
||||
}
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
Timber.d("Application entered background")
|
||||
foreground = false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var foreground = false
|
||||
|
||||
fun isForeground(): Boolean {
|
||||
return foreground
|
||||
}
|
||||
|
||||
lateinit var instance: WireGuardAutoTunnel
|
||||
private set
|
||||
}
|
||||
|
|
|
@ -7,25 +7,21 @@ import com.zaneschepke.wireguardautotunnel.service.network.WifiService
|
|||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ServiceComponent
|
||||
import dagger.hilt.android.scopes.ServiceScoped
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@Module
|
||||
@InstallIn(ServiceComponent::class)
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class ServiceModule {
|
||||
|
||||
@Binds
|
||||
@Wifi
|
||||
@ServiceScoped
|
||||
abstract fun provideWifiService(wifiService: WifiService): NetworkService
|
||||
|
||||
@Binds
|
||||
@MobileData
|
||||
@ServiceScoped
|
||||
abstract fun provideMobileDataService(mobileDataService: MobileDataService): NetworkService
|
||||
|
||||
@Binds
|
||||
@Ethernet
|
||||
@ServiceScoped
|
||||
abstract fun provideEthernetService(ethernetService: EthernetService): NetworkService
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ 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.network.NetworkService
|
||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.WireGuardTunnel
|
||||
|
@ -78,6 +79,9 @@ class TunnelModule {
|
|||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
serviceManager: ServiceManager,
|
||||
notificationService: NotificationService,
|
||||
@Wifi wifiService: NetworkService,
|
||||
@MobileData mobileDataService: NetworkService,
|
||||
@Ethernet ethernetService: NetworkService,
|
||||
): TunnelService {
|
||||
return WireGuardTunnel(
|
||||
amneziaBackend,
|
||||
|
@ -88,6 +92,9 @@ class TunnelModule {
|
|||
ioDispatcher,
|
||||
serviceManager,
|
||||
notificationService,
|
||||
wifiService,
|
||||
mobileDataService,
|
||||
ethernetService,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class AppUpdateReceiver : BroadcastReceiver() {
|
|||
}
|
||||
if (!settings.isAutoTunnelEnabled) {
|
||||
val tunnels = appDataRepository.tunnels.getAll().filter { it.isActive }
|
||||
if (tunnels.isNotEmpty()) tunnelService.get().startTunnel(tunnels.first(), true)
|
||||
if (tunnels.isNotEmpty()) tunnelService.get().startTunnel(tunnels.first())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ class BootReceiver : BroadcastReceiver() {
|
|||
val tunState = tunnelService.get().vpnState.value.status
|
||||
if (activeTunnels.isNotEmpty() && tunState != TunnelState.UP) {
|
||||
Timber.i("Starting previously active tunnel")
|
||||
tunnelService.get().startTunnel(activeTunnels.first(), true)
|
||||
tunnelService.get().startTunnel(activeTunnels.first())
|
||||
}
|
||||
if (isAutoTunnelEnabled) {
|
||||
Timber.i("Starting watcher service from boot")
|
||||
|
|
|
@ -29,7 +29,7 @@ class ServiceManager
|
|||
val autoTunnelActive = _autoTunnelActive.asStateFlow()
|
||||
|
||||
var autoTunnelService = CompletableDeferred<AutoTunnelService>()
|
||||
var backgroundService = CompletableDeferred<TunnelBackgroundService>()
|
||||
var backgroundService = CompletableDeferred<TunnelForegroundService>()
|
||||
var autoTunnelTile = CompletableDeferred<AutoTunnelControlTile>()
|
||||
var tunnelControlTile = CompletableDeferred<TunnelControlTile>()
|
||||
|
||||
|
@ -59,10 +59,10 @@ class ServiceManager
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun startBackgroundService(tunnelConfig: TunnelConfig?) {
|
||||
suspend fun startBackgroundService(tunnelConfig: TunnelConfig) {
|
||||
if (backgroundService.isCompleted) return
|
||||
kotlin.runCatching {
|
||||
startService(TunnelBackgroundService::class.java, true)
|
||||
startService(TunnelForegroundService::class.java, true)
|
||||
backgroundService.await()
|
||||
backgroundService.getCompleted().start(tunnelConfig)
|
||||
}.onFailure {
|
||||
|
|
|
@ -16,7 +16,7 @@ import kotlinx.coroutines.CompletableDeferred
|
|||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TunnelBackgroundService : LifecycleService() {
|
||||
class TunnelForegroundService : LifecycleService() {
|
||||
|
||||
@Inject
|
||||
lateinit var notificationService: NotificationService
|
||||
|
@ -39,9 +39,9 @@ class TunnelBackgroundService : LifecycleService() {
|
|||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
fun start(tunnelConfig: TunnelConfig?) {
|
||||
fun start(tunnelConfig: TunnelConfig) {
|
||||
ServiceCompat.startForeground(
|
||||
this,
|
||||
this@TunnelForegroundService,
|
||||
NotificationService.KERNEL_SERVICE_NOTIFICATION_ID,
|
||||
createNotification(tunnelConfig),
|
||||
Constants.SYSTEM_EXEMPT_SERVICE_TYPE_ID,
|
|
@ -5,4 +5,8 @@ data class NetworkState(
|
|||
val isMobileDataConnected: Boolean = false,
|
||||
val isEthernetConnected: Boolean = false,
|
||||
val wifiName: String? = null,
|
||||
)
|
||||
) {
|
||||
fun hasNoCapabilities(): Boolean {
|
||||
return !isWifiConnected && !isMobileDataConnected && !isEthernetConnected
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class ShortcutsActivity : ComponentActivity() {
|
|||
Timber.d("Shortcut action on name: ${tunnelConfig?.name}")
|
||||
tunnelConfig?.let {
|
||||
when (intent.action) {
|
||||
Action.START.name -> tunnelService.get().startTunnel(it, true)
|
||||
Action.START.name -> tunnelService.get().startTunnel(it)
|
||||
Action.STOP.name -> tunnelService.get().stopTunnel()
|
||||
else -> Unit
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ class TunnelControlTile : TileService() {
|
|||
applicationScope.launch {
|
||||
if (tunnelService.vpnState.value.status.isUp()) return@launch tunnelService.stopTunnel()
|
||||
appDataRepository.getStartTunnelConfig()?.let {
|
||||
tunnelService.startTunnel(it, true)
|
||||
tunnelService.startTunnel(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
|
||||
interface TunnelService : Tunnel, org.amnezia.awg.backend.Tunnel {
|
||||
|
||||
suspend fun startTunnel(tunnelConfig: TunnelConfig?, background: Boolean = false)
|
||||
suspend fun startTunnel(tunnelConfig: TunnelConfig?)
|
||||
|
||||
suspend fun stopTunnel()
|
||||
|
||||
|
|
|
@ -2,33 +2,44 @@ package com.zaneschepke.wireguardautotunnel.service.tunnel
|
|||
|
||||
import com.wireguard.android.backend.Backend
|
||||
import com.wireguard.android.backend.Tunnel.State
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepository
|
||||
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.module.Ethernet
|
||||
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.module.Kernel
|
||||
import com.zaneschepke.wireguardautotunnel.module.MobileData
|
||||
import com.zaneschepke.wireguardautotunnel.module.Wifi
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationAction
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.NetworkState
|
||||
import com.zaneschepke.wireguardautotunnel.service.network.NetworkService
|
||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
|
||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService.Companion.VPN_NOTIFICATION_ID
|
||||
import com.zaneschepke.wireguardautotunnel.service.notification.WireGuardNotification
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.AmneziaStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.WireGuardStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.service.notification.WireGuardNotification
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asAmBackendState
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asBackendState
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.cancelWithMessage
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isReachable
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
@ -37,6 +48,7 @@ import kotlinx.coroutines.withContext
|
|||
import org.amnezia.awg.backend.Tunnel
|
||||
import timber.log.Timber
|
||||
import java.net.InetAddress
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
|
@ -51,6 +63,9 @@ constructor(
|
|||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
private val serviceManager: ServiceManager,
|
||||
private val notificationService: NotificationService,
|
||||
@Wifi private val wifiService: NetworkService,
|
||||
@MobileData private val mobileDataService: NetworkService,
|
||||
@Ethernet private val ethernetService: NetworkService,
|
||||
) : TunnelService {
|
||||
|
||||
private val _vpnState = MutableStateFlow(VpnState())
|
||||
|
@ -59,9 +74,11 @@ constructor(
|
|||
private var statsJob: Job? = null
|
||||
private var tunnelChangesJob: Job? = null
|
||||
private var pingJob: Job? = null
|
||||
private var networkJob: Job? = null
|
||||
|
||||
@get:Synchronized @set:Synchronized
|
||||
private var isKernelBackend: Boolean? = null
|
||||
private val isNetworkAvailable = AtomicBoolean(false)
|
||||
|
||||
private val tunnelControlMutex = Mutex()
|
||||
|
||||
|
@ -88,6 +105,23 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO refactor duplicate
|
||||
@OptIn(FlowPreview::class)
|
||||
private fun combineNetworkEventsJob(): Flow<NetworkState> {
|
||||
return combine(
|
||||
wifiService.status,
|
||||
mobileDataService.status,
|
||||
ethernetService.status,
|
||||
) { wifi, mobileData, ethernet ->
|
||||
NetworkState(
|
||||
wifi.available,
|
||||
mobileData.available,
|
||||
ethernet.available,
|
||||
wifi.name,
|
||||
)
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private suspend fun setState(tunnelConfig: TunnelConfig, tunnelState: TunnelState): Result<TunnelState> {
|
||||
return runCatching {
|
||||
when (val backend = backend()) {
|
||||
|
@ -113,20 +147,43 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun startTunnel(tunnelConfig: TunnelConfig?, background: Boolean) {
|
||||
override suspend fun startTunnel(tunnelConfig: TunnelConfig?) {
|
||||
withContext(ioDispatcher) {
|
||||
if (tunnelConfig == null || isTunnelAlreadyRunning(tunnelConfig)) return@withContext
|
||||
onBeforeStart(background)
|
||||
onBeforeStart(tunnelConfig)
|
||||
updateTunnelConfig(tunnelConfig) // need to update this here
|
||||
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true))
|
||||
withServiceActive {
|
||||
setState(tunnelConfig, TunnelState.UP).onSuccess {
|
||||
updateTunnelState(it, tunnelConfig)
|
||||
onTunnelStart(tunnelConfig, background)
|
||||
startActiveTunnelJobs()
|
||||
}.onFailure {
|
||||
onTunnelStop(tunnelConfig)
|
||||
// TODO improve this with better statuses and handling
|
||||
showTunnelStartFailed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showTunnelStartFailed() {
|
||||
if (WireGuardAutoTunnel.isForeground()) {
|
||||
SnackbarController.showMessage(StringValue.StringResource(R.string.error_tunnel_start))
|
||||
} else {
|
||||
launchStartFailedNotification()
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchStartFailedNotification() {
|
||||
with(notificationService) {
|
||||
val notification = createNotification(
|
||||
WireGuardNotification.NotificationChannels.VPN,
|
||||
title = context.getString(R.string.error_tunnel_start),
|
||||
)
|
||||
show(VPN_NOTIFICATION_ID, notification)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun stopTunnel() {
|
||||
withContext(ioDispatcher) {
|
||||
if (_vpnState.value.status.isDown()) return@withContext
|
||||
|
@ -200,31 +257,10 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun onBeforeStart(background: Boolean) {
|
||||
private suspend fun onBeforeStart(tunnelConfig: TunnelConfig) {
|
||||
with(_vpnState.value) {
|
||||
if (status.isUp()) stopTunnel() else clearJobsAndStats()
|
||||
if (isKernelBackend == true || background) serviceManager.startBackgroundService(tunnelConfig)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun onTunnelStart(tunnelConfig: TunnelConfig, background: Boolean) {
|
||||
startActiveTunnelJobs()
|
||||
if (_vpnState.value.status.isUp()) {
|
||||
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true))
|
||||
}
|
||||
if (isKernelBackend == false && !background) launchUserspaceTunnelNotification()
|
||||
}
|
||||
|
||||
private fun launchUserspaceTunnelNotification() {
|
||||
with(notificationService) {
|
||||
val notification = createNotification(
|
||||
WireGuardNotification.NotificationChannels.VPN,
|
||||
title = "${context.getString(R.string.tunnel_running)} - ${_vpnState.value.tunnelConfig?.name}",
|
||||
actions = listOf(
|
||||
notificationService.createNotificationAction(NotificationAction.TUNNEL_OFF),
|
||||
),
|
||||
)
|
||||
show(VPN_NOTIFICATION_ID, notification)
|
||||
serviceManager.startBackgroundService(tunnelConfig)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,14 +314,21 @@ constructor(
|
|||
statsJob?.cancelWithMessage("Tunnel stats job cancelled")
|
||||
tunnelChangesJob?.cancelWithMessage("Tunnel changes job cancelled")
|
||||
pingJob?.cancelWithMessage("Ping job cancelled")
|
||||
networkJob?.cancelWithMessage("Network job cancelled")
|
||||
}
|
||||
|
||||
override fun startActiveTunnelJobs() {
|
||||
statsJob = startTunnelStatisticsJob()
|
||||
tunnelChangesJob = startTunnelConfigChangesJob()
|
||||
if (_vpnState.value.tunnelConfig?.isPingEnabled == true) pingJob = startPingJob()
|
||||
if (_vpnState.value.tunnelConfig?.isPingEnabled == true) {
|
||||
startPingJobs()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startPingJobs() {
|
||||
pingJob = startPingJob()
|
||||
networkJob = startNetworkJob()
|
||||
}
|
||||
override fun getName(): String {
|
||||
return _vpnState.value.tunnelConfig?.name ?: ""
|
||||
}
|
||||
|
@ -331,6 +374,7 @@ constructor(
|
|||
if (this == null) return
|
||||
if (!isPingEnabled && pingJob?.isActive == true) {
|
||||
pingJob?.cancelWithMessage("Ping job cancelled")
|
||||
networkJob?.cancelWithMessage("Network job cancelled")
|
||||
return
|
||||
}
|
||||
restartPingJob()
|
||||
|
@ -339,7 +383,8 @@ constructor(
|
|||
|
||||
private fun restartPingJob() {
|
||||
pingJob?.cancelWithMessage("Ping job cancelled")
|
||||
pingJob = startPingJob()
|
||||
networkJob?.cancelWithMessage("Network job cancelled")
|
||||
startPingJobs()
|
||||
}
|
||||
|
||||
private fun startTunnelConfigChangesJob() = applicationScope.launch(ioDispatcher) {
|
||||
|
@ -376,13 +421,16 @@ constructor(
|
|||
do {
|
||||
run {
|
||||
with(_vpnState.value) {
|
||||
// TODO ignore when no connectivity
|
||||
if (status.isUp() && tunnelConfig != null) {
|
||||
if (status.isUp() && tunnelConfig != null && isNetworkAvailable.get()) {
|
||||
val reachable = pingTunnel(tunnelConfig)
|
||||
if (reachable.contains(false)) {
|
||||
Timber.i("Ping result: target was not reachable, bouncing the tunnel")
|
||||
bounceTunnel()
|
||||
delay(tunnelConfig.pingCooldown ?: Constants.PING_COOLDOWN)
|
||||
if (isNetworkAvailable.get()) {
|
||||
Timber.i("Ping result: target was not reachable, bouncing the tunnel")
|
||||
bounceTunnel()
|
||||
delay(tunnelConfig.pingCooldown ?: Constants.PING_COOLDOWN)
|
||||
} else {
|
||||
Timber.i("Ping result: target was not reachable, but not network available")
|
||||
}
|
||||
return@run
|
||||
} else {
|
||||
Timber.i("Ping result: all ping targets were reached successfully")
|
||||
|
@ -394,6 +442,17 @@ constructor(
|
|||
} while (true)
|
||||
}
|
||||
|
||||
private fun startNetworkJob() = applicationScope.launch(ioDispatcher) {
|
||||
combineNetworkEventsJob().collect {
|
||||
Timber.d("New network state: $it")
|
||||
if (!it.isWifiConnected && !it.isEthernetConnected && !it.isMobileDataConnected) {
|
||||
isNetworkAvailable.set(false)
|
||||
} else {
|
||||
isNetworkAvailable.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStateChange(newState: Tunnel.State) {
|
||||
_vpnState.update {
|
||||
it.copy(status = TunnelState.from(newState))
|
||||
|
|
|
@ -112,12 +112,14 @@ constructor(
|
|||
}
|
||||
|
||||
private suspend fun initTunnel() {
|
||||
if (tunnelService.get().getState() == TunnelState.UP) tunnelService.get().startActiveTunnelJobs()
|
||||
val activeTunnels = appDataRepository.tunnels.getActive()
|
||||
if (activeTunnels.isNotEmpty() &&
|
||||
tunnelService.get().getState() == TunnelState.DOWN
|
||||
) {
|
||||
tunnelService.get().startTunnel(activeTunnels.first())
|
||||
withContext(ioDispatcher) {
|
||||
if (tunnelService.get().getState() == TunnelState.UP) tunnelService.get().startActiveTunnelJobs()
|
||||
val activeTunnels = appDataRepository.tunnels.getActive()
|
||||
if (activeTunnels.isNotEmpty() &&
|
||||
tunnelService.get().getState() == TunnelState.DOWN
|
||||
) {
|
||||
tunnelService.get().startTunnel(activeTunnels.first())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
|
@ -142,7 +141,6 @@ class MainActivity : AppCompatActivity() {
|
|||
SnackbarControllerProvider { host ->
|
||||
WireguardAutoTunnelTheme(theme = appUiState.generalState.theme) {
|
||||
Scaffold(
|
||||
modifier = Modifier.background(color = MaterialTheme.colorScheme.background),
|
||||
contentWindowInsets = WindowInsets(0),
|
||||
snackbarHost = {
|
||||
SnackbarHost(host) { snackbarData: SnackbarData ->
|
||||
|
|
|
@ -82,7 +82,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
|
|||
|
||||
val startAutoTunnel = withVpnPermission<Unit> { viewModel.onToggleAutoTunnel() }
|
||||
val startTunnel = withVpnPermission<TunnelConfig> {
|
||||
viewModel.onTunnelStart(it, uiState.settings.isKernelEnabled)
|
||||
viewModel.onTunnelStart(it)
|
||||
}
|
||||
val autoTunnelToggleBattery = withIgnoreBatteryOpt(uiState.generalState.isBatteryOptimizationDisableShown) {
|
||||
if (!uiState.generalState.isBatteryOptimizationDisableShown) viewModel.setBatteryOptimizeDisableShown()
|
||||
|
@ -129,7 +129,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
|
|||
fun onTunnelToggle(checked: Boolean, tunnel: TunnelConfig) {
|
||||
if (!checked) viewModel.onTunnelStop().also { return }
|
||||
if (uiState.settings.isKernelEnabled) {
|
||||
viewModel.onTunnelStart(tunnel, uiState.settings.isKernelEnabled)
|
||||
viewModel.onTunnelStart(tunnel)
|
||||
} else {
|
||||
startTunnel.invoke(tunnel)
|
||||
}
|
||||
|
@ -226,8 +226,9 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
|
|||
) { tunnel ->
|
||||
val expanded = uiState.generalState.isTunnelStatsExpanded
|
||||
TunnelRowItem(
|
||||
tunnel.id == uiState.vpnState.tunnelConfig?.id &&
|
||||
uiState.vpnState.status.isUp(),
|
||||
tunnel.id == uiState.vpnState.tunnelConfig?.id && (
|
||||
uiState.vpnState.status.isUp() || (uiState.settings.isKernelEnabled && tunnel.isActive)
|
||||
),
|
||||
expanded,
|
||||
selectedTunnel?.id == tunnel.id,
|
||||
tunnel,
|
||||
|
|
|
@ -67,9 +67,9 @@ constructor(
|
|||
appDataRepository.appState.setTunnelStatsExpanded(expanded)
|
||||
}
|
||||
|
||||
fun onTunnelStart(tunnelConfig: TunnelConfig, background: Boolean) = viewModelScope.launch {
|
||||
fun onTunnelStart(tunnelConfig: TunnelConfig) = viewModelScope.launch {
|
||||
Timber.i("Starting tunnel ${tunnelConfig.name}")
|
||||
tunnelService.get().startTunnel(tunnelConfig, background)
|
||||
tunnelService.get().startTunnel(tunnelConfig)
|
||||
}
|
||||
|
||||
fun onTunnelStop() = viewModelScope.launch {
|
||||
|
@ -86,20 +86,6 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun generateQrCodeTunnelName(config: String): String {
|
||||
var defaultName = generateQrCodeDefaultName(config)
|
||||
val lines = config.lines().toMutableList()
|
||||
val linesIterator = lines.iterator()
|
||||
while (linesIterator.hasNext()) {
|
||||
val next = linesIterator.next()
|
||||
if (next.contains(Constants.QR_CODE_NAME_PROPERTY)) {
|
||||
defaultName = next.substringAfter(Constants.QR_CODE_NAME_PROPERTY).trim()
|
||||
break
|
||||
}
|
||||
}
|
||||
return defaultName
|
||||
}
|
||||
|
||||
private suspend fun makeTunnelNameUnique(name: String): String {
|
||||
return withContext(ioDispatcher) {
|
||||
val tunnels = appDataRepository.tunnels.getAll()
|
||||
|
|
|
@ -202,4 +202,5 @@
|
|||
<string name="remove_amnezia_compatibility">Remove Amnezia compatibility</string>
|
||||
<string name="exclude_lan">Exclude LAN</string>
|
||||
<string name="include_lan">Include LAN</string>
|
||||
<string name="error_tunnel_start">Failed to starting tunnel</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
What's new:
|
||||
- Ping feature now works independent of auto tunnel
|
||||
- Added convenience action for Amnezia compatibility
|
||||
- Added convenience action for excluding LAN from tunnel
|
||||
- Added debounce delay tuning option for auto tunnel
|
||||
- Many bug fixes and improvements
|
Loading…
Reference in New Issue