From 0d1a3cf415ab26fb4930a959e9bfbf72c8715c28 Mon Sep 17 00:00:00 2001 From: Andrea Gottardo Date: Fri, 22 Mar 2024 11:34:34 -0700 Subject: [PATCH] Revert "android: add UI to run as exit node (#230)" (#235) This reverts commit c3b62124bbda9a8dab255d1695e9ace3f4d49855. Signed-off-by: Andrea Gottardo --- .../java/com/tailscale/ipn/MainActivity.kt | 8 +- .../java/com/tailscale/ipn/ui/model/Ipn.kt | 2 +- .../tailscale/ipn/ui/view/ExitNodePicker.kt | 27 +--- .../tailscale/ipn/ui/view/RunExitNodeView.kt | 115 ------------------ .../ui/viewModel/ExitNodePickerViewModel.kt | 6 +- .../ipn/ui/viewModel/RunExitNodeViewModel.kt | 97 --------------- android/src/main/res/drawable/android.xml | 5 - android/src/main/res/drawable/computer.xml | 5 - android/src/main/res/drawable/globe.xml | 5 - android/src/main/res/values/strings.xml | 12 +- 10 files changed, 6 insertions(+), 276 deletions(-) delete mode 100644 android/src/main/java/com/tailscale/ipn/ui/view/RunExitNodeView.kt delete mode 100644 android/src/main/java/com/tailscale/ipn/ui/viewModel/RunExitNodeViewModel.kt delete mode 100644 android/src/main/res/drawable/android.xml delete mode 100644 android/src/main/res/drawable/computer.xml delete mode 100644 android/src/main/res/drawable/globe.xml diff --git a/android/src/main/java/com/tailscale/ipn/MainActivity.kt b/android/src/main/java/com/tailscale/ipn/MainActivity.kt index 2e0dc6a..342c596 100644 --- a/android/src/main/java/com/tailscale/ipn/MainActivity.kt +++ b/android/src/main/java/com/tailscale/ipn/MainActivity.kt @@ -37,7 +37,6 @@ import com.tailscale.ipn.ui.view.MainViewNavigation import com.tailscale.ipn.ui.view.ManagedByView import com.tailscale.ipn.ui.view.MullvadExitNodePicker import com.tailscale.ipn.ui.view.PeerDetails -import com.tailscale.ipn.ui.view.RunExitNodeView import com.tailscale.ipn.ui.view.Settings import com.tailscale.ipn.ui.view.UserSwitcherView import com.tailscale.ipn.ui.viewModel.ExitNodePickerNav @@ -92,9 +91,7 @@ class MainActivity : ComponentActivity() { onNavigateHome = { navController.popBackStack(route = "main", inclusive = false) }, - onNavigateToExitNodePicker = { navController.popBackStack() }, - onNavigateToMullvadCountry = { navController.navigate("mullvad/$it") }, - onNavigateToRunAsExitNode = { navController.navigate("runExitNode") }) + onNavigateToMullvadCountry = { navController.navigate("mullvad/$it") }) composable("main") { MainView(navigation = mainViewNav) } composable("settings") { Settings(settingsNav) } @@ -106,9 +103,6 @@ class MainActivity : ComponentActivity() { MullvadExitNodePicker( it.arguments!!.getString("countryCode")!!, exitNodePickerNav) } - composable("runExitNode") { - RunExitNodeView(exitNodePickerNav) - } } composable( "peerDetails/{nodeId}", diff --git a/android/src/main/java/com/tailscale/ipn/ui/model/Ipn.kt b/android/src/main/java/com/tailscale/ipn/ui/model/Ipn.kt index 7d4bba9..65f1130 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/model/Ipn.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/model/Ipn.kt @@ -111,7 +111,7 @@ class Ipn { ShieldsUpSet = true } - var AdvertiseRoutes: List? = null + var AdvertiseRoutes: Boolean? = null set(value) { field = value AdvertiseRoutesSet = true diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt b/android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt index 96f0e48..d291a18 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt @@ -15,7 +15,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.KeyboardArrowRight import androidx.compose.material.icons.outlined.Check import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme @@ -26,6 +25,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel @@ -50,11 +50,6 @@ fun ExitNodePicker( val anyActive = model.anyActive.collectAsState() LazyColumn(modifier = Modifier.padding(innerPadding)) { - item(key = "runExitNode") { - RunAsExitNodeItem(nav = nav, viewModel = model) - HorizontalDivider() - } - item(key = "none") { ExitNodeItem( model, @@ -137,27 +132,9 @@ fun ExitNodeItem( Icon(Icons.Outlined.Check, contentDescription = stringResource(R.string.more)) } else if (!node.online) { Spacer(modifier = Modifier.width(8.dp)) - Text(stringResource(R.string.offline)) + Text(stringResource(R.string.offline), fontStyle = FontStyle.Italic) } } }) } } - -@Composable -fun RunAsExitNodeItem(nav: ExitNodePickerNav, viewModel: ExitNodePickerViewModel) { - val isRunningExitNode = viewModel.isRunningExitNode.collectAsState().value - - Box { - ListItem( - modifier = Modifier.clickable { nav.onNavigateToRunAsExitNode() }, - headlineContent = { Text(stringResource(id = R.string.run_as_exit_node)) }, - trailingContent = { - if (isRunningExitNode) { - Text(stringResource(R.string.enabled)) - } else { - Text(stringResource(R.string.disabled)) - } - }) - } -} diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/RunExitNodeView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/RunExitNodeView.kt deleted file mode 100644 index 7356e4c..0000000 --- a/android/src/main/java/com/tailscale/ipn/ui/view/RunExitNodeView.kt +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -package com.tailscale.ipn.ui.view - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.outlined.ArrowForward -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import com.tailscale.ipn.R -import com.tailscale.ipn.ui.theme.ts_color_light_blue -import com.tailscale.ipn.ui.util.LoadingIndicator -import com.tailscale.ipn.ui.viewModel.ExitNodePickerNav -import com.tailscale.ipn.ui.viewModel.RunExitNodeViewModel -import com.tailscale.ipn.ui.viewModel.RunExitNodeViewModelFactory - -@Composable -fun RunExitNodeView( - nav: ExitNodePickerNav, - model: RunExitNodeViewModel = viewModel(factory = RunExitNodeViewModelFactory()) -) { - val isRunningExitNode = model.isRunningExitNode.collectAsState().value - - Scaffold( - topBar = { Header(R.string.run_as_exit_node, onBack = nav.onNavigateToExitNodePicker) }) { - innerPadding -> - LoadingIndicator.Wrap { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = - Arrangement.spacedBy(16.dp, alignment = Alignment.Top), - modifier = Modifier.padding(innerPadding).padding(16.dp).fillMaxHeight()) { - RunExitNodeGraphic() - - if (isRunningExitNode) { - Text( - stringResource(R.string.running_as_exit_node), - fontFamily = MaterialTheme.typography.titleLarge.fontFamily, - fontSize = MaterialTheme.typography.titleLarge.fontSize, - fontWeight = FontWeight.SemiBold) - Text(stringResource(R.string.run_exit_node_explainer_running)) - } else { - Text( - stringResource(R.string.run_this_device_as_an_exit_node), - fontFamily = MaterialTheme.typography.titleLarge.fontFamily, - fontSize = MaterialTheme.typography.titleLarge.fontSize, - fontWeight = FontWeight.SemiBold) - Text(stringResource(R.string.run_exit_node_explainer)) - } - Text(stringResource(R.string.run_exit_node_caution), color = Color.Red) - - PrimaryActionButton(onClick = { model.setRunningExitNode(!isRunningExitNode) }) { - if (isRunningExitNode) { - Text(stringResource(R.string.stop_running_as_exit_node)) - } else { - Text(stringResource(R.string.start_running_as_exit_node)) - } - } - } - } - } -} - -@Composable -fun RunExitNodeGraphic() { - @Composable - fun ArrowForward() { - Icon( - Icons.AutoMirrored.Outlined.ArrowForward, - "Arrow Forward", - tint = MaterialTheme.colorScheme.secondary, - modifier = Modifier.size(24.dp)) - } - - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(vertical = 18.dp)) { - Icon( - painter = painterResource(id = R.drawable.computer), - "Computer icon", - tint = ts_color_light_blue, - modifier = Modifier.size(36.dp)) - ArrowForward() - Icon( - painter = painterResource(id = R.drawable.android), - "Android icon", - tint = ts_color_light_blue, - modifier = Modifier.size(36.dp)) - ArrowForward() - Icon( - painter = painterResource(id = R.drawable.globe), - "Globe icon", - tint = ts_color_light_blue, - modifier = Modifier.size(36.dp)) - } -} diff --git a/android/src/main/java/com/tailscale/ipn/ui/viewModel/ExitNodePickerViewModel.kt b/android/src/main/java/com/tailscale/ipn/ui/viewModel/ExitNodePickerViewModel.kt index f97dbba..90c0e79 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/viewModel/ExitNodePickerViewModel.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/viewModel/ExitNodePickerViewModel.kt @@ -12,18 +12,16 @@ import com.tailscale.ipn.ui.model.StableNodeID import com.tailscale.ipn.ui.notifier.Notifier import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.util.set +import java.util.TreeMap import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import java.util.TreeMap data class ExitNodePickerNav( val onNavigateHome: () -> Unit, - val onNavigateToExitNodePicker: () -> Unit, val onNavigateToMullvadCountry: (String) -> Unit, - val onNavigateToRunAsExitNode: () -> Unit, ) class ExitNodePickerViewModelFactory(private val nav: ExitNodePickerNav) : @@ -51,7 +49,6 @@ class ExitNodePickerViewModel(private val nav: ExitNodePickerNav) : IpnViewModel MutableStateFlow(TreeMap()) val mullvadBestAvailableByCountry: StateFlow> = MutableStateFlow(TreeMap()) val anyActive: StateFlow = MutableStateFlow(false) - val isRunningExitNode: StateFlow = MutableStateFlow(false) init { viewModelScope.launch { @@ -59,7 +56,6 @@ class ExitNodePickerViewModel(private val nav: ExitNodePickerNav) : IpnViewModel .combine(Notifier.prefs) { netmap, prefs -> Pair(netmap, prefs) } .stateIn(viewModelScope) .collect { (netmap, prefs) -> - isRunningExitNode.set(prefs?.let { AdvertisedRoutesHelper.exitNodeOnFromPrefs(it) }) val exitNodeId = prefs?.ExitNodeID netmap?.Peers?.let { peers -> val allNodes = diff --git a/android/src/main/java/com/tailscale/ipn/ui/viewModel/RunExitNodeViewModel.kt b/android/src/main/java/com/tailscale/ipn/ui/viewModel/RunExitNodeViewModel.kt deleted file mode 100644 index f847b8d..0000000 --- a/android/src/main/java/com/tailscale/ipn/ui/viewModel/RunExitNodeViewModel.kt +++ /dev/null @@ -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 create(modelClass: Class): 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 = 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().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 - } -} diff --git a/android/src/main/res/drawable/android.xml b/android/src/main/res/drawable/android.xml deleted file mode 100644 index 4db0bf4..0000000 --- a/android/src/main/res/drawable/android.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/computer.xml b/android/src/main/res/drawable/computer.xml deleted file mode 100644 index 43924ec..0000000 --- a/android/src/main/res/drawable/computer.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/globe.xml b/android/src/main/res/drawable/globe.xml deleted file mode 100644 index 1625b40..0000000 --- a/android/src/main/res/drawable/globe.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index 36ec421..ad5d193 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -12,7 +12,7 @@ %s More - Offline + offline OK @@ -90,15 +90,5 @@ Tailnet Exit Nodes Mullvad VPN Best Available - Run as Exit Node - Run this device as an exit node? - Other devices in your tailnet will be able to route their Internet traffic through this Android device. Make sure to approve this exit node in the admin console in order for other devices to see it. - Caution: Running an exit node will severely impact battery life. On a metered data plan, significant cellular data charges may also apply. Always disable this feature when no longer needed. - Stop Running as Exit Node - Start Running as Exit Node - Now Running as Exit Node - Other devices in your tailnet can now route their Internet traffic through this Android device. Make sure to approve this exit node in the admin console in order for other devices to see it. - Enabled - Disabled