fix: tile auto tunnel toggle and wifi internet connection check
Change tile toggle behavior to also toggle auto tunnel service to allow user temporary override and re-enablement of auto tunnel from the tile. Add tunnel name to tile. Fix bug where wifi networks without internet access were impacting auto tunneling determinations. Closes #22
This commit is contained in:
parent
0e64bbb4e1
commit
eeccc71469
|
@ -17,7 +17,7 @@ android {
|
||||||
|
|
||||||
val versionMajor = 2
|
val versionMajor = 2
|
||||||
val versionMinor = 3
|
val versionMinor = 3
|
||||||
val versionPatch = 6
|
val versionPatch = 7
|
||||||
val versionBuild = 0
|
val versionBuild = 0
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.wireguard.android.backend.Tunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
||||||
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceState
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceTracker
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceTracker
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService
|
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService
|
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService
|
||||||
|
@ -39,18 +40,15 @@ class TunnelControlTile : TileService() {
|
||||||
private lateinit var job : Job
|
private lateinit var job : Job
|
||||||
|
|
||||||
override fun onStartListening() {
|
override fun onStartListening() {
|
||||||
if (!this::job.isInitialized) {
|
job = scope.launch {
|
||||||
job = scope.launch {
|
updateTileState()
|
||||||
updateTileState()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Timber.d("On start listening")
|
|
||||||
super.onStartListening()
|
super.onStartListening()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTileAdded() {
|
override fun onTileAdded() {
|
||||||
super.onTileAdded()
|
super.onTileAdded()
|
||||||
qsTile.contentDescription = "Toggle VPN"
|
qsTile.contentDescription = this.resources.getString(R.string.toggle_vpn)
|
||||||
scope.launch {
|
scope.launch {
|
||||||
updateTileState();
|
updateTileState();
|
||||||
}
|
}
|
||||||
|
@ -62,43 +60,50 @@ class TunnelControlTile : TileService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick() {
|
override fun onClick() {
|
||||||
|
super.onClick()
|
||||||
unlockAndRun {
|
unlockAndRun {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
try {
|
try {
|
||||||
if(vpnService.getState() == Tunnel.State.UP) {
|
val tunnel = determineTileTunnel();
|
||||||
stopTunnel();
|
if(tunnel != null) {
|
||||||
return@launch
|
attemptWatcherServiceToggle(tunnel.toString())
|
||||||
}
|
if(vpnService.getState() == Tunnel.State.UP) {
|
||||||
val settings = settingsRepo.getAll()
|
stopTunnelService();
|
||||||
if (!settings.isNullOrEmpty()) {
|
|
||||||
val setting = settings.first()
|
|
||||||
if (setting.defaultTunnel != null) {
|
|
||||||
startTunnel(setting.defaultTunnel!!)
|
|
||||||
} else {
|
} else {
|
||||||
val config = configRepo.getAll()?.first();
|
startTunnelService(tunnel.toString())
|
||||||
if(config != null) {
|
|
||||||
startTunnel(config.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e : Exception) {
|
||||||
|
Timber.e(e.message)
|
||||||
} finally {
|
} finally {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onClick()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopTunnel() {
|
private suspend fun determineTileTunnel() : TunnelConfig? {
|
||||||
|
var tunnelConfig : TunnelConfig? = null;
|
||||||
|
val settings = settingsRepo.getAll()
|
||||||
|
if (!settings.isNullOrEmpty()) {
|
||||||
|
val setting = settings.first()
|
||||||
|
tunnelConfig = if (setting.defaultTunnel != null) {
|
||||||
|
TunnelConfig.from(setting.defaultTunnel!!);
|
||||||
|
} else {
|
||||||
|
val config = configRepo.getAll()?.first();
|
||||||
|
config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tunnelConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopTunnelService() {
|
||||||
ServiceTracker.actionOnService(
|
ServiceTracker.actionOnService(
|
||||||
Action.STOP, this@TunnelControlTile,
|
Action.STOP, this.applicationContext,
|
||||||
WireGuardConnectivityWatcherService::class.java)
|
|
||||||
ServiceTracker.actionOnService(
|
|
||||||
Action.STOP, this@TunnelControlTile,
|
|
||||||
WireGuardTunnelService::class.java)
|
WireGuardTunnelService::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startTunnel(tunnelConfig : String) {
|
private fun startTunnelService(tunnelConfig : String) {
|
||||||
ServiceTracker.actionOnService(
|
ServiceTracker.actionOnService(
|
||||||
Action.START, this.applicationContext,
|
Action.START, this.applicationContext,
|
||||||
WireGuardTunnelService::class.java,
|
WireGuardTunnelService::class.java,
|
||||||
|
@ -107,42 +112,64 @@ class TunnelControlTile : TileService() {
|
||||||
tunnelConfig))
|
tunnelConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startWatcherService(tunnelConfig : String) {
|
||||||
|
ServiceTracker.actionOnService(
|
||||||
|
Action.START, this,
|
||||||
|
WireGuardConnectivityWatcherService::class.java, mapOf(this.resources.
|
||||||
|
getString(R.string.tunnel_extras_key) to
|
||||||
|
tunnelConfig))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopWatcherService() {
|
||||||
|
ServiceTracker.actionOnService(
|
||||||
|
Action.STOP, this,
|
||||||
|
WireGuardConnectivityWatcherService::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attemptWatcherServiceToggle(tunnelConfig : String) {
|
||||||
|
scope.launch {
|
||||||
|
val settings = settingsRepo.getAll()
|
||||||
|
if (!settings.isNullOrEmpty()) {
|
||||||
|
val setting = settings.first()
|
||||||
|
if(setting.isAutoTunnelEnabled) {
|
||||||
|
when(ServiceTracker.getServiceState( this@TunnelControlTile,
|
||||||
|
WireGuardConnectivityWatcherService::class.java,)) {
|
||||||
|
ServiceState.STARTED -> stopWatcherService()
|
||||||
|
ServiceState.STOPPED -> startWatcherService(tunnelConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun updateTileState() {
|
private suspend fun updateTileState() {
|
||||||
vpnService.state.collect {
|
vpnService.state.collect {
|
||||||
when(it) {
|
when(it) {
|
||||||
Tunnel.State.UP -> {
|
Tunnel.State.UP -> {
|
||||||
setTileOn()
|
qsTile.state = Tile.STATE_ACTIVE
|
||||||
}
|
}
|
||||||
Tunnel.State.DOWN -> {
|
Tunnel.State.DOWN -> {
|
||||||
setTileOff()
|
qsTile.state = Tile.STATE_INACTIVE;
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
qsTile.state = Tile.STATE_UNAVAILABLE
|
qsTile.state = Tile.STATE_UNAVAILABLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val config = determineTileTunnel();
|
||||||
|
setTileDescription(config?.name ?: this.resources.getString(R.string.no_tunnel_available))
|
||||||
qsTile.updateTile()
|
qsTile.updateTile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setTileOff() {
|
private fun setTileDescription(description : String) {
|
||||||
qsTile.state = Tile.STATE_INACTIVE;
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
qsTile.subtitle = "Off"
|
qsTile.subtitle = description
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
qsTile.stateDescription = "VPN Off";
|
qsTile.stateDescription = description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setTileOn() {
|
|
||||||
qsTile.state = Tile.STATE_ACTIVE;
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
qsTile.subtitle = "On"
|
|
||||||
}
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
qsTile.stateDescription = "VPN On";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun cancelJob() {
|
private fun cancelJob() {
|
||||||
if(this::job.isInitialized) {
|
if(this::job.isInitialized) {
|
||||||
job.cancel();
|
job.cancel();
|
||||||
|
|
|
@ -4,10 +4,7 @@ import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
|
|
||||||
open class ForegroundService : Service() {
|
open class ForegroundService : Service() {
|
||||||
|
|
|
@ -24,6 +24,7 @@ object ServiceTracker {
|
||||||
|
|
||||||
fun <T : Service> actionOnService(action: Action, application: Application, cls : Class<T>, extras : Map<String,String>? = null) {
|
fun <T : Service> actionOnService(action: Action, application: Application, cls : Class<T>, extras : Map<String,String>? = null) {
|
||||||
if (getServiceState(application, cls) == ServiceState.STOPPED && action == Action.STOP) return
|
if (getServiceState(application, cls) == ServiceState.STOPPED && action == Action.STOP) return
|
||||||
|
if (getServiceState(application, cls) == ServiceState.STARTED && action == Action.START) return
|
||||||
val intent = Intent(application, cls).also {
|
val intent = Intent(application, cls).also {
|
||||||
it.action = action.name
|
it.action = action.name
|
||||||
extras?.forEach {(k, v) ->
|
extras?.forEach {(k, v) ->
|
||||||
|
@ -40,6 +41,7 @@ object ServiceTracker {
|
||||||
|
|
||||||
fun <T : Service> actionOnService(action: Action, context: Context, cls : Class<T>, extras : Map<String,String>? = null) {
|
fun <T : Service> actionOnService(action: Action, context: Context, cls : Class<T>, extras : Map<String,String>? = null) {
|
||||||
if (getServiceState(context, cls) == ServiceState.STOPPED && action == Action.STOP) return
|
if (getServiceState(context, cls) == ServiceState.STOPPED && action == Action.STOP) return
|
||||||
|
if (getServiceState(context, cls) == ServiceState.STARTED && action == Action.START) return
|
||||||
val intent = Intent(context, cls).also {
|
val intent = Intent(context, cls).also {
|
||||||
it.action = action.name
|
it.action = action.name
|
||||||
extras?.forEach {(k, v) ->
|
extras?.forEach {(k, v) ->
|
||||||
|
|
|
@ -85,7 +85,6 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cancelWatcherJob()
|
cancelWatcherJob()
|
||||||
stopVPN()
|
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ abstract class BaseNetworkService<T : BaseNetworkService<T>>(val context: Contex
|
||||||
object : ConnectivityManager.NetworkCallback(
|
object : ConnectivityManager.NetworkCallback(
|
||||||
FLAG_INCLUDE_LOCATION_INFO
|
FLAG_INCLUDE_LOCATION_INFO
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun onAvailable(network: Network) {
|
override fun onAvailable(network: Network) {
|
||||||
trySend(NetworkStatus.Available(network))
|
trySend(NetworkStatus.Available(network))
|
||||||
}
|
}
|
||||||
|
@ -68,6 +67,8 @@ abstract class BaseNetworkService<T : BaseNetworkService<T>>(val context: Contex
|
||||||
}
|
}
|
||||||
val request = NetworkRequest.Builder()
|
val request = NetworkRequest.Builder()
|
||||||
.addTransportType(networkCapability)
|
.addTransportType(networkCapability)
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||||
.build()
|
.build()
|
||||||
connectivityManager.registerNetworkCallback(request, networkStatusCallback)
|
connectivityManager.registerNetworkCallback(request, networkStatusCallback)
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ class WireGuardTunnel @Inject constructor(private val backend : Backend,
|
||||||
override val tunnelName get() = _tunnelName.asStateFlow()
|
override val tunnelName get() = _tunnelName.asStateFlow()
|
||||||
|
|
||||||
private val _state = MutableSharedFlow<Tunnel.State>(
|
private val _state = MutableSharedFlow<Tunnel.State>(
|
||||||
|
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||||
replay = 1)
|
replay = 1)
|
||||||
|
|
||||||
private val _handshakeStatus = MutableSharedFlow<HandshakeStatus>(replay = 1,
|
private val _handshakeStatus = MutableSharedFlow<HandshakeStatus>(replay = 1,
|
||||||
|
|
|
@ -11,5 +11,5 @@ data class Settings(
|
||||||
var isTunnelOnMobileDataEnabled : Boolean = false,
|
var isTunnelOnMobileDataEnabled : Boolean = false,
|
||||||
var trustedNetworkSSIDs : MutableList<String> = mutableListOf(),
|
var trustedNetworkSSIDs : MutableList<String> = mutableListOf(),
|
||||||
var defaultTunnel : String? = null,
|
var defaultTunnel : String? = null,
|
||||||
var isAlwaysOnVpnEnabled : Boolean = false
|
var isAlwaysOnVpnEnabled : Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
|
@ -84,4 +84,6 @@
|
||||||
<string name="detecting_location_services_disabled">Detecting Location Services disabled</string>
|
<string name="detecting_location_services_disabled">Detecting Location Services disabled</string>
|
||||||
<string name="precise_location_message">This feature requires precise location to access Wi-Fi SSID name. Please enable precise location here or in the app settings.</string>
|
<string name="precise_location_message">This feature requires precise location to access Wi-Fi SSID name. Please enable precise location here or in the app settings.</string>
|
||||||
<string name="request">Request</string>
|
<string name="request">Request</string>
|
||||||
|
<string name="toggle_vpn">Toggle VPN</string>
|
||||||
|
<string name="no_tunnel_available">No tunnels available</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue