From 11f52ad96bf52e87f0ec0d31c000aa51af7fcd17 Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Thu, 11 Apr 2024 13:59:23 -0400 Subject: [PATCH] android/ui: add state subheadings to settings rows (#311) Fixes tailscale/corp#19044 Add version, dns state, and tailnet lock status as settings option subtitles. Signed-off-by: Jonathan Nobels --- .../com/tailscale/ipn/ui/view/SettingsView.kt | 35 ++++++++++++++++--- .../ipn/ui/viewModel/SettingsViewModel.kt | 24 ++++++++++++- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt index 9b4f7a1..7bde4c1 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt @@ -41,6 +41,8 @@ fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewMo val user = viewModel.loggedInUser.collectAsState().value val isAdmin = viewModel.isAdmin.collectAsState().value val managedByOrganization = viewModel.managedByOrganization.collectAsState().value + val tailnetLockEnabled = viewModel.tailNetLockEnabled.collectAsState().value + val corpDNSEnabled = viewModel.corpDNSEnabled.collectAsState().value Scaffold( topBar = { @@ -58,10 +60,23 @@ fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewMo } Lists.SectionDivider() - Setting.Text(R.string.dns_settings, onClick = settingsNav.onNavigateToDNSSettings) + Setting.Text( + R.string.dns_settings, + subtitle = + corpDNSEnabled?.let { + stringResource( + if (it) R.string.using_tailscale_dns else R.string.not_using_tailscale_dns) + }, + onClick = settingsNav.onNavigateToDNSSettings) Lists.ItemDivider() - Setting.Text(R.string.tailnet_lock, onClick = settingsNav.onNavigateToTailnetLock) + Setting.Text( + R.string.tailnet_lock, + subtitle = + tailnetLockEnabled?.let { + stringResource(if (it) R.string.enabled else R.string.disabled) + }, + onClick = settingsNav.onNavigateToTailnetLock) Lists.ItemDivider() Setting.Text(R.string.permissions, onClick = settingsNav.onNavigateToPermissions) @@ -77,7 +92,10 @@ fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewMo Setting.Text(R.string.bug_report, onClick = settingsNav.onNavigateToBugReport) Lists.ItemDivider() - Setting.Text(R.string.about_tailscale, onClick = settingsNav.onNavigateToAbout) + Setting.Text( + R.string.about_tailscale, + subtitle = "${stringResource(id = R.string.version)} ${BuildConfig.VERSION_NAME}", + onClick = settingsNav.onNavigateToAbout) // TODO: put a heading for the debug section if (BuildConfig.DEBUG) { @@ -93,6 +111,7 @@ object Setting { fun Text( titleRes: Int = 0, title: String? = null, + subtitle: String? = null, destructive: Boolean = false, enabled: Boolean = true, onClick: (() -> Unit)? = null @@ -110,7 +129,15 @@ object Setting { style = MaterialTheme.typography.bodyMedium, color = if (destructive) MaterialTheme.colorScheme.error else Color.Unspecified) }, - ) + supportingContent = + subtitle?.let { + { + Text( + it, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant) + } + }) } @Composable diff --git a/android/src/main/java/com/tailscale/ipn/ui/viewModel/SettingsViewModel.kt b/android/src/main/java/com/tailscale/ipn/ui/viewModel/SettingsViewModel.kt index fc2bec0..fbdcec0 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/viewModel/SettingsViewModel.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/viewModel/SettingsViewModel.kt @@ -5,7 +5,9 @@ package com.tailscale.ipn.ui.viewModel import androidx.lifecycle.viewModelScope import com.tailscale.ipn.mdm.MDMSettings +import com.tailscale.ipn.ui.localapi.Client import com.tailscale.ipn.ui.notifier.Notifier +import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.util.set import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -24,14 +26,34 @@ data class SettingsNav( val onBackToSettings: () -> Unit, ) -class SettingsViewModel() : IpnViewModel() { +class SettingsViewModel : IpnViewModel() { // Display name for the logged in user val isAdmin: StateFlow = MutableStateFlow(false) val managedByOrganization = MDMSettings.managedByOrganizationName.flow + // True if tailnet lock is enabled. nil if not yet known. + val tailNetLockEnabled: StateFlow = MutableStateFlow(null) + // True if tailscaleDNS is enabled. nil if not yet known. + val corpDNSEnabled: StateFlow = MutableStateFlow(null) init { viewModelScope.launch { Notifier.netmap.collect { netmap -> isAdmin.set(netmap?.SelfNode?.isAdmin ?: false) } } + + Client(viewModelScope).tailnetLockStatus { result -> + result.onSuccess { status -> tailNetLockEnabled.set(status.Enabled) } + + LoadingIndicator.stop() + } + + viewModelScope.launch { + Notifier.prefs.collect { + it?.let { + corpDNSEnabled.set(it.CorpDNS) + } ?: run { + corpDNSEnabled.set(null) + } + } + } } }