android: fix paddings and headers of Taildrop destination picker (#465)

Updates tailscale/corp#22362

First round of polish for the Taildrop device picker, to use more consistent metrics and SectionDivider resembling the rest of the app. We'll follow up with device icons like the ones we have on iOS in a later PR.

Signed-off-by: Andrea Gottardo <andrea@gottardo.me>
pull/474/head
Andrea Gottardo 3 months ago committed by GitHub
parent 20a5beab3e
commit 4830d8826e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -61,6 +61,7 @@
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND" />

@ -8,11 +8,12 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@ -31,6 +32,7 @@ import coil.compose.AsyncImage
import com.tailscale.ipn.R import com.tailscale.ipn.R
import com.tailscale.ipn.ui.model.Ipn import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Tailcfg import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.util.Lists.SectionDivider
import com.tailscale.ipn.ui.util.set import com.tailscale.ipn.ui.util.set
import com.tailscale.ipn.ui.viewModel.TaildropViewModel import com.tailscale.ipn.ui.viewModel.TaildropViewModel
import com.tailscale.ipn.ui.viewModel.TaildropViewModelFactory import com.tailscale.ipn.ui.viewModel.TaildropViewModelFactory
@ -44,33 +46,36 @@ fun TaildropView(
viewModel: TaildropViewModel = viewModel: TaildropViewModel =
viewModel(factory = TaildropViewModelFactory(requestedTransfers, applicationScope)) viewModel(factory = TaildropViewModelFactory(requestedTransfers, applicationScope))
) { ) {
Scaffold(topBar = { Header(R.string.share) }) { paddingInsets -> Scaffold(
val showDialog = viewModel.showDialog.collectAsState().value contentWindowInsets = WindowInsets.Companion.statusBars,
topBar = { Header(R.string.share) }) { paddingInsets ->
val showDialog = viewModel.showDialog.collectAsState().value
// Show the error overlay // Show the error overlay
showDialog?.let { ErrorDialog(type = it, action = { viewModel.showDialog.set(null) }) } showDialog?.let { ErrorDialog(type = it, action = { viewModel.showDialog.set(null) }) }
Column(modifier = Modifier.padding(paddingInsets)) { Column(modifier = Modifier.padding(paddingInsets)) {
FileShareHeader( FileShareHeader(
fileTransfers = requestedTransfers.collectAsState().value, fileTransfers = requestedTransfers.collectAsState().value,
totalSize = viewModel.totalSize) totalSize = viewModel.totalSize)
Spacer(modifier = Modifier.size(8.dp))
when (viewModel.state.collectAsState().value) { when (viewModel.state.collectAsState().value) {
Ipn.State.Running -> { Ipn.State.Running -> {
val peers by viewModel.myPeers.collectAsState() val peers by viewModel.myPeers.collectAsState()
val context = LocalContext.current val context = LocalContext.current
FileSharePeerList( FileSharePeerList(
peers = peers, peers = peers,
stateViewGenerator = { peerId -> viewModel.TrailingContentForPeer(peerId = peerId) }, stateViewGenerator = { peerId ->
onShare = { viewModel.share(context, it) }) viewModel.TrailingContentForPeer(peerId = peerId)
} },
else -> { onShare = { viewModel.share(context, it) })
FileShareConnectView { viewModel.startVPN() } }
else -> {
FileShareConnectView { viewModel.startVPN() }
}
}
} }
} }
}
}
} }
@Composable @Composable
@ -79,9 +84,7 @@ fun FileSharePeerList(
stateViewGenerator: @Composable (String) -> Unit, stateViewGenerator: @Composable (String) -> Unit,
onShare: (Tailcfg.Node) -> Unit onShare: (Tailcfg.Node) -> Unit
) { ) {
Column(modifier = Modifier.padding(horizontal = 8.dp)) { SectionDivider(stringResource(R.string.my_devices))
Text(stringResource(R.string.my_devices), style = MaterialTheme.typography.titleMedium)
}
when (peers.isEmpty()) { when (peers.isEmpty()) {
true -> { true -> {
@ -113,8 +116,8 @@ fun FileSharePeerList(
@Composable @Composable
fun FileShareConnectView(onToggle: () -> Unit) { fun FileShareConnectView(onToggle: () -> Unit) {
Column( Column(
modifier = Modifier.padding(horizontal = 8.dp).fillMaxHeight(), modifier = Modifier.padding(horizontal = 16.dp).fillMaxHeight(),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.spacedBy(6.dp, alignment = Alignment.CenterVertically),
horizontalAlignment = Alignment.CenterHorizontally) { horizontalAlignment = Alignment.CenterHorizontally) {
Text( Text(
stringResource(R.string.connect_to_your_tailnet_to_share_files), stringResource(R.string.connect_to_your_tailnet_to_share_files),
@ -130,7 +133,7 @@ fun FileShareConnectView(onToggle: () -> Unit) {
@Composable @Composable
fun FileShareHeader(fileTransfers: List<Ipn.OutgoingFile>, totalSize: Long) { fun FileShareHeader(fileTransfers: List<Ipn.OutgoingFile>, totalSize: Long) {
Column(modifier = Modifier.padding(horizontal = 8.dp)) { Column(modifier = Modifier.padding(horizontal = 12.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
IconForTransfer(fileTransfers) IconForTransfer(fileTransfers)
Column(modifier = Modifier.padding(horizontal = 8.dp)) { Column(modifier = Modifier.padding(horizontal = 8.dp)) {
@ -151,10 +154,12 @@ fun FileShareHeader(fileTransfers: List<Ipn.OutgoingFile>, totalSize: Long) {
} }
} }
val size = Formatter.formatFileSize(LocalContext.current, totalSize.toLong()) val size = Formatter.formatFileSize(LocalContext.current, totalSize.toLong())
Text(size, style = MaterialTheme.typography.titleMedium) Text(
size,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.secondary)
} }
} }
HorizontalDivider()
} }
} }

@ -218,11 +218,11 @@
<!-- Strings for the share activity --> <!-- Strings for the share activity -->
<string name="share">Send via Tailscale</string> <string name="share">Send with Taildrop</string>
<string name="share_device_not_connected">Unable to share files with this device. This device is not currently connected to the tailnet.</string> <string name="share_device_not_connected">Unable to share files with this device. This device is not currently connected to the tailnet.</string>
<string name="no_files_to_share">No files to share</string> <string name="no_files_to_share">No files to share</string>
<string name="file_count">%1$s files</string> <string name="file_count">%1$s files</string>
<string name="connect_to_your_tailnet_to_share_files">Connect to your tailnet to share files</string> <string name="connect_to_your_tailnet_to_share_files">Connect to your tailnet to share files.</string>
<string name="my_devices">My devices</string> <string name="my_devices">My devices</string>
<string name="no_devices_to_share_with">There are no devices on your tailnet to share to</string> <string name="no_devices_to_share_with">There are no devices on your tailnet to share to</string>
<string name="taildrop_share_failed">Taildrop failed. Some files were not shared. Please try again.</string> <string name="taildrop_share_failed">Taildrop failed. Some files were not shared. Please try again.</string>

Loading…
Cancel
Save