Skip to content

Commit

Permalink
ui: add Mullvad info view (#450)
Browse files Browse the repository at this point in the history
Updates tailscale/tailscale#9421

Adds a view to highlight Mullvad support when it is not enabled.

Signed-off-by: Andrea Gottardo <andrea@gottardo.me>
  • Loading branch information
agottardo authored Jul 24, 2024
1 parent b9917c8 commit 1465b2a
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 0 deletions.
3 changes: 3 additions & 0 deletions android/src/main/java/com/tailscale/ipn/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ 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.MullvadExitNodePickerList
import com.tailscale.ipn.ui.view.MullvadInfoView
import com.tailscale.ipn.ui.view.PeerDetails
import com.tailscale.ipn.ui.view.PermissionsView
import com.tailscale.ipn.ui.view.RunExitNodeView
Expand Down Expand Up @@ -179,6 +180,7 @@ class MainActivity : ComponentActivity() {
},
onNavigateBackToExitNodes = backTo("exitNodes"),
onNavigateToMullvad = { navController.navigate("mullvad") },
onNavigateToMullvadInfo = { navController.navigate("mullvad_info") },
onNavigateBackToMullvad = backTo("mullvad"),
onNavigateToMullvadCountry = { navController.navigate("mullvad/$it") },
onNavigateToRunAsExitNode = { navController.navigate("runExitNode") })
Expand All @@ -198,6 +200,7 @@ class MainActivity : ComponentActivity() {
composable("settings") { SettingsView(settingsNav) }
composable("exitNodes") { ExitNodePicker(exitNodePickerNav) }
composable("mullvad") { MullvadExitNodePickerList(exitNodePickerNav) }
composable("mullvad_info") { MullvadInfoView(exitNodePickerNav) }
composable(
"mullvad/{countryCode}",
arguments =
Expand Down
24 changes: 24 additions & 0 deletions android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ fun ExitNodePicker(
val mullvadExitNodesByCountryCode by model.mullvadExitNodesByCountryCode.collectAsState()
val mullvadExitNodeCount by model.mullvadExitNodeCount.collectAsState()
val anyActive by model.anyActive.collectAsState()
val shouldShowMullvadInfo by model.shouldShowMullvadInfo.collectAsState()
val allowLANAccess = Notifier.prefs.collectAsState().value?.ExitNodeAllowLANAccess == true
val showRunAsExitNode by MDMSettings.runExitNode.flow.collectAsState()
val allowLanAccessMDMDisposition by MDMSettings.exitNodeAllowLANAccess.flow.collectAsState()
Expand Down Expand Up @@ -91,6 +92,11 @@ fun ExitNodePicker(
MullvadItem(
nav, mullvadExitNodesByCountryCode.size, mullvadExitNodesByCountryCode.selected)
}
} else if (shouldShowMullvadInfo) {
item(key = "mullvad_info") {
Lists.SectionDivider()
MullvadInfoItem(nav)
}
}

if (!allowLanAccessMDMDisposition.hiddenFromUser) {
Expand Down Expand Up @@ -167,6 +173,24 @@ fun MullvadItem(nav: ExitNodePickerNav, count: Int, selected: Boolean) {
}
}

@Composable
fun MullvadInfoItem(nav: ExitNodePickerNav) {
Box {
ListItem(
modifier = Modifier.clickable { nav.onNavigateToMullvadInfo() },
headlineContent = {
Text(
stringResource(R.string.mullvad_exit_nodes),
style = MaterialTheme.typography.bodyMedium)
},
supportingContent = {
Text(
stringResource(R.string.enable_in_the_admin_console),
style = MaterialTheme.typography.bodyMedium)
})
}
}

@Composable
fun RunAsExitNodeItem(
nav: ExitNodePickerNav,
Expand Down
56 changes: 56 additions & 0 deletions android/src/main/java/com/tailscale/ipn/ui/view/MullvadInfoView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause

package com.tailscale.ipn.ui.view

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.tailscale.ipn.R
import com.tailscale.ipn.ui.viewModel.ExitNodePickerNav

@Composable
fun MullvadInfoView(nav: ExitNodePickerNav) {
Scaffold(
topBar = {
Header(R.string.choose_mullvad_exit_node, onBack = nav.onNavigateBackToExitNodes)
}) { innerPadding ->
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(20.dp),
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 48.dp),
modifier = Modifier.padding(innerPadding)) {
item {
Image(
painter = painterResource(id = R.drawable.mullvad_logo),
contentDescription = stringResource(R.string.the_mullvad_vpn_logo))
}
item {
Text(
stringResource(R.string.mullvad_info_title),
fontFamily = MaterialTheme.typography.titleLarge.fontFamily,
fontSize = MaterialTheme.typography.titleLarge.fontSize,
fontWeight = FontWeight.SemiBold)
}
item {
Text(
stringResource(R.string.mullvad_info_explainer),
color = MaterialTheme.colorScheme.secondary,
textAlign = TextAlign.Center)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ data class ExitNodePickerNav(
val onNavigateBackHome: () -> Unit,
val onNavigateBackToExitNodes: () -> Unit,
val onNavigateToMullvad: () -> Unit,
val onNavigateToMullvadInfo: () -> Unit,
val onNavigateBackToMullvad: () -> Unit,
val onNavigateToMullvadCountry: (String) -> Unit,
val onNavigateToRunAsExitNode: () -> Unit,
Expand Down Expand Up @@ -55,6 +56,7 @@ class ExitNodePickerViewModel(private val nav: ExitNodePickerNav) : IpnViewModel
val mullvadBestAvailableByCountry: StateFlow<Map<String, ExitNode>> = MutableStateFlow(TreeMap())
val mullvadExitNodeCount: StateFlow<Int> = MutableStateFlow(0)
val anyActive: StateFlow<Boolean> = MutableStateFlow(false)
val shouldShowMullvadInfo: StateFlow<Boolean> = MutableStateFlow(false)

init {
viewModelScope.launch {
Expand Down Expand Up @@ -128,6 +130,13 @@ class ExitNodePickerViewModel(private val nav: ExitNodePickerNav) : IpnViewModel
mullvadBestAvailableByCountry.set(bestAvailableByCountry)

anyActive.set(allNodes.any { it.selected })

prefs?.let { prefs ->
// Only show the Mullvad info view if the user is an admin and is using a Tailscale
// control server, as it wouldn't be actionable information otherwise.
shouldShowMullvadInfo.set(
netmap.SelfNode.isAdmin && prefs.ControlURL.endsWith(".tailscale.com"))
}
}
}
}
Expand Down
Binary file added android/src/main/res/drawable/mullvad_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,10 @@
<string name="specifies_a_list_of_apps_that_will_always_use_tailscale_routes_and_dns_when_tailscale_is_running_all_other_apps_won_t_use_tailscale_if_this_value_is_non_empty">Specifies a list of apps that will always use Tailscale routes and DNS when Tailscale is running. All other apps won\'t use Tailscale if this value is non-empty.</string>
<string name="included_packages">Included packages</string>
<string name="excluded_packages">Excluded packages</string>

<!-- Strings for the Mullvad info view -->
<string name="enable_in_the_admin_console">Enable in the admin console</string>
<string name="mullvad_info_explainer">Once you enable Mullvad VPN on the admin console, you\'ll be able to encrypt and route your traffic using Mullvad’s global network of servers as exit nodes.</string>
<string name="mullvad_info_title">Mullvad VPN is not configured</string>
<string name="the_mullvad_vpn_logo">The Mullvad VPN logo</string>
</resources>

0 comments on commit 1465b2a

Please sign in to comment.