android: prepare VPN when quick tile is clicked (#473)
Currently, the VPN is prepared when MainActivity is launched. If Tailscale is enabled by a quick tile, the VPN is not prepared. This change creates an application scoped view model and moves the VPN prep to the application class so that it is not dependent on MainActivity. Fixes tailscale/tailscale#12489 Signed-off-by: kari-ts <kari@tailscale.com>pull/475/head
parent
4830d8826e
commit
10a4350c02
@ -0,0 +1,55 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package com.tailscale.ipn.ui.viewModel
|
||||
|
||||
import android.app.Application
|
||||
import android.net.VpnService
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class VpnViewModelFactory(private val application: Application) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(VpnViewModel::class.java)) {
|
||||
return VpnViewModel(application) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}
|
||||
|
||||
// Application context aware view model that tracks whether the VPN has been prepared. This must be
|
||||
// application scoped because Tailscale might be toggled on and off outside of the activity
|
||||
// lifecycle.
|
||||
class VpnViewModel(application: Application) : AndroidViewModel(application) {
|
||||
// Whether the VPN is prepared
|
||||
val _vpnPrepared = MutableStateFlow(false)
|
||||
val vpnPrepared: StateFlow<Boolean> = _vpnPrepared
|
||||
val TAG = "VpnViewModel"
|
||||
|
||||
init {
|
||||
prepareVpn()
|
||||
}
|
||||
|
||||
private fun prepareVpn() {
|
||||
// Check if the user has granted permission yet.
|
||||
if (!vpnPrepared.value) {
|
||||
val vpnIntent = VpnService.prepare(getApplication())
|
||||
if (vpnIntent != null) {
|
||||
setVpnPrepared(false)
|
||||
Log.d(TAG, "VpnService.prepare returned non-null intent")
|
||||
} else {
|
||||
setVpnPrepared(true)
|
||||
Log.d(TAG, "VpnService.prepare returned null intent, VPN is already prepared")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setVpnPrepared(prepared: Boolean) {
|
||||
_vpnPrepared.value = prepared
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue