android/ui: more UI tweaks

1. Add title to internal debug options

Updates tailscale/corp#19045

Signed-off-by: Percy Wegmann <percy@tailscale.com>
pull/315/head
Percy Wegmann 1 month ago committed by Percy Wegmann
parent 0ae9da385e
commit d396fdab27

@ -22,6 +22,8 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContract import androidx.activity.result.contract.ActivityResultContract
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.slideOutHorizontally
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -159,7 +161,9 @@ class MainActivity : ComponentActivity() {
onNavigateToMullvadCountry = { navController.navigate("mullvad/$it") }, onNavigateToMullvadCountry = { navController.navigate("mullvad/$it") },
onNavigateToRunAsExitNode = { navController.navigate("runExitNode") }) onNavigateToRunAsExitNode = { navController.navigate("runExitNode") })
composable("main") { MainView(navigation = mainViewNav) } composable("main", enterTransition = { fadeIn(animationSpec = tween(150)) }) {
MainView(navigation = mainViewNav)
}
composable("settings") { SettingsView(settingsNav) } composable("settings") { SettingsView(settingsNav) }
composable("exitNodes") { ExitNodePicker(exitNodePickerNav) } composable("exitNodes") { ExitNodePicker(exitNodePickerNav) }
composable("mullvad") { MullvadExitNodePickerList(exitNodePickerNav) } composable("mullvad") { MullvadExitNodePickerList(exitNodePickerNav) }
@ -188,7 +192,9 @@ class MainActivity : ComponentActivity() {
composable("permissions") { composable("permissions") {
PermissionsView(backTo("settings"), ::openApplicationSettings) PermissionsView(backTo("settings"), ::openApplicationSettings)
} }
composable("intro") { IntroView(backTo("main")) } composable("intro", exitTransition = { fadeOut(animationSpec = tween(150)) }) {
IntroView(backTo("main"))
}
} }
// Show the intro screen one time // Show the intro screen one time

@ -28,7 +28,7 @@ object Lists {
@Composable @Composable
fun SectionDivider(title: String? = null) { fun SectionDivider(title: String? = null) {
Box(Modifier.size(0.dp, 16.dp)) Box(Modifier.size(0.dp, 16.dp))
title?.let { SectionTitle(title) } title?.let { LargeTitle(title) }
} }
@Composable @Composable
@ -37,7 +37,7 @@ object Lists {
} }
@Composable @Composable
fun SectionTitle( fun LargeTitle(
title: String, title: String,
bottomPadding: Dp = 0.dp, bottomPadding: Dp = 0.dp,
style: TextStyle = MaterialTheme.typography.titleMedium, style: TextStyle = MaterialTheme.typography.titleMedium,
@ -56,6 +56,20 @@ object Lists {
} }
} }
@Composable
fun MutedHeader(text: String) {
Box(
modifier =
Modifier.fillMaxWidth()
.background(color = MaterialTheme.colorScheme.surface, shape = RectangleShape)) {
Text(
modifier = Modifier.padding(start = 16.dp, top = 16.dp),
text = text,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant)
}
}
@Composable @Composable
fun InfoItem(text: CharSequence, onClick: (() -> Unit)? = null) { fun InfoItem(text: CharSequence, onClick: (() -> Unit)? = null) {
val style = val style =
@ -69,6 +83,14 @@ object Lists {
} }
}) })
} }
@Composable
fun MultilineDescription(headlineContent: @Composable () -> Unit) {
ListItem(
headlineContent = {
Box(modifier = Modifier.padding(vertical = 8.dp)) { headlineContent() }
})
}
} }
/** Similar to items() but includes a horizontal divider between items. */ /** Similar to items() but includes a horizontal divider between items. */

@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -38,19 +37,23 @@ fun BugReportView(backToSettings: BackNavigation, model: BugReportViewModel = vi
Scaffold(topBar = { Header(R.string.bug_report_title, onBack = backToSettings) }) { innerPadding Scaffold(topBar = { Header(R.string.bug_report_title, onBack = backToSettings) }) { innerPadding
-> ->
Column(modifier = Modifier.padding(innerPadding).fillMaxWidth().fillMaxHeight().verticalScroll(rememberScrollState())) { Column(
ListItem( modifier =
headlineContent = { Modifier.padding(innerPadding)
.fillMaxWidth()
.fillMaxHeight()
.verticalScroll(rememberScrollState())) {
Lists.MultilineDescription {
ClickableText( ClickableText(
text = contactText(), text = contactText(),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
onClick = { handler.openUri(Links.SUPPORT_URL) }) onClick = { handler.openUri(Links.SUPPORT_URL) })
}) }
ClipboardValueView(bugReportID, title = stringResource(R.string.bug_report_id)) ClipboardValueView(bugReportID, title = stringResource(R.string.bug_report_id))
Lists.InfoItem(stringResource(id = R.string.bug_report_id_desc)) Lists.InfoItem(stringResource(id = R.string.bug_report_id_desc))
} }
} }
} }

@ -322,7 +322,8 @@ fun PeerList(
) { ) {
val peerList = viewModel.peers.collectAsState(initial = emptyList<PeerSet>()) val peerList = viewModel.peers.collectAsState(initial = emptyList<PeerSet>())
val searchTermStr by viewModel.searchTerm.collectAsState(initial = "") val searchTermStr by viewModel.searchTerm.collectAsState(initial = "")
val showNoResults = derivedStateOf { searchTermStr.isNotEmpty() && peerList.value.isEmpty() }.value val showNoResults =
derivedStateOf { searchTermStr.isNotEmpty() && peerList.value.isEmpty() }.value
val netmap = viewModel.netmap.collectAsState() val netmap = viewModel.netmap.collectAsState()
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
@ -373,7 +374,7 @@ fun PeerList(
.fillMaxSize() .fillMaxSize()
.background(color = MaterialTheme.colorScheme.surface)) .background(color = MaterialTheme.colorScheme.surface))
Lists.SectionTitle( Lists.LargeTitle(
stringResource(id = R.string.no_results), stringResource(id = R.string.no_results),
bottomPadding = 8.dp, bottomPadding = 8.dp,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
@ -394,7 +395,7 @@ fun PeerList(
.fillMaxSize() .fillMaxSize()
.background(color = MaterialTheme.colorScheme.surface)) .background(color = MaterialTheme.colorScheme.surface))
Lists.SectionTitle( Lists.LargeTitle(
peerSet.user?.DisplayName ?: stringResource(id = R.string.unknown_user), peerSet.user?.DisplayName ?: stringResource(id = R.string.unknown_user),
bottomPadding = 8.dp, bottomPadding = 8.dp,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,

@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.Box
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.fillMaxWidth
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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@ -24,7 +23,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@ -81,17 +79,7 @@ fun PeerDetails(
modifier = Modifier.padding(innerPadding), modifier = Modifier.padding(innerPadding),
) { ) {
item(key = "tailscaleAddresses") { item(key = "tailscaleAddresses") {
Box( Lists.MutedHeader(stringResource(R.string.tailscale_addresses))
modifier =
Modifier.fillMaxWidth()
.background(
color = MaterialTheme.colorScheme.surface, shape = RectangleShape)) {
Text(
modifier = Modifier.padding(start = 16.dp, top = 16.dp),
text = stringResource(R.string.tailscale_addresses),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant)
}
} }
itemsWithDividers(node.displayAddresses, key = { it.address }) { itemsWithDividers(node.displayAddresses, key = { it.address }) {

@ -97,6 +97,7 @@ fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewMo
// TODO: put a heading for the debug section // TODO: put a heading for the debug section
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Lists.SectionDivider() Lists.SectionDivider()
Lists.MutedHeader(text = stringResource(R.string.internal_debug_options))
Setting.Text(R.string.mdm_settings, onClick = settingsNav.onNavigateToMDMSettings) Setting.Text(R.string.mdm_settings, onClick = settingsNav.onNavigateToMDMSettings)
} }
} }

@ -3,7 +3,6 @@
package com.tailscale.ipn.ui.view package com.tailscale.ipn.ui.view
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
@ -24,7 +23,6 @@ import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.tailscale.ipn.R import com.tailscale.ipn.R
import com.tailscale.ipn.ui.Links import com.tailscale.ipn.ui.Links
@ -88,15 +86,12 @@ fun TailnetLockSetupView(
private fun ExplainerView() { private fun ExplainerView() {
val handler = LocalUriHandler.current val handler = LocalUriHandler.current
ListItem( Lists.MultilineDescription {
headlineContent = { ClickableText(
Box(modifier = Modifier.padding(vertical = 8.dp)) { explainerText(),
ClickableText( onClick = { handler.openUri(Links.TAILNET_LOCK_KB_URL) },
explainerText(), style = MaterialTheme.typography.bodyMedium)
onClick = { handler.openUri(Links.TAILNET_LOCK_KB_URL) }, }
style = MaterialTheme.typography.bodyMedium)
}
})
} }
@Composable @Composable

@ -47,6 +47,7 @@
<string name="about_tailscale">About Tailscale</string> <string name="about_tailscale">About Tailscale</string>
<string name="bug_report">Bug report</string> <string name="bug_report">Bug report</string>
<string name="use_ts_dns">Use Tailscale DNS</string> <string name="use_ts_dns">Use Tailscale DNS</string>
<string name="internal_debug_options">Internal debug options</string>
<!-- Strings for the main screen --> <!-- Strings for the main screen -->
<string name="exit_node">EXIT NODE</string> <string name="exit_node">EXIT NODE</string>

Loading…
Cancel
Save