Skip to content

Commit da8271f

Browse files
committed
android: expand SAF FileOps implementation
This expands the SAF FileOps to implement the refactored FileOps Updates tailscale/corp#29211 Signed-off-by: kari-ts <[email protected]>
1 parent e5a704f commit da8271f

File tree

11 files changed

+439
-133
lines changed

11 files changed

+439
-133
lines changed

android/src/main/java/com/tailscale/ipn/ui/model/NetMap.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ class Netmap {
1515
var Domain: String,
1616
var UserProfiles: Map<String, Tailcfg.UserProfile>,
1717
var TKAEnabled: Boolean,
18-
var DNS: Tailcfg.DNSConfig? = null
18+
var DNS: Tailcfg.DNSConfig? = null,
19+
var AllCaps: List<String> = emptyList()
1920
) {
2021
// Keys are tailcfg.UserIDs thet get stringified
2122
// Helpers
@@ -51,5 +52,9 @@ class Netmap {
5152
UserProfiles == other.UserProfiles &&
5253
TKAEnabled == other.TKAEnabled
5354
}
55+
56+
fun hasCap(capability: String): Boolean {
57+
return AllCaps.contains(capability)
58+
}
5459
}
5560
}

android/src/main/java/com/tailscale/ipn/ui/util/OutputStreamAdapter.kt

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,16 @@ import java.io.OutputStream
88

99
// This class adapts a Java OutputStream to the libtailscale.OutputStream interface.
1010
class OutputStreamAdapter(private val outputStream: OutputStream) : libtailscale.OutputStream {
11-
// writes data to the outputStream in its entirety. Returns -1 on error.
12-
override fun write(data: ByteArray): Long {
13-
return try {
14-
outputStream.write(data)
15-
outputStream.flush()
16-
data.size.toLong()
17-
} catch (e: Exception) {
18-
TSLog.d("OutputStreamAdapter", "write exception: $e")
19-
-1L
11+
// Write the entire buffer. If the underlying stream throws,
12+
// gomobile will convert the IOException into a Go error and
13+
// io.Copy will stop immediately.
14+
override fun write(data: ByteArray): Long {
15+
outputStream.write(data) // may throw IOException=
16+
return data.size.toLong() // reached only on success
17+
}
18+
override fun close() {
19+
try { outputStream.flush() } catch (_: Exception) { /* ignore */ }
20+
outputStream.close()
2021
}
21-
}
22-
23-
override fun close() {
24-
outputStream.close()
25-
}
2622
}
23+

android/src/main/java/com/tailscale/ipn/ui/view/UserSwitcherView.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package com.tailscale.ipn.ui.view
55

6+
import android.content.Intent
7+
import android.net.Uri
68
import androidx.compose.foundation.background
79
import androidx.compose.foundation.layout.Arrangement
810
import androidx.compose.foundation.layout.Column
@@ -12,6 +14,7 @@ import androidx.compose.foundation.layout.padding
1214
import androidx.compose.foundation.lazy.LazyColumn
1315
import androidx.compose.material.icons.Icons
1416
import androidx.compose.material.icons.filled.MoreVert
17+
import androidx.compose.material3.AlertDialog
1518
import androidx.compose.material3.DropdownMenu
1619
import androidx.compose.material3.DropdownMenuItem
1720
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -20,12 +23,15 @@ import androidx.compose.material3.IconButton
2023
import androidx.compose.material3.MaterialTheme
2124
import androidx.compose.material3.Scaffold
2225
import androidx.compose.material3.Text
26+
import androidx.compose.material3.TextButton
2327
import androidx.compose.runtime.Composable
2428
import androidx.compose.runtime.collectAsState
2529
import androidx.compose.runtime.getValue
2630
import androidx.compose.runtime.mutableStateOf
2731
import androidx.compose.runtime.remember
32+
import androidx.compose.runtime.setValue
2833
import androidx.compose.ui.Modifier
34+
import androidx.compose.ui.platform.LocalContext
2935
import androidx.compose.ui.res.stringResource
3036
import androidx.compose.ui.tooling.preview.Preview
3137
import androidx.compose.ui.unit.dp
@@ -50,6 +56,11 @@ fun UserSwitcherView(nav: UserSwitcherNav, viewModel: UserSwitcherViewModel = vi
5056
val users by viewModel.loginProfiles.collectAsState()
5157
val currentUser by viewModel.loggedInUser.collectAsState()
5258
val showHeaderMenu by viewModel.showHeaderMenu.collectAsState()
59+
var showDeleteDialog by remember { mutableStateOf(false) }
60+
val context = LocalContext.current
61+
val netmap = viewModel.netmap.collectAsState()
62+
val CapabilityIsOwner = "https://tailscale.com/capability/is-owner"
63+
val isOwner = netmap.value?.hasCap(CapabilityIsOwner) == true
5364

5465
Scaffold(
5566
topBar = {
@@ -138,10 +149,41 @@ fun UserSwitcherView(nav: UserSwitcherNav, viewModel: UserSwitcherViewModel = vi
138149
}
139150
})
140151
}
152+
153+
Lists.SectionDivider()
154+
Setting.Text(R.string.delete_tailnet, destructive = true) {
155+
showDeleteDialog = true
156+
}
141157
}
142158
}
143159
}
144160
}
161+
162+
if (showDeleteDialog) {
163+
AlertDialog(
164+
onDismissRequest = { showDeleteDialog = false },
165+
title = { Text(text = stringResource(R.string.delete_tailnet)) },
166+
text = { Text(text = stringResource(if (isOwner) R.string.request_deletion_owner else R.string.request_deletion_nonowner)) },
167+
confirmButton = {
168+
TextButton(
169+
onClick = {
170+
// Call into your ViewModel
171+
// viewModel.onDeleteClicked(viewModel.currentTailnetId)
172+
173+
val intent =
174+
Intent(Intent.ACTION_VIEW, Uri.parse("https://tailscale.com/contact/support"))
175+
context.startActivity(intent)
176+
showDeleteDialog = false
177+
}) {
178+
Text(text = stringResource(R.string.contact_support))
179+
}
180+
},
181+
dismissButton = {
182+
TextButton(onClick = { showDeleteDialog = false }) {
183+
Text(text = stringResource(R.string.cancel))
184+
}
185+
})
186+
}
145187
}
146188

147189
@Composable

0 commit comments

Comments
 (0)