You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.2 KiB
Kotlin
102 lines
3.2 KiB
Kotlin
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package com.tailscale.ipn.ui.viewModel
|
|
|
|
import android.util.Log
|
|
import androidx.lifecycle.viewModelScope
|
|
import com.tailscale.ipn.R
|
|
import com.tailscale.ipn.ui.localapi.Client
|
|
import com.tailscale.ipn.ui.model.Ipn.State
|
|
import com.tailscale.ipn.ui.notifier.Notifier
|
|
import com.tailscale.ipn.ui.util.LoadingIndicator
|
|
import com.tailscale.ipn.ui.util.PeerCategorizer
|
|
import com.tailscale.ipn.ui.util.PeerSet
|
|
import com.tailscale.ipn.ui.util.set
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.flow.StateFlow
|
|
import kotlinx.coroutines.launch
|
|
|
|
class MainViewModel : IpnViewModel() {
|
|
|
|
// The user readable state of the system
|
|
val stateRes: StateFlow<Int> = MutableStateFlow(State.NoState.userStringRes())
|
|
|
|
// The expected state of the VPN toggle
|
|
val vpnToggleState: StateFlow<Boolean> = MutableStateFlow(false)
|
|
|
|
// The list of peers
|
|
val peers: StateFlow<List<PeerSet>> = MutableStateFlow(emptyList<PeerSet>())
|
|
|
|
// The current state of the IPN for determining view visibility
|
|
val ipnState = Notifier.state
|
|
|
|
val prefs = Notifier.prefs
|
|
val netmap = Notifier.netmap
|
|
|
|
// The active search term for filtering peers
|
|
val searchTerm: StateFlow<String> = MutableStateFlow("")
|
|
|
|
private val peerCategorizer = PeerCategorizer()
|
|
|
|
init {
|
|
viewModelScope.launch {
|
|
Notifier.state.collect { state ->
|
|
stateRes.set(state.userStringRes())
|
|
vpnToggleState.set(
|
|
(state == State.Running || state == State.Starting || state == State.NoState))
|
|
}
|
|
}
|
|
|
|
viewModelScope.launch {
|
|
Notifier.netmap.collect { it ->
|
|
it?.let { netmap ->
|
|
peerCategorizer.regenerateGroupedPeers(netmap)
|
|
peers.set(peerCategorizer.groupedAndFilteredPeers(searchTerm.value))
|
|
}
|
|
}
|
|
}
|
|
|
|
viewModelScope.launch {
|
|
searchTerm.collect { term -> peers.set(peerCategorizer.groupedAndFilteredPeers(term)) }
|
|
}
|
|
|
|
viewModelScope.launch {
|
|
Notifier.prefs.collect { prefs -> Log.d(TAG, "Main VM - prefs = ${prefs}") }
|
|
}
|
|
}
|
|
|
|
fun searchPeers(searchTerm: String) {
|
|
this.searchTerm.set(searchTerm)
|
|
}
|
|
|
|
fun toggleExitNode() {
|
|
val prefs = prefs.value ?: return
|
|
|
|
LoadingIndicator.start()
|
|
if (prefs.activeExitNodeID != null) {
|
|
// We have an active exit node so we should keep it, but disable it
|
|
Client(viewModelScope).setUseExitNode(false) { LoadingIndicator.stop() }
|
|
} else if (prefs.selectedExitNodeID != null) {
|
|
// We have a prior exit node to enable
|
|
Client(viewModelScope).setUseExitNode(true) { LoadingIndicator.stop() }
|
|
} else {
|
|
// This should not be possible. In this state the button is hidden
|
|
Log.e(TAG, "No exit node to disable an no prior exit node to enable")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun State?.userStringRes(): Int {
|
|
return when (this) {
|
|
State.NoState -> R.string.waiting
|
|
State.InUseOtherUser -> R.string.placeholder
|
|
State.NeedsLogin -> R.string.please_login
|
|
State.NeedsMachineAuth -> R.string.placeholder
|
|
State.Stopped -> R.string.stopped
|
|
State.Starting -> R.string.starting
|
|
State.Running -> R.string.connected
|
|
else -> R.string.placeholder
|
|
}
|
|
}
|