From d676dca4f4d7632b979c8a8c8170d942c28c4f30 Mon Sep 17 00:00:00 2001 From: Percy Wegmann Date: Mon, 8 Apr 2024 12:25:04 -0500 Subject: [PATCH] android/ui: navigation improvements 1. More careful back navigation to avoid navigating to blank screen 2. After adding an account, navigate back to main view Updates tailscale/corp#18202 Signed-off-by: Percy Wegmann --- .../java/com/tailscale/ipn/MainActivity.kt | 61 ++++++----- .../com/tailscale/ipn/ui/view/AboutView.kt | 5 +- .../tailscale/ipn/ui/view/BugReportView.kt | 5 +- .../tailscale/ipn/ui/view/DNSSettingsView.kt | 4 +- .../tailscale/ipn/ui/view/ExitNodePicker.kt | 2 +- .../ipn/ui/view/MDMSettingsDebugView.kt | 6 +- .../tailscale/ipn/ui/view/ManagedByView.kt | 4 +- .../ipn/ui/view/MullvadExitNodePicker.kt | 2 +- .../ipn/ui/view/MullvadExitNodePickerList.kt | 103 +++++++++--------- .../com/tailscale/ipn/ui/view/PeerDetails.kt | 4 +- .../tailscale/ipn/ui/view/PermissionsView.kt | 6 +- .../tailscale/ipn/ui/view/RunExitNodeView.kt | 2 +- .../com/tailscale/ipn/ui/view/SettingsView.kt | 6 +- .../com/tailscale/ipn/ui/view/SharedViews.kt | 4 +- .../ipn/ui/view/TailnetLockSetupView.kt | 4 +- .../tailscale/ipn/ui/view/UserSwitcherView.kt | 4 +- .../ui/viewModel/ExitNodePickerViewModel.kt | 8 +- .../ipn/ui/viewModel/SettingsViewModel.kt | 3 +- 18 files changed, 122 insertions(+), 111 deletions(-) diff --git a/android/src/main/java/com/tailscale/ipn/MainActivity.kt b/android/src/main/java/com/tailscale/ipn/MainActivity.kt index aa5d738..2b9fbb5 100644 --- a/android/src/main/java/com/tailscale/ipn/MainActivity.kt +++ b/android/src/main/java/com/tailscale/ipn/MainActivity.kt @@ -3,6 +3,7 @@ package com.tailscale.ipn +import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.Intent @@ -30,6 +31,7 @@ import androidx.compose.ui.Modifier import androidx.core.net.toUri import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.lifecycleScope +import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -45,7 +47,6 @@ import com.tailscale.ipn.ui.util.AndroidTVUtil import com.tailscale.ipn.ui.util.set import com.tailscale.ipn.ui.util.universalFit import com.tailscale.ipn.ui.view.AboutView -import com.tailscale.ipn.ui.view.BackNavigation import com.tailscale.ipn.ui.view.BugReportView import com.tailscale.ipn.ui.view.DNSSettingsView import com.tailscale.ipn.ui.view.ExitNodePicker @@ -73,14 +74,12 @@ import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { private lateinit var requestVpnPermission: ActivityResultLauncher + private lateinit var navController: NavHostController companion object { - // Request codes for Android callbacks. - // requestPrepareVPN is for when Android's VpnService.prepare completes. - @JvmStatic val requestPrepareVPN: Int = 1001 - - const val WRITE_STORAGE_RESULT = 1000 private const val TAG = "Main Activity" + + private const val START_AT_ROOT = "startAtRoot" } private fun Context.isLandscapeCapable(): Boolean { @@ -93,6 +92,7 @@ class MainActivity : ComponentActivity() { // simply opening the URL. This should be consumed once it has been handled. private val loginQRCode: StateFlow = MutableStateFlow(null) + @SuppressLint("SourceLockedOrientationActivity") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -124,6 +124,10 @@ class MainActivity : ComponentActivity() { popExitTransition = { slideOutHorizontally(animationSpec = tween(150), targetOffsetX = { it }) }) { + fun backTo(route: String): () -> Unit = { + navController.popBackStack(route = route, inclusive = false) + } + val mainViewNav = MainViewNavigation( onNavigateToSettings = { navController.navigate("settings") }, @@ -143,19 +147,17 @@ class MainActivity : ComponentActivity() { onNavigateToManagedBy = { navController.navigate("managedBy") }, onNavigateToUserSwitcher = { navController.navigate("userSwitcher") }, onNavigateToPermissions = { navController.navigate("permissions") }, - onBackPressed = { navController.popBackStack() }, - ) - - val backNav = BackNavigation(onBack = { navController.popBackStack() }) + onBackToSettings = backTo("settings"), + onNavigateBackHome = backTo("main")) val exitNodePickerNav = ExitNodePickerNav( - onNavigateHome = { + onNavigateBackHome = { navController.popBackStack(route = "main", inclusive = false) }, - onNavigateBack = { navController.popBackStack() }, - onNavigateToExitNodePicker = { navController.popBackStack() }, + onNavigateBackToExitNodes = backTo("exitNodes"), onNavigateToMullvad = { navController.navigate("mullvad") }, + onNavigateBackToMullvad = backTo("mullvad"), onNavigateToMullvadCountry = { navController.navigate("mullvad/$it") }, onNavigateToRunAsExitNode = { navController.navigate("runExitNode") }) @@ -176,26 +178,21 @@ class MainActivity : ComponentActivity() { composable( "peerDetails/{nodeId}", arguments = listOf(navArgument("nodeId") { type = NavType.StringType })) { - PeerDetails(nav = backNav, it.arguments?.getString("nodeId") ?: "") + PeerDetails(backTo("main"), it.arguments?.getString("nodeId") ?: "") } - composable("bugReport") { BugReportView(nav = backNav) } - composable("dnsSettings") { DNSSettingsView(nav = backNav) } - composable("tailnetLock") { TailnetLockSetupView(nav = backNav) } - composable("about") { AboutView(nav = backNav) } - composable("mdmSettings") { MDMSettingsDebugView(nav = backNav) } - composable("managedBy") { ManagedByView(nav = backNav) } + composable("bugReport") { BugReportView(backTo("settings")) } + composable("dnsSettings") { DNSSettingsView(backTo("settings")) } + composable("tailnetLock") { TailnetLockSetupView(backTo("settings")) } + composable("about") { AboutView(backTo("settings")) } + composable("mdmSettings") { MDMSettingsDebugView(backTo("settings")) } + composable("managedBy") { ManagedByView(backTo("settings")) } composable("userSwitcher") { - UserSwitcherView( - nav = backNav, - onNavigateHome = { - navController.popBackStack(route = "main", inclusive = false) - }) + UserSwitcherView(backTo("settings"), backTo("main")) } composable("permissions") { - PermissionsView( - nav = backNav, openApplicationSettings = ::openApplicationSettings) + PermissionsView(backTo("settings"), ::openApplicationSettings) } - composable("intro") { IntroView { navController.popBackStack() } } + composable("intro") { IntroView(backTo("main")) } } // Show the intro screen one time @@ -245,6 +242,13 @@ class MainActivity : ComponentActivity() { return AndroidTVUtil.isAndroidTV() } + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + if (intent?.getBooleanExtra(START_AT_ROOT, false) == true) { + navController.popBackStack(route = "main", inclusive = false) + } + } + private fun login(urlString: String) { // Launch coroutine to listen for state changes. When the user completes login, relaunch // MainActivity to bring the app back to focus. @@ -257,6 +261,7 @@ class MainActivity : ComponentActivity() { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) action = Intent.ACTION_MAIN addCategory(Intent.CATEGORY_LAUNCHER) + putExtra(START_AT_ROOT, true) } startActivity(intent) diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/AboutView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/AboutView.kt index 7b3d65d..74c0945 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/AboutView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/AboutView.kt @@ -30,8 +30,9 @@ import com.tailscale.ipn.R import com.tailscale.ipn.ui.Links @Composable -fun AboutView(nav: BackNavigation) { - Scaffold(topBar = { Header(R.string.about_view_title, onBack = nav.onBack) }) { innerPadding -> +fun AboutView(backToSettings: BackNavigation) { + Scaffold(topBar = { Header(R.string.about_view_title, onBack = backToSettings) }) { innerPadding + -> Column( verticalArrangement = Arrangement.spacedBy(space = 20.dp, alignment = Alignment.CenterVertically), diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/BugReportView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/BugReportView.kt index 46da2b2..8f8a2d3 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/BugReportView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/BugReportView.kt @@ -29,11 +29,12 @@ import com.tailscale.ipn.ui.util.ClipboardValueView import com.tailscale.ipn.ui.viewModel.BugReportViewModel @Composable -fun BugReportView(nav: BackNavigation, model: BugReportViewModel = viewModel()) { +fun BugReportView(backToSettings: BackNavigation, model: BugReportViewModel = viewModel()) { val handler = LocalUriHandler.current val bugReportID = model.bugReportID.collectAsState().value - Scaffold(topBar = { Header(R.string.bug_report_title, onBack = nav.onBack) }) { innerPadding -> + Scaffold(topBar = { Header(R.string.bug_report_title, onBack = backToSettings) }) { innerPadding + -> Column(modifier = Modifier.padding(innerPadding).fillMaxWidth().fillMaxHeight()) { ListItem( headlineContent = { diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/DNSSettingsView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/DNSSettingsView.kt index 1276517..89febab 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/DNSSettingsView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/DNSSettingsView.kt @@ -35,7 +35,7 @@ data class ViewableRoute(val name: String, val resolvers: List @OptIn(ExperimentalFoundationApi::class) @Composable fun DNSSettingsView( - nav: BackNavigation, + backToSettings: BackNavigation, model: DNSSettingsViewModel = viewModel(factory = DNSSettingsViewModelFactory()) ) { val state: DNSEnablementState = model.enablementState.collectAsState().value @@ -47,7 +47,7 @@ fun DNSSettingsView( } ?: emptyList() val useCorpDNS = Notifier.prefs.collectAsState().value?.CorpDNS == true - Scaffold(topBar = { Header(R.string.dns_settings, onBack = nav.onBack) }) { innerPadding -> + Scaffold(topBar = { Header(R.string.dns_settings, onBack = backToSettings) }) { innerPadding -> LoadingIndicator.Wrap { LazyColumn(Modifier.padding(innerPadding)) { item("state") { diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt b/android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt index 8217dd4..034f5e9 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/ExitNodePicker.kt @@ -38,7 +38,7 @@ fun ExitNodePicker( model: ExitNodePickerViewModel = viewModel(factory = ExitNodePickerViewModelFactory(nav)) ) { LoadingIndicator.Wrap { - Scaffold(topBar = { Header(R.string.choose_exit_node, onBack = nav.onNavigateBack) }) { + Scaffold(topBar = { Header(R.string.choose_exit_node, onBack = nav.onNavigateBackHome) }) { innerPadding -> val tailnetExitNodes = model.tailnetExitNodes.collectAsState().value val mullvadExitNodesByCountryCode = model.mullvadExitNodesByCountryCode.collectAsState().value diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/MDMSettingsDebugView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/MDMSettingsDebugView.kt index c3b91c9..4de3097 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/MDMSettingsDebugView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/MDMSettingsDebugView.kt @@ -24,9 +24,9 @@ import com.tailscale.ipn.ui.viewModel.IpnViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable -fun MDMSettingsDebugView(nav: BackNavigation, model: IpnViewModel = viewModel()) { - Scaffold(topBar = { Header(R.string.current_mdm_settings, onBack = nav.onBack) }) { innerPadding - -> +fun MDMSettingsDebugView(backToSettings: BackNavigation, model: IpnViewModel = viewModel()) { + Scaffold(topBar = { Header(R.string.current_mdm_settings, onBack = backToSettings) }) { + innerPadding -> LazyColumn(modifier = Modifier.padding(innerPadding)) { itemsWithDividers(MDMSettings.allSettings.sortedBy { "${it::class.java.name}|${it.key}" }) { setting -> diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/ManagedByView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/ManagedByView.kt index e8e0def..7e3d3af 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/ManagedByView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/ManagedByView.kt @@ -21,8 +21,8 @@ import com.tailscale.ipn.mdm.MDMSettings import com.tailscale.ipn.ui.viewModel.IpnViewModel @Composable -fun ManagedByView(nav: BackNavigation, model: IpnViewModel = viewModel()) { - Scaffold(topBar = { Header(R.string.managed_by, onBack = nav.onBack) }) { innerPadding -> +fun ManagedByView(backToSettings: BackNavigation, model: IpnViewModel = viewModel()) { + Scaffold(topBar = { Header(R.string.managed_by, onBack = backToSettings) }) { innerPadding -> Column( verticalArrangement = Arrangement.spacedBy(space = 20.dp, alignment = Alignment.CenterVertically), diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/MullvadExitNodePicker.kt b/android/src/main/java/com/tailscale/ipn/ui/view/MullvadExitNodePicker.kt index 34e004f..75458ab 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/MullvadExitNodePicker.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/MullvadExitNodePicker.kt @@ -40,7 +40,7 @@ fun MullvadExitNodePicker( topBar = { Header( title = { Text("${countryCode.flag()} ${any.country}") }, - onBack = nav.onNavigateBack) + onBack = nav.onNavigateBackToExitNodes) }) { innerPadding -> LazyColumn(modifier = Modifier.padding(innerPadding)) { if (nodes.size > 1) { diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/MullvadExitNodePickerList.kt b/android/src/main/java/com/tailscale/ipn/ui/view/MullvadExitNodePickerList.kt index 20e7f5c..6bc8404 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/MullvadExitNodePickerList.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/MullvadExitNodePickerList.kt @@ -36,60 +36,63 @@ fun MullvadExitNodePickerList( model: ExitNodePickerViewModel = viewModel(factory = ExitNodePickerViewModelFactory(nav)) ) { LoadingIndicator.Wrap { - Scaffold(topBar = { Header(R.string.choose_mullvad_exit_node, onBack = nav.onNavigateBack) }) { - innerPadding -> - val mullvadExitNodes = model.mullvadExitNodesByCountryCode.collectAsState() + Scaffold( + topBar = { + Header(R.string.choose_mullvad_exit_node, onBack = nav.onNavigateBackToMullvad) + }) { innerPadding -> + val mullvadExitNodes = model.mullvadExitNodesByCountryCode.collectAsState() - LazyColumn(modifier = Modifier.padding(innerPadding)) { - val sortedCountries = - mullvadExitNodes.value.entries.toList().sortedBy { - it.value.first().country.lowercase() - } - itemsWithDividers(sortedCountries) { (countryCode, nodes) -> - val first = nodes.first() + LazyColumn(modifier = Modifier.padding(innerPadding)) { + val sortedCountries = + mullvadExitNodes.value.entries.toList().sortedBy { + it.value.first().country.lowercase() + } + itemsWithDividers(sortedCountries) { (countryCode, nodes) -> + val first = nodes.first() - // TODO(oxtoacart): the modifier on the ListItem occasionally causes a crash - // with java.lang.ClassCastException: androidx.compose.ui.ComposedModifier cannot be cast - // to androidx.compose.runtime.RecomposeScopeImpl - // Wrapping it in a Box eliminates this. It appears to be some kind of - // interaction between the LazyList and the modifier. - Box { - ListItem( - modifier = - Modifier.clickable { - if (nodes.size > 1) { - nav.onNavigateToMullvadCountry(countryCode) - } else { - model.setExitNode(first) - } + // TODO(oxtoacart): the modifier on the ListItem occasionally causes a crash + // with java.lang.ClassCastException: androidx.compose.ui.ComposedModifier cannot be + // cast + // to androidx.compose.runtime.RecomposeScopeImpl + // Wrapping it in a Box eliminates this. It appears to be some kind of + // interaction between the LazyList and the modifier. + Box { + ListItem( + modifier = + Modifier.clickable { + if (nodes.size > 1) { + nav.onNavigateToMullvadCountry(countryCode) + } else { + model.setExitNode(first) + } + }, + leadingContent = { + Text( + countryCode.flag(), + style = MaterialTheme.typography.titleLarge, + ) + }, + headlineContent = { + Text(first.country, style = MaterialTheme.typography.bodyMedium) }, - leadingContent = { - Text( - countryCode.flag(), - style = MaterialTheme.typography.titleLarge, - ) - }, - headlineContent = { - Text(first.country, style = MaterialTheme.typography.bodyMedium) - }, - supportingContent = { - Text( - if (nodes.size == 1) first.city - else "${nodes.size} ${stringResource(R.string.cities_available)}", - style = MaterialTheme.typography.bodyMedium) - }, - trailingContent = { - if (nodes.size > 1 && nodes.selected || first.selected) { - if (nodes.selected) { - Icon( - Icons.Outlined.Check, - contentDescription = stringResource(R.string.selected)) - } - } - }) + supportingContent = { + Text( + if (nodes.size == 1) first.city + else "${nodes.size} ${stringResource(R.string.cities_available)}", + style = MaterialTheme.typography.bodyMedium) + }, + trailingContent = { + if (nodes.size > 1 && nodes.selected || first.selected) { + if (nodes.selected) { + Icon( + Icons.Outlined.Check, + contentDescription = stringResource(R.string.selected)) + } + } + }) + } + } } } - } - } } } diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/PeerDetails.kt b/android/src/main/java/com/tailscale/ipn/ui/view/PeerDetails.kt index 22bb698..09fd45d 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/PeerDetails.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/PeerDetails.kt @@ -43,7 +43,7 @@ import com.tailscale.ipn.ui.viewModel.PeerDetailsViewModelFactory @OptIn(ExperimentalMaterial3Api::class) @Composable fun PeerDetails( - nav: BackNavigation, + backToHome: BackNavigation, nodeId: String, model: PeerDetailsViewModel = viewModel(factory = PeerDetailsViewModelFactory(nodeId, LocalContext.current.filesDir)) @@ -74,7 +74,7 @@ fun PeerDetails( } } }, - onBack = { nav.onBack() }) + onBack = backToHome) }, ) { innerPadding -> LazyColumn( diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/PermissionsView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/PermissionsView.kt index 37e8667..b033f24 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/PermissionsView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/PermissionsView.kt @@ -25,10 +25,10 @@ import com.tailscale.ipn.ui.util.itemsWithDividers @OptIn(ExperimentalPermissionsApi::class) @Composable -fun PermissionsView(nav: BackNavigation, openApplicationSettings: () -> Unit) { +fun PermissionsView(backToSettings: BackNavigation, openApplicationSettings: () -> Unit) { val permissions = Permissions.withGrantedStatus - Scaffold(topBar = { Header(titleRes = R.string.permissions, onBack = nav.onBack) }) { innerPadding - -> + Scaffold(topBar = { Header(titleRes = R.string.permissions, onBack = backToSettings) }) { + innerPadding -> LazyColumn(modifier = Modifier.padding(innerPadding)) { itemsWithDividers(permissions) { (permission, granted) -> ListItem( diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/RunExitNodeView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/RunExitNodeView.kt index df512a1..f499090 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/RunExitNodeView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/RunExitNodeView.kt @@ -40,7 +40,7 @@ fun RunExitNodeView( val isRunningExitNode = model.isRunningExitNode.collectAsState().value Scaffold( - topBar = { Header(R.string.run_as_exit_node, onBack = nav.onNavigateToExitNodePicker) }) { + topBar = { Header(R.string.run_as_exit_node, onBack = nav.onNavigateBackToExitNodes) }) { innerPadding -> LoadingIndicator.Wrap { Column( 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 6d6259a..9b4f7a1 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 @@ -4,6 +4,7 @@ package com.tailscale.ipn.ui.view import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -41,8 +42,9 @@ fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewMo val isAdmin = viewModel.isAdmin.collectAsState().value val managedByOrganization = viewModel.managedByOrganization.collectAsState().value - Scaffold(topBar = { - Header(titleRes = R.string.settings_title, onBack = settingsNav.onBackPressed) + Scaffold( + topBar = { + Header(titleRes = R.string.settings_title, onBack = settingsNav.onNavigateBackHome) }) { innerPadding -> Column(modifier = Modifier.padding(innerPadding).verticalScroll(rememberScrollState())) { UserView( diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/SharedViews.kt b/android/src/main/java/com/tailscale/ipn/ui/view/SharedViews.kt index 019b541..46ce7f1 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/SharedViews.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/SharedViews.kt @@ -29,9 +29,7 @@ import androidx.compose.ui.unit.dp import com.tailscale.ipn.ui.theme.topAppBar import com.tailscale.ipn.ui.theme.ts_color_light_blue -data class BackNavigation( - val onBack: () -> Unit, -) +typealias BackNavigation = () -> Unit // Header view for all secondary screens // @see TopAppBar actions for additional actions (usually a row of icons) diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/TailnetLockSetupView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/TailnetLockSetupView.kt index 6c06165..374d164 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/TailnetLockSetupView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/TailnetLockSetupView.kt @@ -37,14 +37,14 @@ import com.tailscale.ipn.ui.viewModel.TailnetLockSetupViewModelFactory @Composable fun TailnetLockSetupView( - nav: BackNavigation, + backToSettings: BackNavigation, model: TailnetLockSetupViewModel = viewModel(factory = TailnetLockSetupViewModelFactory()) ) { val statusItems = model.statusItems.collectAsState().value val nodeKey = model.nodeKey.collectAsState().value val tailnetLockKey = model.tailnetLockKey.collectAsState().value - Scaffold(topBar = { Header(R.string.tailnet_lock, onBack = nav.onBack) }) { innerPadding -> + Scaffold(topBar = { Header(R.string.tailnet_lock, onBack = backToSettings) }) { innerPadding -> LoadingIndicator.Wrap { LazyColumn(modifier = Modifier.padding(innerPadding)) { item(key = "header") { ExplainerView() } 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 02b91cc..e9f3f11 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 @@ -43,7 +43,7 @@ import com.tailscale.ipn.ui.viewModel.UserSwitcherViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun UserSwitcherView( - nav: BackNavigation, + backToSettings: BackNavigation, onNavigateHome: () -> Unit, viewModel: UserSwitcherViewModel = viewModel() ) { @@ -56,7 +56,7 @@ fun UserSwitcherView( topBar = { Header( R.string.accounts, - onBack = nav.onBack, + onBack = backToSettings, actions = { Row { FusMenu(viewModel = viewModel) diff --git a/android/src/main/java/com/tailscale/ipn/ui/viewModel/ExitNodePickerViewModel.kt b/android/src/main/java/com/tailscale/ipn/ui/viewModel/ExitNodePickerViewModel.kt index 9990f02..2f400de 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/viewModel/ExitNodePickerViewModel.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/viewModel/ExitNodePickerViewModel.kt @@ -20,10 +20,10 @@ import kotlinx.coroutines.launch import java.util.TreeMap data class ExitNodePickerNav( - val onNavigateHome: () -> Unit, - val onNavigateBack: () -> Unit, - val onNavigateToExitNodePicker: () -> Unit, + val onNavigateBackHome: () -> Unit, + val onNavigateBackToExitNodes: () -> Unit, val onNavigateToMullvad: () -> Unit, + val onNavigateBackToMullvad: () -> Unit, val onNavigateToMullvadCountry: (String) -> Unit, val onNavigateToRunAsExitNode: () -> Unit, ) @@ -138,7 +138,7 @@ class ExitNodePickerViewModel(private val nav: ExitNodePickerNav) : IpnViewModel val prefsOut = Ipn.MaskedPrefs() prefsOut.ExitNodeID = node.id Client(viewModelScope).editPrefs(prefsOut) { - nav.onNavigateHome() + nav.onNavigateBackHome() LoadingIndicator.stop() } } 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 afc59ed..fc2bec0 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 @@ -20,7 +20,8 @@ data class SettingsNav( val onNavigateToManagedBy: () -> Unit, val onNavigateToUserSwitcher: () -> Unit, val onNavigateToPermissions: () -> Unit, - val onBackPressed: () -> Unit, + val onNavigateBackHome: () -> Unit, + val onBackToSettings: () -> Unit, ) class SettingsViewModel() : IpnViewModel() {