android: exit node banner ui improvements (#408)
-show if device is running as an exit node -show exit node connection status Updates tailscale/corp#19122 Follow up will include: -make exit node picker recompose when exit node connection status changes -prevent user from running as exit node if it is using an exit node and vice versa instead of silently failing -add explanation box for MDM offline state Signed-off-by: kari-ts <kari@tailscale.com>pull/412/head
parent
72f35cd318
commit
a05829b3c0
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package com.tailscale.ipn.ui.util
|
||||||
|
|
||||||
|
import com.tailscale.ipn.ui.model.Ipn
|
||||||
|
|
||||||
|
class AdvertisedRoutesHelper {
|
||||||
|
companion object {
|
||||||
|
fun exitNodeOnFromPrefs(prefs: Ipn.Prefs): Boolean {
|
||||||
|
var v4 = false
|
||||||
|
var v6 = false
|
||||||
|
prefs.AdvertiseRoutes?.forEach {
|
||||||
|
if (it == "0.0.0.0/0") {
|
||||||
|
v4 = true
|
||||||
|
}
|
||||||
|
if (it == "::/0") {
|
||||||
|
v6 = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v4 && v6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,97 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
package com.tailscale.ipn.ui.viewModel
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.tailscale.ipn.ui.localapi.Client
|
|
||||||
import com.tailscale.ipn.ui.model.Ipn
|
|
||||||
import com.tailscale.ipn.ui.notifier.Notifier
|
|
||||||
import com.tailscale.ipn.ui.util.LoadingIndicator
|
|
||||||
import com.tailscale.ipn.ui.util.set
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class RunExitNodeViewModelFactory() : ViewModelProvider.Factory {
|
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
|
||||||
return RunExitNodeViewModel() as T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AdvertisedRoutesHelper() {
|
|
||||||
companion object {
|
|
||||||
fun exitNodeOnFromPrefs(prefs: Ipn.Prefs): Boolean {
|
|
||||||
var v4 = false
|
|
||||||
var v6 = false
|
|
||||||
prefs.AdvertiseRoutes?.forEach {
|
|
||||||
if (it == "0.0.0.0/0") {
|
|
||||||
v4 = true
|
|
||||||
}
|
|
||||||
if (it == "::/0") {
|
|
||||||
v6 = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v4 && v6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RunExitNodeViewModel() : IpnViewModel() {
|
|
||||||
|
|
||||||
val isRunningExitNode: StateFlow<Boolean> = MutableStateFlow(false)
|
|
||||||
var lastPrefs: Ipn.Prefs? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
viewModelScope.launch {
|
|
||||||
Notifier.prefs.stateIn(viewModelScope).collect { prefs ->
|
|
||||||
Log.d("RunExitNode", "prefs: AdvertiseRoutes=" + prefs?.AdvertiseRoutes.toString())
|
|
||||||
prefs?.let {
|
|
||||||
lastPrefs = it
|
|
||||||
isRunningExitNode.set(AdvertisedRoutesHelper.exitNodeOnFromPrefs(it))
|
|
||||||
} ?: run { isRunningExitNode.set(false) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setRunningExitNode(isOn: Boolean) {
|
|
||||||
LoadingIndicator.start()
|
|
||||||
lastPrefs?.let { currentPrefs ->
|
|
||||||
val newPrefs: Ipn.MaskedPrefs
|
|
||||||
if (isOn) {
|
|
||||||
newPrefs = setZeroRoutes(currentPrefs)
|
|
||||||
} else {
|
|
||||||
newPrefs = removeAllZeroRoutes(currentPrefs)
|
|
||||||
}
|
|
||||||
Client(viewModelScope).editPrefs(newPrefs) { result ->
|
|
||||||
LoadingIndicator.stop()
|
|
||||||
Log.d("RunExitNodeViewModel", "Edited prefs: $result")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setZeroRoutes(prefs: Ipn.Prefs): Ipn.MaskedPrefs {
|
|
||||||
val newRoutes = (removeAllZeroRoutes(prefs).AdvertiseRoutes ?: emptyList()).toMutableList()
|
|
||||||
newRoutes.add("0.0.0.0/0")
|
|
||||||
newRoutes.add("::/0")
|
|
||||||
val newPrefs = Ipn.MaskedPrefs()
|
|
||||||
newPrefs.AdvertiseRoutes = newRoutes
|
|
||||||
return newPrefs
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun removeAllZeroRoutes(prefs: Ipn.Prefs): Ipn.MaskedPrefs {
|
|
||||||
val newRoutes = emptyList<String>().toMutableList()
|
|
||||||
(prefs.AdvertiseRoutes ?: emptyList()).forEach {
|
|
||||||
if (it != "0.0.0.0/0" && it != "::/0") {
|
|
||||||
newRoutes.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val newPrefs = Ipn.MaskedPrefs()
|
|
||||||
newPrefs.AdvertiseRoutes = newRoutes
|
|
||||||
return newPrefs
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue