From 05f3b58e10e6dea7c25d8d509ecb38c029c2fe28 Mon Sep 17 00:00:00 2001 From: Nick O'Neill Date: Mon, 21 Jul 2025 16:36:07 -0700 Subject: [PATCH 1/4] android: bump OSS (#678) OSS and Version updated to 1.85.235-t8453170aa-ge5a704f78 Signed-off-by: Nick O'Neill --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 73ff6e6..53f7650 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/tailscale/tailscale-android go 1.24.4 require ( - github.com/tailscale/wireguard-go v0.0.0-20250530210235-65cd6eed7d7f + github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da golang.org/x/mobile v0.0.0-20240806205939-81131f6468ab - tailscale.com v1.85.0-pre.0.20250627205655-0a64e86a0df8 + tailscale.com v1.85.0-pre.0.20250721193616-8453170aa120 ) require ( diff --git a/go.sum b/go.sum index 868652f..009fb76 100644 --- a/go.sum +++ b/go.sum @@ -163,8 +163,8 @@ github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:U github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M= github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y= -github.com/tailscale/wireguard-go v0.0.0-20250530210235-65cd6eed7d7f h1:vg3PmQdq1BbB2V81iC1VBICQtfwbVGZ/4A/p7QKXTK0= -github.com/tailscale/wireguard-go v0.0.0-20250530210235-65cd6eed7d7f/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= +github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da h1:jVRUZPRs9sqyKlYHHzHjAqKN+6e/Vog6NpHYeNPJqOw= +github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e h1:zOGKqN5D5hHhiYUp091JqK7DPCqSARyUfduhGUY8Bek= github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg= github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= @@ -235,5 +235,5 @@ howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= -tailscale.com v1.85.0-pre.0.20250627205655-0a64e86a0df8 h1:gR3XF35IWpV4WhON27gR2vd8ypXbnjnrj5WreLWFxWk= -tailscale.com v1.85.0-pre.0.20250627205655-0a64e86a0df8/go.mod h1:zrtwlwmFfEWbUz77UN58gaLADx4rXSecFhGO+XW0JbU= +tailscale.com v1.85.0-pre.0.20250721193616-8453170aa120 h1:i9i9tJL/rxOIpNSfjY1AsWR4HYk9lQIAntAEdzcusS0= +tailscale.com v1.85.0-pre.0.20250721193616-8453170aa120/go.mod h1:Lm8dnzU2i/Emw15r6sl3FRNp/liSQ/nYw6ZSQvIdZ1M= From 483a949eb2e20703597b4a9d69edb7672661ddb5 Mon Sep 17 00:00:00 2001 From: kari-ts <135075563+kari-ts@users.noreply.github.com> Date: Tue, 22 Jul 2025 16:51:31 -0700 Subject: [PATCH 2/4] android: don't show Taildrop picker on TV (#679) Updates tailscale/tailscale#16164 Signed-off-by: kari-ts --- .../main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt b/android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt index 9634a86..edb41eb 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt @@ -23,6 +23,7 @@ import com.tailscale.ipn.ui.model.Ipn import com.tailscale.ipn.ui.model.Ipn.State import com.tailscale.ipn.ui.model.Tailcfg import com.tailscale.ipn.ui.notifier.Notifier +import com.tailscale.ipn.ui.util.AndroidTVUtil import com.tailscale.ipn.ui.util.PeerCategorizer import com.tailscale.ipn.ui.util.PeerSet import com.tailscale.ipn.ui.util.TimeUtil @@ -226,7 +227,7 @@ class MainViewModel(private val vpnViewModel: VpnViewModel) : IpnViewModel() { } fun checkIfTaildropDirectorySelected() { - if (skipPromptsForAuthKeyLogin()) { + if (skipPromptsForAuthKeyLogin() || AndroidTVUtil.isAndroidTV()) { return } From b3626fc342581bba3a09204a5a1115ee98d94908 Mon Sep 17 00:00:00 2001 From: kari-ts <135075563+kari-ts@users.noreply.github.com> Date: Tue, 22 Jul 2025 16:59:00 -0700 Subject: [PATCH 3/4] android: bump OSS (#680) OSS and Version updated to 1.85.241-t729d6532f-ge5a704f78 Signed-off-by: kari-ts Signed-off-by: kari-ts <135075563+kari-ts@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 53f7650..0d9c935 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24.4 require ( github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da golang.org/x/mobile v0.0.0-20240806205939-81131f6468ab - tailscale.com v1.85.0-pre.0.20250721193616-8453170aa120 + tailscale.com v1.85.0-pre.0.20250722205428-729d6532ff35 ) require ( diff --git a/go.sum b/go.sum index 009fb76..1e56587 100644 --- a/go.sum +++ b/go.sum @@ -235,5 +235,5 @@ howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= -tailscale.com v1.85.0-pre.0.20250721193616-8453170aa120 h1:i9i9tJL/rxOIpNSfjY1AsWR4HYk9lQIAntAEdzcusS0= -tailscale.com v1.85.0-pre.0.20250721193616-8453170aa120/go.mod h1:Lm8dnzU2i/Emw15r6sl3FRNp/liSQ/nYw6ZSQvIdZ1M= +tailscale.com v1.85.0-pre.0.20250722205428-729d6532ff35 h1:RaZ9EcaONTkfAerz5hbjpFbtok9uqB46I34Q9T7VGQg= +tailscale.com v1.85.0-pre.0.20250722205428-729d6532ff35/go.mod h1:Lm8dnzU2i/Emw15r6sl3FRNp/liSQ/nYw6ZSQvIdZ1M= From 7aab785be0e826eb341554f83971e843e5d470f2 Mon Sep 17 00:00:00 2001 From: kari-ts <135075563+kari-ts@users.noreply.github.com> Date: Tue, 5 Aug 2025 07:19:03 -0700 Subject: [PATCH 4/4] android: add tailnet deletion dialog (#682) Add dialog for deleting tailnet in user switcher view. Fixes tailscale/corp#31024 Signed-off-by: kari-ts --- .../java/com/tailscale/ipn/ui/model/NetMap.kt | 7 +- .../tailscale/ipn/ui/view/UserSwitcherView.kt | 88 ++++++++++++++++++- android/src/main/res/values/strings.xml | 16 +++- 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/tailscale/ipn/ui/model/NetMap.kt b/android/src/main/java/com/tailscale/ipn/ui/model/NetMap.kt index 77322cb..f7a7a92 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/model/NetMap.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/model/NetMap.kt @@ -15,7 +15,8 @@ class Netmap { var Domain: String, var UserProfiles: Map, var TKAEnabled: Boolean, - var DNS: Tailcfg.DNSConfig? = null + var DNS: Tailcfg.DNSConfig? = null, + var AllCaps: List = emptyList() ) { // Keys are tailcfg.UserIDs thet get stringified // Helpers @@ -51,5 +52,9 @@ class Netmap { UserProfiles == other.UserProfiles && TKAEnabled == other.TKAEnabled } + + fun hasCap(capability: String): Boolean { + return AllCaps.contains(capability) + } } } diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/UserSwitcherView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/UserSwitcherView.kt index 6fb76a6..64d4bd4 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/UserSwitcherView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/UserSwitcherView.kt @@ -3,6 +3,8 @@ package com.tailscale.ipn.ui.view +import android.content.Intent +import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -10,8 +12,10 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.text.ClickableText import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.AlertDialog import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api @@ -20,13 +24,19 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel @@ -46,10 +56,14 @@ data class UserSwitcherNav( @OptIn(ExperimentalMaterial3Api::class) @Composable fun UserSwitcherView(nav: UserSwitcherNav, viewModel: UserSwitcherViewModel = viewModel()) { - val users by viewModel.loginProfiles.collectAsState() val currentUser by viewModel.loggedInUser.collectAsState() val showHeaderMenu by viewModel.showHeaderMenu.collectAsState() + var showDeleteDialog by remember { mutableStateOf(false) } + val context = LocalContext.current + val netmapState by viewModel.netmap.collectAsState() + val capabilityIsOwner = "https://tailscale.com/cap/is-owner" + val isOwner = netmapState?.hasCap(capabilityIsOwner) == true Scaffold( topBar = { @@ -138,10 +152,47 @@ fun UserSwitcherView(nav: UserSwitcherNav, viewModel: UserSwitcherViewModel = vi } }) } + + Lists.SectionDivider() + Setting.Text(R.string.delete_tailnet, destructive = true) { + showDeleteDialog = true + } } } } } + + if (showDeleteDialog) { + AlertDialog( + onDismissRequest = { showDeleteDialog = false }, + title = { Text(text = stringResource(R.string.delete_tailnet)) }, + text = { + if (isOwner) { + OwnerDeleteDialogText { + val uri = Uri.parse("https://login.tailscale.com/admin/settings/general") + context.startActivity(Intent(Intent.ACTION_VIEW, uri)) + } + } else { + Text(stringResource(R.string.request_deletion_nonowner)) + } + }, + confirmButton = { + TextButton( + onClick = { + val intent = + Intent(Intent.ACTION_VIEW, Uri.parse("https://tailscale.com/contact/support")) + context.startActivity(intent) + showDeleteDialog = false + }) { + Text(text = stringResource(R.string.contact_support)) + } + }, + dismissButton = { + TextButton(onClick = { showDeleteDialog = false }) { + Text(text = stringResource(R.string.cancel)) + } + }) + } } @Composable @@ -171,6 +222,41 @@ fun FusMenu( } } +@Composable +fun OwnerDeleteDialogText(onSettingsClick: () -> Unit) { + val part1 = stringResource(R.string.request_deletion_owner_part1) + val part2a = stringResource(R.string.request_deletion_owner_part2a) + val part2b = stringResource(R.string.request_deletion_owner_part2b) + + val annotatedText = buildAnnotatedString { + append(part1 + " ") + + pushStringAnnotation( + tag = "settings", annotation = "https://login.tailscale.com/admin/settings/general") + withStyle(style = SpanStyle(color = MaterialTheme.colorScheme.primary)) { + append("Settings > General") + } + pop() + + append(" $part2a\n\n") // newline after "Delete tailnet." + append(part2b) + } + + val context = LocalContext.current + ClickableText( + text = annotatedText, + style = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onSurface), + onClick = { offset -> + annotatedText + .getStringAnnotations(tag = "settings", start = offset, end = offset) + .firstOrNull() + ?.let { annotation -> + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(annotation.item)) + context.startActivity(intent) + } + }) +} + @Composable fun MenuItem(text: String, onClick: () -> Unit) { DropdownMenuItem( diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index ed1fa16..5cefe14 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -13,7 +13,7 @@ Not connected %s - Selected + Selected Offline OK Continue @@ -137,6 +137,20 @@ Custom control server URL Auth key + Delete tailnet + Contact support + All requests related to the removal or deletion of data are handled by our Support team. To open a request, tap the Contact Support button below to be taken to our contact form in the browser. Complete the form, and a Customer Support Engineer will work with you directly to assist. + + As the owner of this tailnet, to remove yourself from the tailnet you can either reassign ownership and contact our Support team, or delete the whole tailnet through the admin console. To do the latter, go to + + + and look for “Delete tailnet”. + + + + All requests related to the removal or deletion of data are handled by our Support team. To open a request, tap the Contact Support button below to be taken to our contact form in the browser. Complete the form, and a Customer Support Engineer will work with you directly to assist. + + Choose exit node Mullvad exit nodes