diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/MainActivity.kt b/app/src/main/kotlin/com/x8bit/bitwarden/MainActivity.kt index 5cdff27b50..512e9f3ddf 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/MainActivity.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/MainActivity.kt @@ -36,7 +36,7 @@ import com.x8bit.bitwarden.ui.platform.composition.LocalManagerProvider import com.x8bit.bitwarden.ui.platform.feature.debugmenu.debugMenuDestination import com.x8bit.bitwarden.ui.platform.feature.debugmenu.manager.DebugMenuLaunchManager import com.x8bit.bitwarden.ui.platform.feature.debugmenu.navigateToDebugMenuScreen -import com.x8bit.bitwarden.ui.platform.feature.rootnav.ROOT_ROUTE +import com.x8bit.bitwarden.ui.platform.feature.rootnav.RootNavigationRoute import com.x8bit.bitwarden.ui.platform.feature.rootnav.rootNavDestination import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage import com.x8bit.bitwarden.ui.platform.model.AuthTabLaunchers @@ -119,11 +119,11 @@ class MainActivity : AppCompatActivity() { ) { NavHost( navController = navController, - startDestination = ROOT_ROUTE, + startDestination = RootNavigationRoute, ) { - // Nothing else should end up at this top level, we just want the ability - // to have the debug menu appear on top of the rest of the app without - // interacting with the state-based navigation used by the RootNavScreen. + // Both root navigation and debug menu exist at this top level. + // The debug menu can appear on top of the rest of the app without + // interacting with the state-based navigation used by RootNavScreen. rootNavDestination { shouldShowSplashScreen = false } debugMenuDestination( onNavigateBack = { navController.popBackStack() }, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavNavigation.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavNavigation.kt index 7814d8bf30..7a204764c4 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavNavigation.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavNavigation.kt @@ -1,12 +1,17 @@ +@file:OmitFromCoverage + package com.x8bit.bitwarden.ui.platform.feature.rootnav import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import com.bitwarden.annotation.OmitFromCoverage +import kotlinx.serialization.Serializable /** - * The route for the root navigation screen. + * The type-safe route for the root navigation screen. */ -const val ROOT_ROUTE: String = "root" +@Serializable +data object RootNavigationRoute /** * Add the root navigation screen to the nav graph. @@ -14,7 +19,7 @@ const val ROOT_ROUTE: String = "root" fun NavGraphBuilder.rootNavDestination( onSplashScreenRemoved: () -> Unit, ) { - composable(route = ROOT_ROUTE) { + composable { RootNavScreen(onSplashScreenRemoved = onSplashScreenRemoved) } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreen.kt index 87cc2f1129..8b2bc4e046 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarScreen.kt @@ -11,15 +11,12 @@ import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavBackStackEntry -import androidx.navigation.NavController import androidx.navigation.NavDestination.Companion.hierarchy -import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController -import androidx.navigation.NavOptions import androidx.navigation.compose.NavHost import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.navOptions import com.bitwarden.ui.platform.base.util.EventsEffect +import com.bitwarden.ui.platform.base.util.navigateToTabOrRoot import com.bitwarden.ui.platform.components.navigation.model.NavigationItem import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold import com.bitwarden.ui.platform.components.scaffold.model.ScaffoldNavigationData @@ -29,21 +26,17 @@ import com.x8bit.bitwarden.ui.platform.components.util.rememberBitwardenNavContr import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType import com.x8bit.bitwarden.ui.platform.feature.settings.about.navigateToAbout import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.navigateToAutoFill -import com.x8bit.bitwarden.ui.platform.feature.settings.navigateToSettingsGraph import com.x8bit.bitwarden.ui.platform.feature.settings.navigateToSettingsGraphRoot import com.x8bit.bitwarden.ui.platform.feature.settings.settingsGraph import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.model.VaultUnlockedNavBarTab import com.x8bit.bitwarden.ui.tools.feature.generator.generatorGraph -import com.x8bit.bitwarden.ui.tools.feature.generator.navigateToGeneratorGraph import com.x8bit.bitwarden.ui.tools.feature.send.addedit.AddEditSendRoute -import com.x8bit.bitwarden.ui.tools.feature.send.navigateToSendGraph import com.x8bit.bitwarden.ui.tools.feature.send.sendGraph import com.x8bit.bitwarden.ui.tools.feature.send.viewsend.ViewSendRoute import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditArgs import com.x8bit.bitwarden.ui.vault.feature.importitems.navigateToImportItemsScreen import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemArgs import com.x8bit.bitwarden.ui.vault.feature.vault.VaultGraphRoute -import com.x8bit.bitwarden.ui.vault.feature.vault.navigateToVaultGraph import com.x8bit.bitwarden.ui.vault.feature.vault.vaultGraph import kotlinx.collections.immutable.persistentListOf @@ -81,41 +74,7 @@ fun VaultUnlockedNavBarScreen( val state by viewModel.stateFlow.collectAsStateWithLifecycle() EventsEffect(viewModel = viewModel) { event -> - navController.apply { - when (event) { - is VaultUnlockedNavBarEvent.Shortcut.NavigateToVaultScreen, - is VaultUnlockedNavBarEvent.NavigateToVaultScreen, - -> { - navigateToTabOrRoot(tabToNavigateTo = event.tab) { - navigateToVaultGraph(navOptions = it) - } - } - - VaultUnlockedNavBarEvent.Shortcut.NavigateToSendScreen, - VaultUnlockedNavBarEvent.NavigateToSendScreen, - -> { - navigateToTabOrRoot(tabToNavigateTo = event.tab) { - navigateToSendGraph(navOptions = it) - } - } - - VaultUnlockedNavBarEvent.Shortcut.NavigateToGeneratorScreen, - VaultUnlockedNavBarEvent.NavigateToGeneratorScreen, - -> { - navigateToTabOrRoot(tabToNavigateTo = event.tab) { - navigateToGeneratorGraph(navOptions = it) - } - } - - VaultUnlockedNavBarEvent.Shortcut.NavigateToSettingsScreen, - VaultUnlockedNavBarEvent.NavigateToSettingsScreen, - -> { - navigateToTabOrRoot(tabToNavigateTo = event.tab) { - navigateToSettingsGraph(navOptions = it) - } - } - } - } + navController.navigateToTabOrRoot(target = event.tab) } VaultUnlockedNavBarScaffold( @@ -279,37 +238,6 @@ private fun VaultUnlockedNavBarScaffold( } } -/** - * Helper function to determine how to navigate to a specified [VaultUnlockedNavBarTab]. - * If direct navigation is required, the [navigate] lambda will be invoked with the appropriate - * [NavOptions]. - */ -@Suppress("MaxLineLength") -private fun NavController.navigateToTabOrRoot( - tabToNavigateTo: VaultUnlockedNavBarTab, - navigate: (NavOptions) -> Unit, -) { - if (tabToNavigateTo.startDestinationRoute.toObjectNavigationRoute() == currentDestination?.route) { - // We are at the start destination already, so nothing to do. - return - } else if (currentDestination?.parent?.route == tabToNavigateTo.graphRoute.toObjectNavigationRoute()) { - // We are not at the start destination but we are in the correct graph, - // so lets pop up to the start destination. - popBackStack(route = tabToNavigateTo.startDestinationRoute, inclusive = false) - } else { - // We are not in correct graph at all, so navigate there. - navigate( - navOptions { - popUpTo(id = graph.findStartDestination().id) { - saveState = true - } - launchSingleTop = true - restoreState = true - }, - ) - } -} - /** * Determine if the current destination is the same as the given tab. */ diff --git a/app/src/main/res/values-night/values.xml b/app/src/main/res/values-night/values.xml index 9191708a30..7f0ce6be00 100644 --- a/app/src/main/res/values-night/values.xml +++ b/app/src/main/res/values-night/values.xml @@ -1,5 +1,4 @@ - 0.75 @android:color/transparent diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index b4e16f546e..e64680e124 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -1,6 +1,5 @@ - 0.55 0 @android:color/transparent diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/MainActivity.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/MainActivity.kt index 06ac904c73..0a440dc8bd 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/MainActivity.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/MainActivity.kt @@ -10,24 +10,26 @@ import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate +import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.lifecycleScope import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost import androidx.navigation.compose.rememberNavController import com.bitwarden.authenticator.data.platform.repository.SettingsRepository import com.bitwarden.authenticator.ui.platform.composition.LocalManagerProvider +import com.bitwarden.authenticator.ui.platform.feature.debugmenu.debugMenuDestination import com.bitwarden.authenticator.ui.platform.feature.debugmenu.manager.DebugMenuLaunchManager import com.bitwarden.authenticator.ui.platform.feature.debugmenu.navigateToDebugMenuScreen -import com.bitwarden.authenticator.ui.platform.feature.rootnav.RootNavScreen +import com.bitwarden.authenticator.ui.platform.feature.rootnav.RootNavigationRoute +import com.bitwarden.authenticator.ui.platform.feature.rootnav.rootNavDestination +import com.bitwarden.ui.platform.base.util.EventsEffect import com.bitwarden.ui.platform.theme.BitwardenTheme import com.bitwarden.ui.platform.util.setupEdgeToEdge import com.bitwarden.ui.platform.util.validate import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach import javax.inject.Inject /** @@ -61,20 +63,28 @@ class MainActivity : AppCompatActivity() { AppCompatDelegate.setDefaultNightMode(settingsRepository.appTheme.osValue) setupEdgeToEdge(appThemeFlow = mainViewModel.stateFlow.map { it.theme }) setContent { + val navController = rememberNavController() + SetupEventsEffect(navController = navController) val state by mainViewModel.stateFlow.collectAsStateWithLifecycle() updateScreenCapture(isScreenCaptureAllowed = state.isScreenCaptureAllowed) - val navController = rememberNavController() - observeViewModelEvents(navController) LocalManagerProvider { BitwardenTheme( theme = state.theme, dynamicColor = state.isDynamicColorsEnabled, ) { - RootNavScreen( + NavHost( navController = navController, - onSplashScreenRemoved = { shouldShowSplashScreen = false }, - onExitApplication = { finishAffinity() }, - ) + startDestination = RootNavigationRoute, + ) { + // Both root navigation and debug menu exist at this top level. + // The debug menu can appear on top of the rest of the app without + // interacting with the state-based navigation used by RootNavScreen. + rootNavDestination { shouldShowSplashScreen = false } + debugMenuDestination( + onNavigateBack = { navController.popBackStack() }, + onSplashScreenRemoved = { shouldShowSplashScreen = false }, + ) + } } } } @@ -92,18 +102,16 @@ class MainActivity : AppCompatActivity() { mainViewModel.trySendAction(MainAction.ReceiveNewIntent(intent = newIntent)) } - private fun observeViewModelEvents(navController: NavHostController) { - mainViewModel - .eventFlow - .onEach { event -> - when (event) { - MainEvent.NavigateToDebugMenu -> navController.navigateToDebugMenuScreen() - is MainEvent.UpdateAppTheme -> { - AppCompatDelegate.setDefaultNightMode(event.osTheme) - } + @Composable + private fun SetupEventsEffect(navController: NavHostController) { + EventsEffect(viewModel = mainViewModel) { event -> + when (event) { + MainEvent.NavigateToDebugMenu -> navController.navigateToDebugMenuScreen() + is MainEvent.UpdateAppTheme -> { + AppCompatDelegate.setDefaultNightMode(event.osTheme) } } - .launchIn(lifecycleScope) + } } override fun dispatchTouchEvent(event: MotionEvent): Boolean = debugLaunchManager diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/authenticator/AuthenticatorNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/authenticator/AuthenticatorNavigation.kt index bcaeb4e017..227c46c907 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/authenticator/AuthenticatorNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/authenticator/AuthenticatorNavigation.kt @@ -1,19 +1,24 @@ +@file:OmitFromCoverage + package com.bitwarden.authenticator.ui.authenticator.feature.authenticator import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.navigation +import com.bitwarden.annotation.OmitFromCoverage +import com.bitwarden.authenticator.ui.authenticator.feature.edititem.editItemDestination import com.bitwarden.authenticator.ui.authenticator.feature.edititem.navigateToEditItem -import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.itemListingGraph +import com.bitwarden.authenticator.ui.authenticator.feature.manualcodeentry.manualCodeEntryDestination import com.bitwarden.authenticator.ui.authenticator.feature.manualcodeentry.navigateToManualCodeEntryScreen import com.bitwarden.authenticator.ui.authenticator.feature.navbar.AuthenticatorNavbarRoute import com.bitwarden.authenticator.ui.authenticator.feature.navbar.authenticatorNavBarDestination import com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.navigateToQrCodeScanScreen +import com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.qrCodeScanDestination +import com.bitwarden.authenticator.ui.authenticator.feature.search.itemSearchDestination import com.bitwarden.authenticator.ui.authenticator.feature.search.navigateToSearch -import com.bitwarden.authenticator.ui.platform.feature.settings.export.navigateToExport -import com.bitwarden.authenticator.ui.platform.feature.settings.importing.navigateToImporting import com.bitwarden.authenticator.ui.platform.feature.tutorial.navigateToSettingsTutorial +import com.bitwarden.authenticator.ui.platform.feature.tutorial.tutorialSettingsDestination import kotlinx.serialization.Serializable /** @@ -34,39 +39,41 @@ fun NavController.navigateToAuthenticatorGraph(navOptions: NavOptions? = null) { */ fun NavGraphBuilder.authenticatorGraph( navController: NavController, - onNavigateBack: () -> Unit, ) { navigation( startDestination = AuthenticatorNavbarRoute, ) { authenticatorNavBarDestination( - onNavigateBack = onNavigateBack, + onNavigateBack = { navController.popBackStack() }, onNavigateToSearch = { navController.navigateToSearch() }, onNavigateToQrCodeScanner = { navController.navigateToQrCodeScanScreen() }, onNavigateToManualKeyEntry = { navController.navigateToManualCodeEntryScreen() }, onNavigateToEditItem = { navController.navigateToEditItem(itemId = it) }, - onNavigateToExport = { navController.navigateToExport() }, - onNavigateToImport = { navController.navigateToImporting() }, onNavigateToTutorial = { navController.navigateToSettingsTutorial() }, ) - itemListingGraph( - navController = navController, - navigateBack = onNavigateBack, - navigateToSearch = { - navController.navigateToSearch() - }, - navigateToQrCodeScanner = { - navController.navigateToQrCodeScanScreen() - }, - navigateToManualKeyEntry = { + editItemDestination( + onNavigateBack = { navController.popBackStack() }, + ) + itemSearchDestination( + onNavigateBack = { navController.popBackStack() }, + onNavigateToEdit = { navController.navigateToEditItem(itemId = it) }, + ) + qrCodeScanDestination( + onNavigateBack = { navController.popBackStack() }, + onNavigateToManualCodeEntryScreen = { + navController.popBackStack() navController.navigateToManualCodeEntryScreen() }, - navigateToEditItem = { - navController.navigateToEditItem(itemId = it) + ) + manualCodeEntryDestination( + onNavigateBack = { navController.popBackStack() }, + onNavigateToQrCodeScreen = { + navController.popBackStack() + navController.navigateToQrCodeScanScreen() }, - navigateToExport = { navController.navigateToExport() }, - navigateToImport = { navController.navigateToImporting() }, - navigateToTutorial = { navController.navigateToSettingsTutorial() }, + ) + tutorialSettingsDestination( + onTutorialFinished = { navController.popBackStack() }, ) } } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/edititem/EditItemNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/edititem/EditItemNavigation.kt index 8be06ced00..eedfc71226 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/edititem/EditItemNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/edititem/EditItemNavigation.kt @@ -5,7 +5,7 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.toRoute -import com.bitwarden.ui.platform.base.util.composableWithPushTransitions +import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions import kotlinx.serialization.Serializable /** @@ -37,7 +37,7 @@ fun SavedStateHandle.toEditItemArgs(): EditItemArgs { fun NavGraphBuilder.editItemDestination( onNavigateBack: () -> Unit = { }, ) { - composableWithPushTransitions { + composableWithSlideTransitions { EditItemScreen( onNavigateBack = onNavigateBack, ) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingGraphNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingGraphNavigation.kt index a61f2d1da2..c585d55a97 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingGraphNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingGraphNavigation.kt @@ -4,13 +4,6 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.navigation -import com.bitwarden.authenticator.ui.authenticator.feature.edititem.editItemDestination -import com.bitwarden.authenticator.ui.authenticator.feature.manualcodeentry.manualCodeEntryDestination -import com.bitwarden.authenticator.ui.authenticator.feature.manualcodeentry.navigateToManualCodeEntryScreen -import com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.navigateToQrCodeScanScreen -import com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.qrCodeScanDestination -import com.bitwarden.authenticator.ui.authenticator.feature.search.itemSearchDestination -import com.bitwarden.authenticator.ui.platform.feature.settings.settingsGraph import kotlinx.serialization.Serializable /** @@ -22,54 +15,22 @@ data object ItemListingGraphRoute /** * Add the item listing graph to the nav graph. */ -@Suppress("LongParameterList") fun NavGraphBuilder.itemListingGraph( - navController: NavController, - navigateBack: () -> Unit, - navigateToSearch: () -> Unit, - navigateToQrCodeScanner: () -> Unit, - navigateToManualKeyEntry: () -> Unit, - navigateToEditItem: (String) -> Unit, - navigateToExport: () -> Unit, - navigateToImport: () -> Unit, - navigateToTutorial: () -> Unit, + onNavigateBack: () -> Unit, + onNavigateToSearch: () -> Unit, + onNavigateToQrCodeScanner: () -> Unit, + onNavigateToManualKeyEntry: () -> Unit, + onNavigateToEditItem: (String) -> Unit, ) { navigation( startDestination = ItemListingRoute, ) { itemListingDestination( - onNavigateBack = navigateBack, - onNavigateToSearch = navigateToSearch, - onNavigateToQrCodeScanner = navigateToQrCodeScanner, - onNavigateToManualKeyEntry = navigateToManualKeyEntry, - onNavigateToEditItemScreen = navigateToEditItem, - ) - editItemDestination( - onNavigateBack = { navController.popBackStack() }, - ) - itemSearchDestination( - onNavigateBack = { navController.popBackStack() }, - onNavigateToEdit = navigateToEditItem, - ) - qrCodeScanDestination( - onNavigateBack = { navController.popBackStack() }, - onNavigateToManualCodeEntryScreen = { - navController.popBackStack() - navController.navigateToManualCodeEntryScreen() - }, - ) - manualCodeEntryDestination( - onNavigateBack = { navController.popBackStack() }, - onNavigateToQrCodeScreen = { - navController.popBackStack() - navController.navigateToQrCodeScanScreen() - }, - ) - settingsGraph( - navController = navController, - onNavigateToExport = navigateToExport, - onNavigateToImport = navigateToImport, - onNavigateToTutorial = navigateToTutorial, + onNavigateBack = onNavigateBack, + onNavigateToSearch = onNavigateToSearch, + onNavigateToQrCodeScanner = onNavigateToQrCodeScanner, + onNavigateToManualKeyEntry = onNavigateToManualKeyEntry, + onNavigateToEditItemScreen = onNavigateToEditItem, ) } } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingNavigation.kt index a10a6172cd..963da07ff6 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingNavigation.kt @@ -1,7 +1,7 @@ package com.bitwarden.authenticator.ui.authenticator.feature.itemlisting import androidx.navigation.NavGraphBuilder -import com.bitwarden.ui.platform.base.util.composableWithPushTransitions +import com.bitwarden.ui.platform.base.util.composableWithRootPushTransitions import kotlinx.serialization.Serializable /** @@ -20,7 +20,7 @@ fun NavGraphBuilder.itemListingDestination( onNavigateToManualKeyEntry: () -> Unit = { }, onNavigateToEditItemScreen: (id: String) -> Unit = { }, ) { - composableWithPushTransitions { + composableWithRootPushTransitions { ItemListingScreen( onNavigateBack = onNavigateBack, onNavigateToSearch = onNavigateToSearch, diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarNavigation.kt index dee1010cc8..f0d74299e6 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarNavigation.kt @@ -20,8 +20,6 @@ fun NavGraphBuilder.authenticatorNavBarDestination( onNavigateToQrCodeScanner: () -> Unit, onNavigateToManualKeyEntry: () -> Unit, onNavigateToEditItem: (itemId: String) -> Unit, - onNavigateToExport: () -> Unit, - onNavigateToImport: () -> Unit, onNavigateToTutorial: () -> Unit, ) { composableWithStayTransitions { @@ -31,8 +29,6 @@ fun NavGraphBuilder.authenticatorNavBarDestination( onNavigateToQrCodeScanner = onNavigateToQrCodeScanner, onNavigateToManualKeyEntry = onNavigateToManualKeyEntry, onNavigateToEditItem = onNavigateToEditItem, - onNavigateToExport = onNavigateToExport, - onNavigateToImport = onNavigateToImport, onNavigateToTutorial = onNavigateToTutorial, ) } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarScreen.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarScreen.kt index 04a9f9387e..c7b5b83e87 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarScreen.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarScreen.kt @@ -2,7 +2,6 @@ package com.bitwarden.authenticator.ui.authenticator.feature.navbar import android.os.Parcelable import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -11,22 +10,20 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation.NavBackStackEntry -import androidx.navigation.NavController -import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController -import androidx.navigation.NavOptions import androidx.navigation.compose.NavHost import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController -import androidx.navigation.navOptions import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.ItemListingGraphRoute import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.ItemListingRoute import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.itemListingGraph -import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.navigateToItemListGraph import com.bitwarden.authenticator.ui.platform.feature.settings.SettingsGraphRoute import com.bitwarden.authenticator.ui.platform.feature.settings.SettingsRoute -import com.bitwarden.authenticator.ui.platform.feature.settings.navigateToSettingsGraph +import com.bitwarden.authenticator.ui.platform.feature.settings.export.navigateToExport +import com.bitwarden.authenticator.ui.platform.feature.settings.importing.navigateToImporting +import com.bitwarden.authenticator.ui.platform.feature.settings.settingsGraph import com.bitwarden.ui.platform.base.util.EventsEffect +import com.bitwarden.ui.platform.base.util.navigateToTabOrRoot import com.bitwarden.ui.platform.components.navigation.model.NavigationItem import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold import com.bitwarden.ui.platform.components.scaffold.model.ScaffoldNavigationData @@ -52,23 +49,10 @@ fun AuthenticatorNavBarScreen( onNavigateToQrCodeScanner: () -> Unit, onNavigateToManualKeyEntry: () -> Unit, onNavigateToEditItem: (itemId: String) -> Unit, - onNavigateToExport: () -> Unit, - onNavigateToImport: () -> Unit, onNavigateToTutorial: () -> Unit, ) { EventsEffect(viewModel = viewModel) { event -> - navController.apply { - val navOptions = navController.authenticatorNavBarScreenNavOptions() - when (event) { - AuthenticatorNavBarEvent.NavigateToSettings -> { - navigateToSettingsGraph(navOptions) - } - - AuthenticatorNavBarEvent.NavigateToVerificationCodes -> { - navigateToItemListGraph(navOptions) - } - } - } + navController.navigateToTabOrRoot(target = event.tab) } LaunchedEffect(Unit) { @@ -93,13 +77,10 @@ fun AuthenticatorNavBarScreen( navigateToQrCodeScanner = onNavigateToQrCodeScanner, navigateToManualKeyEntry = onNavigateToManualKeyEntry, navigateToEditItem = onNavigateToEditItem, - navigateToExport = onNavigateToExport, - navigateToImport = onNavigateToImport, - navigateToTutorial = onNavigateToTutorial, + onNavigateToTutorial = onNavigateToTutorial, ) } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun AuthenticatorNavBarScaffold( navController: NavHostController, @@ -110,9 +91,7 @@ private fun AuthenticatorNavBarScaffold( navigateToQrCodeScanner: () -> Unit, navigateToManualKeyEntry: () -> Unit, navigateToEditItem: (itemId: String) -> Unit, - navigateToExport: () -> Unit, - navigateToImport: () -> Unit, - navigateToTutorial: () -> Unit, + onNavigateToTutorial: () -> Unit, ) { var shouldDimNavBar by rememberSaveable { mutableStateOf(value = false) } @@ -144,15 +123,17 @@ private fun AuthenticatorNavBarScaffold( popExitTransition = RootTransitionProviders.Exit.fadeOut, ) { itemListingGraph( - navController = navController, - navigateBack = navigateBack, - navigateToSearch = navigateToSearch, - navigateToQrCodeScanner = navigateToQrCodeScanner, - navigateToManualKeyEntry = navigateToManualKeyEntry, - navigateToEditItem = navigateToEditItem, - navigateToExport = navigateToExport, - navigateToImport = navigateToImport, - navigateToTutorial = navigateToTutorial, + onNavigateBack = navigateBack, + onNavigateToSearch = navigateToSearch, + onNavigateToQrCodeScanner = navigateToQrCodeScanner, + onNavigateToManualKeyEntry = navigateToManualKeyEntry, + onNavigateToEditItem = navigateToEditItem, + ) + settingsGraph( + onNavigateBack = { navController.popBackStack() }, + onNavigateToTutorial = onNavigateToTutorial, + onNavigateToExport = { navController.navigateToExport() }, + onNavigateToImport = { navController.navigateToImporting() }, ) } } @@ -171,8 +152,9 @@ private fun AuthenticatorNavBarScaffold( * @property iconResSelected The resource ID for the icon representing the tab when it's selected. */ @Parcelize -private sealed class AuthenticatorNavBarTab : NavigationItem, Parcelable { +sealed class AuthenticatorNavBarTab : NavigationItem, Parcelable { + @Suppress("UndocumentedPublicClass") companion object { /** * The list of navigation tabs available in the authenticator. @@ -214,18 +196,6 @@ private sealed class AuthenticatorNavBarTab : NavigationItem, Parcelable { } } -/** - * Helper function to generate [NavOptions] for [AuthenticatorNavBarScreen]. - */ -private fun NavController.authenticatorNavBarScreenNavOptions(): NavOptions = - navOptions { - popUpTo(graph.findStartDestination().id) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - /** * Determine if the current destination is the same as the given tab. */ diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarViewModel.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarViewModel.kt index 7b01481598..6ffb63f155 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarViewModel.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/navbar/AuthenticatorNavBarViewModel.kt @@ -46,15 +46,25 @@ class AuthenticatorNavBarViewModel @Inject constructor( * Models events for the [AuthenticatorNavBarViewModel]. */ sealed class AuthenticatorNavBarEvent { + + /** + * The [AuthenticatorNavBarTab] to be associated with the event. + */ + abstract val tab: AuthenticatorNavBarTab + /** * Navigate to the verification codes screen. */ - data object NavigateToVerificationCodes : AuthenticatorNavBarEvent() + data object NavigateToVerificationCodes : AuthenticatorNavBarEvent() { + override val tab: AuthenticatorNavBarTab = AuthenticatorNavBarTab.VerificationCodes + } /** * Navigate to the settings screen. */ - data object NavigateToSettings : AuthenticatorNavBarEvent() + data object NavigateToSettings : AuthenticatorNavBarEvent() { + override val tab: AuthenticatorNavBarTab = AuthenticatorNavBarTab.Settings + } } /** diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/debugmenu/DebugMenuNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/debugmenu/DebugMenuNavigation.kt index cb1376f5e8..cfdf367747 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/debugmenu/DebugMenuNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/debugmenu/DebugMenuNavigation.kt @@ -23,10 +23,13 @@ fun NavController.navigateToDebugMenuScreen() { /** * Add the setup unlock screen to the nav graph. */ -fun NavGraphBuilder.setupDebugMenuDestination( +fun NavGraphBuilder.debugMenuDestination( onNavigateBack: () -> Unit, + onSplashScreenRemoved: () -> Unit, ) { composableWithPushTransitions { DebugMenuScreen(onNavigateBack = onNavigateBack) + // If we are displaying the debug screen, then we can just hide the splash screen. + onSplashScreenRemoved() } } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/rootnav/RootNavNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/rootnav/RootNavNavigation.kt new file mode 100644 index 0000000000..91c088f4b5 --- /dev/null +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/rootnav/RootNavNavigation.kt @@ -0,0 +1,25 @@ +@file:OmitFromCoverage + +package com.bitwarden.authenticator.ui.platform.feature.rootnav + +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import com.bitwarden.annotation.OmitFromCoverage +import kotlinx.serialization.Serializable + +/** + * The type-safe route for the root navigation screen. + */ +@Serializable +data object RootNavigationRoute + +/** + * Add the root navigation screen to the nav graph. + */ +fun NavGraphBuilder.rootNavDestination( + onSplashScreenRemoved: () -> Unit, +) { + composable { + RootNavScreen(onSplashScreenRemoved = onSplashScreenRemoved) + } +} diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/rootnav/RootNavScreen.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/rootnav/RootNavScreen.kt index d8f6462bec..c02b95ddea 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/rootnav/RootNavScreen.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/rootnav/RootNavScreen.kt @@ -19,7 +19,6 @@ import com.bitwarden.authenticator.ui.auth.unlock.unlockDestination import com.bitwarden.authenticator.ui.authenticator.feature.authenticator.AuthenticatorGraphRoute import com.bitwarden.authenticator.ui.authenticator.feature.authenticator.authenticatorGraph import com.bitwarden.authenticator.ui.authenticator.feature.authenticator.navigateToAuthenticatorGraph -import com.bitwarden.authenticator.ui.platform.feature.debugmenu.setupDebugMenuDestination import com.bitwarden.authenticator.ui.platform.feature.splash.SplashRoute import com.bitwarden.authenticator.ui.platform.feature.splash.navigateToSplash import com.bitwarden.authenticator.ui.platform.feature.splash.splashDestination @@ -42,8 +41,7 @@ import java.util.concurrent.atomic.AtomicReference fun RootNavScreen( viewModel: RootNavViewModel = hiltViewModel(), navController: NavHostController = rememberNavController(), - onSplashScreenRemoved: () -> Unit = {}, - onExitApplication: () -> Unit, + onSplashScreenRemoved: () -> Unit, ) { val state by viewModel.stateFlow.collectAsState() val previousStateReference = remember { AtomicReference(state) } @@ -82,15 +80,7 @@ fun RootNavScreen( viewModel.trySendAction(RootNavAction.Internal.AppUnlocked) }, ) - setupDebugMenuDestination( - onNavigateBack = { - navController.popBackStack() - }, - ) - authenticatorGraph( - navController = navController, - onNavigateBack = onExitApplication, - ) + authenticatorGraph(navController = navController) } val targetRoute = when (state.navState) { diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/SettingsNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/SettingsNavigation.kt index f28b5df78c..19410e3186 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/SettingsNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/SettingsNavigation.kt @@ -6,7 +6,6 @@ import androidx.navigation.NavOptions import androidx.navigation.navigation import com.bitwarden.authenticator.ui.platform.feature.settings.export.exportDestination import com.bitwarden.authenticator.ui.platform.feature.settings.importing.importingDestination -import com.bitwarden.authenticator.ui.platform.feature.tutorial.tutorialSettingsDestination import com.bitwarden.ui.platform.base.util.composableWithRootPushTransitions import kotlinx.serialization.Serializable @@ -23,32 +22,44 @@ data object SettingsGraphRoute data object SettingsRoute /** - * Add settings graph to the nav graph. + * Add settings destination to the nav graph. */ -fun NavGraphBuilder.settingsGraph( - navController: NavController, +fun NavGraphBuilder.settingsDestination( onNavigateToExport: () -> Unit, onNavigateToImport: () -> Unit, onNavigateToTutorial: () -> Unit, +) { + composableWithRootPushTransitions { + SettingsScreen( + onNavigateToTutorial = onNavigateToTutorial, + onNavigateToExport = onNavigateToExport, + onNavigateToImport = onNavigateToImport, + ) + } +} + +/** + * Add settings graph to the nav graph. + */ +fun NavGraphBuilder.settingsGraph( + onNavigateBack: () -> Unit, + onNavigateToTutorial: () -> Unit, + onNavigateToExport: () -> Unit, + onNavigateToImport: () -> Unit, ) { navigation( startDestination = SettingsRoute, ) { - composableWithRootPushTransitions { - SettingsScreen( - onNavigateToTutorial = onNavigateToTutorial, - onNavigateToExport = onNavigateToExport, - onNavigateToImport = onNavigateToImport, - ) - } - tutorialSettingsDestination( - onTutorialFinished = { navController.popBackStack() }, + settingsDestination( + onNavigateToTutorial = onNavigateToTutorial, + onNavigateToExport = onNavigateToExport, + onNavigateToImport = onNavigateToImport, ) exportDestination( - onNavigateBack = { navController.popBackStack() }, + onNavigateBack = onNavigateBack, ) importingDestination( - onNavigateBack = { navController.popBackStack() }, + onNavigateBack = onNavigateBack, ) } } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/SettingsScreen.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/SettingsScreen.kt index cb8c08251c..a4e704bf26 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/SettingsScreen.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/SettingsScreen.kt @@ -57,6 +57,7 @@ import com.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton import com.bitwarden.ui.platform.components.header.BitwardenListHeaderText import com.bitwarden.ui.platform.components.model.CardStyle import com.bitwarden.ui.platform.components.row.BitwardenExternalLinkRow +import com.bitwarden.ui.platform.components.row.BitwardenPushRow import com.bitwarden.ui.platform.components.row.BitwardenTextRow import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold import com.bitwarden.ui.platform.components.toggle.BitwardenSwitch @@ -333,37 +334,21 @@ private fun ColumnScope.VaultSettings( label = stringResource(id = BitwardenString.data), ) Spacer(modifier = Modifier.height(height = 8.dp)) - BitwardenTextRow( + BitwardenPushRow( text = stringResource(id = BitwardenString.import_vault), onClick = onImportClick, + cardStyle = CardStyle.Top(), modifier = Modifier .standardHorizontalMargin() .testTag("Import"), - cardStyle = CardStyle.Top(), - content = { - Icon( - modifier = Modifier.mirrorIfRtl(), - painter = painterResource(id = BitwardenDrawable.ic_chevron_right), - contentDescription = null, - tint = BitwardenTheme.colorScheme.icon.primary, - ) - }, ) - BitwardenTextRow( + BitwardenPushRow( text = stringResource(id = BitwardenString.export), onClick = onExportClick, + cardStyle = CardStyle.Middle(), modifier = Modifier .standardHorizontalMargin() .testTag("Export"), - cardStyle = CardStyle.Middle(), - content = { - Icon( - modifier = Modifier.mirrorIfRtl(), - painter = painterResource(id = BitwardenDrawable.ic_chevron_right), - contentDescription = null, - tint = BitwardenTheme.colorScheme.icon.primary, - ) - }, ) BitwardenExternalLinkRow( text = stringResource(BitwardenString.backup), diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/export/ExportNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/export/ExportNavigation.kt index 135ed566e3..1c1f09d977 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/export/ExportNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/export/ExportNavigation.kt @@ -3,7 +3,7 @@ package com.bitwarden.authenticator.ui.platform.feature.settings.export import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions -import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions +import com.bitwarden.ui.platform.base.util.composableWithPushTransitions import kotlinx.serialization.Serializable /** @@ -18,7 +18,7 @@ data object ExportRoute fun NavGraphBuilder.exportDestination( onNavigateBack: () -> Unit, ) { - composableWithSlideTransitions { + composableWithPushTransitions { ExportScreen( onNavigateBack = onNavigateBack, ) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/export/ExportScreen.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/export/ExportScreen.kt index d16e83c4d4..87a66b1a50 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/export/ExportScreen.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/export/ExportScreen.kt @@ -139,8 +139,8 @@ fun ExportScreen( BitwardenTopAppBar( title = stringResource(id = BitwardenString.export), scrollBehavior = scrollBehavior, - navigationIcon = painterResource(id = BitwardenDrawable.ic_close), - navigationIconContentDescription = stringResource(id = BitwardenString.close), + navigationIcon = painterResource(id = BitwardenDrawable.ic_back), + navigationIconContentDescription = stringResource(id = BitwardenString.back), onNavigationIconClick = remember(viewModel) { { viewModel.trySendAction(ExportAction.CloseButtonClick) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/importing/ImportingNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/importing/ImportingNavigation.kt index 3694d7a0c1..5879f6095c 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/importing/ImportingNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/importing/ImportingNavigation.kt @@ -3,7 +3,7 @@ package com.bitwarden.authenticator.ui.platform.feature.settings.importing import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions -import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions +import com.bitwarden.ui.platform.base.util.composableWithPushTransitions import kotlinx.serialization.Serializable /** @@ -18,7 +18,7 @@ data object ImportRoute fun NavGraphBuilder.importingDestination( onNavigateBack: () -> Unit, ) { - composableWithSlideTransitions { + composableWithPushTransitions { ImportingScreen( onNavigateBack = onNavigateBack, ) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/importing/ImportingScreen.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/importing/ImportingScreen.kt index 511a7d5794..6b572efba3 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/importing/ImportingScreen.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/settings/importing/ImportingScreen.kt @@ -123,8 +123,8 @@ fun ImportingScreen( BitwardenTopAppBar( title = stringResource(id = BitwardenString.import_vault), scrollBehavior = scrollBehavior, - navigationIcon = painterResource(id = BitwardenDrawable.ic_close), - navigationIconContentDescription = stringResource(id = BitwardenString.close), + navigationIcon = painterResource(id = BitwardenDrawable.ic_back), + navigationIconContentDescription = stringResource(id = BitwardenString.back), onNavigationIconClick = remember(viewModel) { { viewModel.trySendAction(ImportAction.CloseButtonClick) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/tutorial/TutorialNavigation.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/tutorial/TutorialNavigation.kt index 1c121fc8d4..625b6b57f6 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/tutorial/TutorialNavigation.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/feature/tutorial/TutorialNavigation.kt @@ -3,7 +3,8 @@ package com.bitwarden.authenticator.ui.platform.feature.tutorial import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions -import androidx.navigation.compose.composable +import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions +import com.bitwarden.ui.platform.base.util.composableWithStayTransitions import kotlinx.serialization.Serializable /** @@ -22,7 +23,7 @@ data object SettingsTutorialRoute * Add the top level Tutorial screen to the nav graph. */ fun NavGraphBuilder.tutorialDestination(onTutorialFinished: () -> Unit) { - composable { + composableWithStayTransitions { TutorialScreen( onTutorialFinished = onTutorialFinished, ) @@ -33,7 +34,7 @@ fun NavGraphBuilder.tutorialDestination(onTutorialFinished: () -> Unit) { * Add the Settings Tutorial screen to the nav graph. */ fun NavGraphBuilder.tutorialSettingsDestination(onTutorialFinished: () -> Unit) { - composable { + composableWithSlideTransitions { TutorialScreen( onTutorialFinished = onTutorialFinished, ) diff --git a/authenticator/src/main/res/values/styles.xml b/authenticator/src/main/res/values/styles.xml index ef420df315..77b270d8b4 100644 --- a/authenticator/src/main/res/values/styles.xml +++ b/authenticator/src/main/res/values/styles.xml @@ -7,6 +7,8 @@ true true default + @dimen/dialogDimBackgroundAmount + @android:color/transparent diff --git a/ui/src/main/kotlin/com/bitwarden/ui/platform/base/util/NavControllerExtensions.kt b/ui/src/main/kotlin/com/bitwarden/ui/platform/base/util/NavControllerExtensions.kt new file mode 100644 index 0000000000..951c9e45dc --- /dev/null +++ b/ui/src/main/kotlin/com/bitwarden/ui/platform/base/util/NavControllerExtensions.kt @@ -0,0 +1,42 @@ +package com.bitwarden.ui.platform.base.util + +import androidx.navigation.NavController +import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.NavOptions +import androidx.navigation.navOptions +import com.bitwarden.ui.platform.components.navigation.model.NavigationItem +import com.bitwarden.ui.platform.util.toObjectNavigationRoute + +/** + * A helper function to determine how to navigate to a specified [NavigationItem]. + * + * This function intelligently handles navigation based on the current destination: + * - If already at the target start destination, no action is taken. + * - If in the correct graph but not at start, pops back to start destination of the graph. + * - Otherwise, navigates to the target graph with appropriate [NavOptions]. + * + * @param target The [NavigationItem] representing the desired navigation target + */ +fun NavController.navigateToTabOrRoot(target: NavigationItem) { + if (target.startDestinationRoute.toObjectNavigationRoute() == currentDestination?.route) { + // We are at the start destination already, so nothing to do. + return + } else if (target.graphRoute.toObjectNavigationRoute() == currentDestination?.parent?.route) { + // We are not at the start destination but we are in the correct graph, + // so lets pop up to the start destination. + popBackStack(route = target.startDestinationRoute, inclusive = false) + return + } else { + // We are not in correct graph at all, so navigate there. + navigate( + route = target.graphRoute, + navOptions = navOptions { + popUpTo(id = graph.findStartDestination().id) { + saveState = true + } + launchSingleTop = true + restoreState = true + }, + ) + } +} diff --git a/ui/src/main/res/values-night/values.xml b/ui/src/main/res/values-night/values.xml new file mode 100644 index 0000000000..51650dd24e --- /dev/null +++ b/ui/src/main/res/values-night/values.xml @@ -0,0 +1,4 @@ + + + 0.75 + diff --git a/ui/src/main/res/values/values.xml b/ui/src/main/res/values/values.xml new file mode 100644 index 0000000000..22def3c299 --- /dev/null +++ b/ui/src/main/res/values/values.xml @@ -0,0 +1,4 @@ + + + 0.55 +