fix: nav crash bug on scratch config

This commit is contained in:
Zane Schepke 2024-09-22 17:57:13 -04:00
parent 57d096ebb1
commit 791b532d85
11 changed files with 61 additions and 48 deletions

View File

@ -44,6 +44,7 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavItem import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavItem
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.isCurrentRoute
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.CustomSnackBar import com.zaneschepke.wireguardautotunnel.ui.common.prompt.CustomSnackBar
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarControllerProvider import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarControllerProvider
import com.zaneschepke.wireguardautotunnel.ui.screens.config.ConfigScreen import com.zaneschepke.wireguardautotunnel.ui.screens.config.ConfigScreen
@ -115,9 +116,10 @@ class MainActivity : AppCompatActivity() {
Modifier Modifier
.focusable() .focusable()
.focusProperties { .focusProperties {
when (navBackStackEntry?.toRoute<Screens>()) { if (navBackStackEntry?.isCurrentRoute(Route.Lock) == true) {
is Screens.Lock -> Unit Unit
else -> up = focusRequester } else {
up = focusRequester
} }
}, },
bottomBar = { bottomBar = {
@ -126,17 +128,17 @@ class MainActivity : AppCompatActivity() {
listOf( listOf(
BottomNavItem( BottomNavItem(
name = stringResource(R.string.tunnels), name = stringResource(R.string.tunnels),
route = Screens.Main, route = Route.Main,
icon = Icons.Rounded.Home, icon = Icons.Rounded.Home,
), ),
BottomNavItem( BottomNavItem(
name = stringResource(R.string.settings), name = stringResource(R.string.settings),
route = Screens.Settings, route = Route.Settings,
icon = Icons.Rounded.Settings, icon = Icons.Rounded.Settings,
), ),
BottomNavItem( BottomNavItem(
name = stringResource(R.string.support), name = stringResource(R.string.support),
route = Screens.Support, route = Route.Support,
icon = Icons.Rounded.QuestionMark, icon = Icons.Rounded.QuestionMark,
), ),
), ),
@ -148,16 +150,16 @@ class MainActivity : AppCompatActivity() {
navController, navController,
enterTransition = { fadeIn(tween(Constants.TRANSITION_ANIMATION_TIME)) }, enterTransition = { fadeIn(tween(Constants.TRANSITION_ANIMATION_TIME)) },
exitTransition = { fadeOut(tween(Constants.TRANSITION_ANIMATION_TIME)) }, exitTransition = { fadeOut(tween(Constants.TRANSITION_ANIMATION_TIME)) },
startDestination = (if (isPinLockEnabled == true) Screens.Lock else Screens.Main), startDestination = (if (isPinLockEnabled == true) Route.Lock else Route.Main),
) { ) {
composable<Screens.Main> { composable<Route.Main> {
MainScreen( MainScreen(
focusRequester = focusRequester, focusRequester = focusRequester,
uiState = appUiState, uiState = appUiState,
navController = navController, navController = navController,
) )
} }
composable<Screens.Settings> { composable<Route.Settings> {
SettingsScreen( SettingsScreen(
appViewModel = appViewModel, appViewModel = appViewModel,
uiState = appUiState, uiState = appUiState,
@ -165,25 +167,25 @@ class MainActivity : AppCompatActivity() {
focusRequester = focusRequester, focusRequester = focusRequester,
) )
} }
composable<Screens.Support> { composable<Route.Support> {
SupportScreen( SupportScreen(
focusRequester = focusRequester, focusRequester = focusRequester,
navController = navController, navController = navController,
appUiState = appUiState, appUiState = appUiState,
) )
} }
composable<Screens.Logs> { composable<Route.Logs> {
LogsScreen() LogsScreen()
} }
composable<Screens.Config> { composable<Route.Config> {
val args = it.toRoute<Screens.Config>() val args = it.toRoute<Route.Config>()
ConfigScreen( ConfigScreen(
focusRequester = focusRequester, focusRequester = focusRequester,
tunnelId = args.id, tunnelId = args.id,
) )
} }
composable<Screens.Option> { composable<Route.Option> {
val args = it.toRoute<Screens.Option>() val args = it.toRoute<Route.Option>()
OptionsScreen( OptionsScreen(
navController = navController, navController = navController,
tunnelId = args.id, tunnelId = args.id,
@ -191,7 +193,7 @@ class MainActivity : AppCompatActivity() {
appUiState = appUiState, appUiState = appUiState,
) )
} }
composable<Screens.Lock> { composable<Route.Lock> {
PinLockScreen( PinLockScreen(
navController = navController, navController = navController,
appViewModel = appViewModel, appViewModel = appViewModel,

View File

@ -2,29 +2,29 @@ package com.zaneschepke.wireguardautotunnel.ui
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
sealed class Screens { sealed class Route {
@Serializable @Serializable
data object Support : Screens() data object Support : Route()
@Serializable @Serializable
data object Settings : Screens() data object Settings : Route()
@Serializable @Serializable
data object Main : Screens() data object Main : Route()
@Serializable @Serializable
data class Option( data class Option(
val id: Int, val id: Int,
) : Screens() ) : Route()
@Serializable @Serializable
data object Lock : Screens() data object Lock : Route()
@Serializable @Serializable
data class Config( data class Config(
val id: Int, val id: Int,
) : Screens() ) : Route()
@Serializable @Serializable
data object Logs : Screens() data object Logs : Route()
} }

View File

@ -33,9 +33,7 @@ fun BottomNavBar(navController: NavController, bottomNavItems: List<BottomNavIte
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
) { ) {
bottomNavItems.forEach { item -> bottomNavItems.forEach { item ->
val selected = navBackStackEntry?.destination?.hierarchy?.any { val selected = navBackStackEntry.isCurrentRoute(item.route)
it.hasRoute(route = item.route::class)
} == true
NavigationBarItem( NavigationBarItem(
selected = selected, selected = selected,
onClick = { onClick = {

View File

@ -1,10 +1,10 @@
package com.zaneschepke.wireguardautotunnel.ui.common.navigation package com.zaneschepke.wireguardautotunnel.ui.common.navigation
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import com.zaneschepke.wireguardautotunnel.ui.Screens import com.zaneschepke.wireguardautotunnel.ui.Route
data class BottomNavItem( data class BottomNavItem(
val name: String, val name: String,
val route: Screens, val route: Route,
val icon: ImageVector, val icon: ImageVector,
) )

View File

@ -0,0 +1,12 @@
package com.zaneschepke.wireguardautotunnel.ui.common.navigation
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import com.zaneschepke.wireguardautotunnel.ui.Route
fun NavBackStackEntry?.isCurrentRoute(route: Route): Boolean {
return this?.destination?.hierarchy?.any {
it.hasRoute(route = route::class)
} == true
}

View File

@ -17,7 +17,7 @@ import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
import com.zaneschepke.wireguardautotunnel.ui.Screens import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
import com.zaneschepke.wireguardautotunnel.ui.screens.config.model.PeerProxy import com.zaneschepke.wireguardautotunnel.ui.screens.config.model.PeerProxy
import com.zaneschepke.wireguardautotunnel.util.Constants import com.zaneschepke.wireguardautotunnel.util.Constants
@ -335,14 +335,15 @@ constructor(
SnackbarController.showMessage( SnackbarController.showMessage(
StringValue.StringResource(R.string.config_changes_saved), StringValue.StringResource(R.string.config_changes_saved),
) )
navController.navigate(Screens.Main) navController.navigate(Route.Main)
}.onFailure { }.onFailure {
Timber.e(it) Timber.e(it)
val message = it.message?.substringAfter(":", missingDelimiterValue = "") val message = it.message?.substringAfter(":", missingDelimiterValue = "")
val stringValue = val stringValue = if (message.isNullOrBlank()) {
message?.let { StringValue.StringResource(R.string.unknown_error)
} else {
StringValue.DynamicString(message) StringValue.DynamicString(message)
} ?: StringValue.StringResource(R.string.unknown_error) }
SnackbarController.showMessage(stringValue) SnackbarController.showMessage(stringValue)
} }
} }

View File

@ -68,7 +68,7 @@ import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.ui.AppUiState import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.Screens import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.RowListItem import com.zaneschepke.wireguardautotunnel.ui.common.RowListItem
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberFileImportLauncherForResult import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberFileImportLauncherForResult
@ -235,7 +235,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
onQrClick = { launchQrScanner() }, onQrClick = { launchQrScanner() },
onManualImportClick = { onManualImportClick = {
navController.navigate( navController.navigate(
Screens.Config(Constants.MANUAL_TUNNEL_CONFIG_ID), Route.Config(Constants.MANUAL_TUNNEL_CONFIG_ID),
) )
}, },
) )
@ -409,7 +409,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
onClick = { onClick = {
selectedTunnel?.let { selectedTunnel?.let {
navController.navigate( navController.navigate(
Screens.Option(it.id), Route.Option(it.id),
) )
} }
}, },
@ -456,7 +456,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
selectedTunnel = tunnel selectedTunnel = tunnel
selectedTunnel?.let { selectedTunnel?.let {
navController.navigate( navController.navigate(
Screens.Option(it.id), Route.Option(it.id),
) )
} }
}, },

View File

@ -50,7 +50,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.AppUiState import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.Screens import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton
import com.zaneschepke.wireguardautotunnel.ui.common.config.ConfigurationToggle import com.zaneschepke.wireguardautotunnel.ui.common.config.ConfigurationToggle
import com.zaneschepke.wireguardautotunnel.ui.common.config.SubmitConfigurationTextBox import com.zaneschepke.wireguardautotunnel.ui.common.config.SubmitConfigurationTextBox
@ -114,7 +114,7 @@ fun OptionsScreen(
) )
}, focusRequester, isVisible = true, onClick = { }, focusRequester, isVisible = true, onClick = {
navController.navigate( navController.navigate(
Screens.Config(config.id), Route.Config(config.id),
) )
}) })
}, },

View File

@ -8,7 +8,7 @@ import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController import androidx.navigation.NavController
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.AppViewModel import com.zaneschepke.wireguardautotunnel.ui.AppViewModel
import com.zaneschepke.wireguardautotunnel.ui.Screens import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
import com.zaneschepke.wireguardautotunnel.util.StringValue import com.zaneschepke.wireguardautotunnel.util.StringValue
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
@ -36,11 +36,11 @@ fun PinLockScreen(navController: NavController, appViewModel: AppViewModel) {
onPinCorrect = { onPinCorrect = {
// pin is correct, navigate or hide pin lock // pin is correct, navigate or hide pin lock
if (context.isRunningOnTv()) { if (context.isRunningOnTv()) {
navController.navigate(Screens.Main) navController.navigate(Route.Main)
} else { } else {
val isPopped = navController.popBackStack() val isPopped = navController.popBackStack()
if (!isPopped) { if (!isPopped) {
navController.navigate(Screens.Main) navController.navigate(Route.Main)
} }
} }
}, },

View File

@ -69,7 +69,7 @@ import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.ui.AppUiState import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.AppViewModel import com.zaneschepke.wireguardautotunnel.ui.AppViewModel
import com.zaneschepke.wireguardautotunnel.ui.Screens import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton
import com.zaneschepke.wireguardautotunnel.ui.common.config.ConfigurationToggle import com.zaneschepke.wireguardautotunnel.ui.common.config.ConfigurationToggle
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
@ -581,7 +581,7 @@ fun SettingsScreen(
} else { } else {
// TODO may want to show a dialog before proceeding in the future // TODO may want to show a dialog before proceeding in the future
PinManager.initialize(WireGuardAutoTunnel.instance) PinManager.initialize(WireGuardAutoTunnel.instance)
navController.navigate(Screens.Lock) navController.navigate(Route.Lock)
} }
}, },
) )

View File

@ -46,7 +46,7 @@ import androidx.navigation.NavController
import com.zaneschepke.wireguardautotunnel.BuildConfig import com.zaneschepke.wireguardautotunnel.BuildConfig
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.AppUiState import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.Screens import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
import com.zaneschepke.wireguardautotunnel.util.extensions.launchSupportEmail import com.zaneschepke.wireguardautotunnel.util.extensions.launchSupportEmail
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
@ -244,7 +244,7 @@ fun SupportScreen(navController: NavController, focusRequester: FocusRequester,
color = MaterialTheme.colorScheme.onBackground, color = MaterialTheme.colorScheme.onBackground,
) )
TextButton( TextButton(
onClick = { navController.navigate(Screens.Logs) }, onClick = { navController.navigate(Route.Logs) },
modifier = Modifier.padding(vertical = 5.dp), modifier = Modifier.padding(vertical = 5.dp),
) { ) {
Row( Row(