@ -3,10 +3,15 @@
package com.tailscale.ipn.ui.viewModel
import android.content.Intent
import android.net.VpnService
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.lifecycle.viewModelScope
import com.tailscale.ipn.App
import com.tailscale.ipn.R
import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Ipn.State
import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.util.PeerCategorizer
@ -16,17 +21,23 @@ import com.tailscale.ipn.ui.util.set
import com.tailscale.ipn.App
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import java.time.Duration
class MainViewModel : IpnViewModel ( ) {
// The user readable state of the system
val stateRes : StateFlow < Int > = MutableStateFlow ( State . NoState . userStringRes ( null ) )
val stateRes : StateFlow < Int > = MutableStateFlow ( userStringRes ( State . NoState , State . NoState , true ) )
// The expected state of the VPN toggle
private val _vpnToggleState = MutableStateFlow ( false )
val vpnToggleState : StateFlow < Boolean > = _vpnToggleState
val vpnToggleState : StateFlow < Boolean > = _vpnToggleState
// Whether or not the VPN has been prepared
private val _vpnPrepared = MutableStateFlow ( false )
val vpnPrepared : StateFlow < Boolean > = _vpnPrepared
private var vpnPermissionLauncher : ActivityResultLauncher < Intent > ? = null
// The list of peers
val peers : StateFlow < List < PeerSet > > = MutableStateFlow ( emptyList < PeerSet > ( ) )
@ -49,16 +60,20 @@ class MainViewModel : IpnViewModel() {
viewModelScope . launch {
var previousState : State ? = null
Notifier . state . collect { currentState ->
val userString = currentState . userStringRes ( previousState )
stateRes . set ( userString )
_vpnToggleState . value = when {
currentState == State . Running || currentState == State . Starting -> true
previousState == State . NoState && currentState == State . Starting -> true
else -> false
combine ( Notifier . state , vpnPrepared ) { state , prepared -> state to prepared }
. collect { ( currentState , prepared ) ->
stateRes . set ( userStringRes ( currentState , previousState , prepared ) )
val isOn = when {
currentState == State . Running || currentState == State . Starting -> true
previousState == State . NoState && currentState == State . Starting -> true
else -> false
}
_vpnToggleState . value = isOn
previousState = currentState
}
previousState = currentState
}
}
viewModelScope . launch {
@ -91,22 +106,48 @@ class MainViewModel : IpnViewModel() {
}
}
fun showVPNPermissionLauncherIfUnauthorized ( ) {
val vpnIntent = VpnService . prepare ( App . get ( ) )
if ( vpnIntent != null ) {
vpnPermissionLauncher ?. launch ( vpnIntent )
}
}
fun toggleVpn ( ) {
val state = Notifier . state . value
val isPrepared = vpnPrepared . value
when {
!is Prepared -> showVPNPermissionLauncherIfUnauthorized ( )
state == Ipn . State . Running -> stopVPN ( )
else -> startVPN ( )
}
}
fun searchPeers ( searchTerm : String ) {
this . searchTerm . set ( searchTerm )
}
fun setVpnPermissionLauncher ( launcher : ActivityResultLauncher < Intent > ) {
vpnPermissionLauncher = launcher
}
fun setVpnPrepared ( prepared : Boolean ) {
_vpnPrepared . value = prepared
}
}
private fun State ?. userStringRes ( previousState : State ? ) : Int {
val resId = when {
previousState == State . NoState && this == State . Starting -> R . string . starting
this == State . NoState -> R . string . placeholder
this == State . InUseOtherUser -> R . string . placeholder
this == State . NeedsLogin -> R . string . please _login
this == State . NeedsMachineAuth -> R . string . needs _machine _auth
this == State . Stopped -> R . string . stopped
this == State . Starting -> R . string . starting
this == State . Running -> R . string . connected
private fun userStringRes( currentState: State ? , previousState: State ? , vpnPrepared : Boolean ) : Int {
return when {
previousState == State . NoState && currentState == State . Starting -> R . string . starting
currentState == State . NoState -> R . string . placeholder
currentState == State . InUseOtherUser -> R . string . placeholder
currentState == State . NeedsLogin -> if ( vpnPrepared ) R . string . please _login else R . string . connect _to _vpn
currentState == State . NeedsMachineAuth -> R . string . needs _machine _auth
currentState == State . Stopped -> R . string . stopped
currentState == State . Starting -> R . string . starting
currentState == State . Running -> R . string . connected
else -> R . string . placeholder
}
return resId
}
}