mirror of
https://github.com/bitwarden/android.git
synced 2026-04-28 11:58:40 -05:00
PM-21348: Type-safe navigation for authenticator (#5156)
This commit is contained in:
@@ -4,8 +4,13 @@ import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
const val UNLOCK_ROUTE: String = "unlock"
|
||||
/**
|
||||
* The type-safe route for the unlock screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object UnlockRoute
|
||||
|
||||
/**
|
||||
* Navigate to the unlock screen.
|
||||
@@ -13,7 +18,7 @@ const val UNLOCK_ROUTE: String = "unlock"
|
||||
fun NavController.navigateToUnlock(
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
navigate(route = UNLOCK_ROUTE, navOptions = navOptions)
|
||||
navigate(route = UnlockRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,7 +27,7 @@ fun NavController.navigateToUnlock(
|
||||
fun NavGraphBuilder.unlockDestination(
|
||||
onUnlocked: () -> Unit,
|
||||
) {
|
||||
composable(route = UNLOCK_ROUTE) {
|
||||
composable<UnlockRoute> {
|
||||
UnlockScreen(onUnlocked = onUnlocked)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,26 @@ import androidx.navigation.navigation
|
||||
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.navigateToManualCodeEntryScreen
|
||||
import com.bitwarden.authenticator.ui.authenticator.feature.navbar.AUTHENTICATOR_NAV_BAR_ROUTE
|
||||
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.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 kotlinx.serialization.Serializable
|
||||
|
||||
const val AUTHENTICATOR_GRAPH_ROUTE = "authenticator_graph"
|
||||
/**
|
||||
* The type-safe route for the authenticator graph.
|
||||
*/
|
||||
@Serializable
|
||||
data object AuthenticatorGraphRoute
|
||||
|
||||
/**
|
||||
* Navigate to the authenticator graph
|
||||
*/
|
||||
fun NavController.navigateToAuthenticatorGraph(navOptions: NavOptions? = null) {
|
||||
navigate(AUTHENTICATOR_NAV_BAR_ROUTE, navOptions)
|
||||
navigate(route = AuthenticatorNavbarRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,9 +36,8 @@ fun NavGraphBuilder.authenticatorGraph(
|
||||
navController: NavController,
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
navigation(
|
||||
startDestination = AUTHENTICATOR_NAV_BAR_ROUTE,
|
||||
route = AUTHENTICATOR_GRAPH_ROUTE,
|
||||
navigation<AuthenticatorGraphRoute>(
|
||||
startDestination = AuthenticatorNavbarRoute,
|
||||
) {
|
||||
authenticatorNavBarDestination(
|
||||
onNavigateBack = onNavigateBack,
|
||||
|
||||
@@ -4,23 +4,31 @@ import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.navArgument
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithPushTransitions
|
||||
import androidx.navigation.toRoute
|
||||
import com.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
private const val EDIT_ITEM_PREFIX = "edit_item"
|
||||
private const val EDIT_ITEM_ITEM_ID = "item_id"
|
||||
private const val EDIT_ITEM_ROUTE = "$EDIT_ITEM_PREFIX/{$EDIT_ITEM_ITEM_ID}"
|
||||
/**
|
||||
* The type-safe route for the edit item screen.
|
||||
*/
|
||||
@Serializable
|
||||
data class EditItemRoute(
|
||||
val itemId: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Class to retrieve authenticator item arguments from the [SavedStateHandle].
|
||||
*
|
||||
* @property itemId ID of the item to be edited.
|
||||
*/
|
||||
data class EditItemArgs(val itemId: String) {
|
||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||
checkNotNull(savedStateHandle[EDIT_ITEM_ITEM_ID]) as String,
|
||||
)
|
||||
data class EditItemArgs(val itemId: String)
|
||||
|
||||
/**
|
||||
* Constructs a [EditItemArgs] from the [SavedStateHandle] and internal route data.
|
||||
*/
|
||||
fun SavedStateHandle.toEditItemArgs(): EditItemArgs {
|
||||
val route = this.toRoute<EditItemRoute>()
|
||||
return EditItemArgs(itemId = route.itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,12 +37,7 @@ data class EditItemArgs(val itemId: String) {
|
||||
fun NavGraphBuilder.editItemDestination(
|
||||
onNavigateBack: () -> Unit = { },
|
||||
) {
|
||||
composableWithPushTransitions(
|
||||
route = EDIT_ITEM_ROUTE,
|
||||
arguments = listOf(
|
||||
navArgument(EDIT_ITEM_ITEM_ID) { type = NavType.StringType },
|
||||
),
|
||||
) {
|
||||
composableWithPushTransitions<EditItemRoute> {
|
||||
EditItemScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -49,7 +52,7 @@ fun NavController.navigateToEditItem(
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
navigate(
|
||||
route = "$EDIT_ITEM_PREFIX/$itemId",
|
||||
route = EditItemRoute(itemId = itemId),
|
||||
navOptions = navOptions,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ import com.bitwarden.authenticator.ui.platform.components.model.IconData
|
||||
import com.bitwarden.authenticator.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.bitwarden.authenticator.ui.platform.components.stepper.BitwardenStepper
|
||||
import com.bitwarden.authenticator.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.bitwarden.authenticator.ui.platform.theme.DEFAULT_FADE_TRANSITION_TIME_MS
|
||||
import com.bitwarden.authenticator.ui.platform.theme.DEFAULT_STAY_TRANSITION_TIME_MS
|
||||
import com.bitwarden.ui.platform.theme.DEFAULT_FADE_TRANSITION_TIME_MS
|
||||
import com.bitwarden.ui.platform.theme.DEFAULT_STAY_TRANSITION_TIME_MS
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,7 @@ class EditItemViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : BaseViewModel<EditItemState, EditItemEvent, EditItemAction>(
|
||||
initialState = savedStateHandle[KEY_STATE] ?: EditItemState(
|
||||
itemId = EditItemArgs(savedStateHandle).itemId,
|
||||
itemId = savedStateHandle.toEditItemArgs().itemId,
|
||||
viewState = EditItemState.ViewState.Loading,
|
||||
dialog = null,
|
||||
),
|
||||
|
||||
@@ -11,8 +11,13 @@ import com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.navigateT
|
||||
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
|
||||
|
||||
const val ITEM_LISTING_GRAPH_ROUTE = "item_listing_graph"
|
||||
/**
|
||||
* The type-safe route for the item listing graph.
|
||||
*/
|
||||
@Serializable
|
||||
data object ItemListingGraphRoute
|
||||
|
||||
/**
|
||||
* Add the item listing graph to the nav graph.
|
||||
@@ -29,9 +34,8 @@ fun NavGraphBuilder.itemListingGraph(
|
||||
navigateToImport: () -> Unit,
|
||||
navigateToTutorial: () -> Unit,
|
||||
) {
|
||||
navigation(
|
||||
route = ITEM_LISTING_GRAPH_ROUTE,
|
||||
startDestination = ITEM_LIST_ROUTE,
|
||||
navigation<ItemListingGraphRoute>(
|
||||
startDestination = ItemListingRoute,
|
||||
) {
|
||||
itemListingDestination(
|
||||
onNavigateBack = navigateBack,
|
||||
@@ -76,7 +80,7 @@ fun NavController.navigateToItemListGraph(
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
navigate(
|
||||
route = ITEM_LISTING_GRAPH_ROUTE,
|
||||
route = ItemListingGraphRoute,
|
||||
navOptions = navOptions,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package com.bitwarden.authenticator.ui.authenticator.feature.itemlisting
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithPushTransitions
|
||||
import com.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
const val ITEM_LIST_ROUTE = "item_list"
|
||||
/**
|
||||
* The type-safe route for the item listing screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object ItemListingRoute
|
||||
|
||||
/**
|
||||
* Add the item listing screen to the nav graph.
|
||||
@@ -15,9 +20,7 @@ fun NavGraphBuilder.itemListingDestination(
|
||||
onNavigateToManualKeyEntry: () -> Unit = { },
|
||||
onNavigateToEditItemScreen: (id: String) -> Unit = { },
|
||||
) {
|
||||
composableWithPushTransitions(
|
||||
route = ITEM_LIST_ROUTE,
|
||||
) {
|
||||
composableWithPushTransitions<ItemListingRoute> {
|
||||
ItemListingScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToSearch = onNavigateToSearch,
|
||||
|
||||
@@ -3,9 +3,14 @@ package com.bitwarden.authenticator.ui.authenticator.feature.manualcodeentry
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithSlideTransitions
|
||||
import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
private const val MANUAL_CODE_ENTRY_ROUTE: String = "manual_code_entry"
|
||||
/**
|
||||
* The type-safe route for the manual code entry screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object ManualCodeEntryRoute
|
||||
|
||||
/**
|
||||
* Add the manual code entry screen to the nav graph.
|
||||
@@ -14,9 +19,7 @@ fun NavGraphBuilder.manualCodeEntryDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToQrCodeScreen: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = MANUAL_CODE_ENTRY_ROUTE,
|
||||
) {
|
||||
composableWithSlideTransitions<ManualCodeEntryRoute> {
|
||||
ManualCodeEntryScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToQrCodeScreen = onNavigateToQrCodeScreen,
|
||||
@@ -30,5 +33,5 @@ fun NavGraphBuilder.manualCodeEntryDestination(
|
||||
fun NavController.navigateToManualCodeEntryScreen(
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
this.navigate(MANUAL_CODE_ENTRY_ROUTE, navOptions)
|
||||
this.navigate(route = ManualCodeEntryRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package com.bitwarden.authenticator.ui.authenticator.feature.navbar
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithStayTransitions
|
||||
import com.bitwarden.ui.platform.base.util.composableWithStayTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
const val AUTHENTICATOR_NAV_BAR_ROUTE: String = "AuthenticatorNavBarRoute"
|
||||
/**
|
||||
* The type-safe route for the authenticator navbar screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object AuthenticatorNavbarRoute
|
||||
|
||||
/**
|
||||
* Add the authenticator nav bar to the nav graph.
|
||||
@@ -19,9 +24,7 @@ fun NavGraphBuilder.authenticatorNavBarDestination(
|
||||
onNavigateToImport: () -> Unit,
|
||||
onNavigateToTutorial: () -> Unit,
|
||||
) {
|
||||
composableWithStayTransitions(
|
||||
route = AUTHENTICATOR_NAV_BAR_ROUTE,
|
||||
) {
|
||||
composableWithStayTransitions<AuthenticatorNavbarRoute> {
|
||||
AuthenticatorNavBarScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToSearch = onNavigateToSearch,
|
||||
|
||||
@@ -43,8 +43,8 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navOptions
|
||||
import com.bitwarden.authenticator.R
|
||||
import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.ITEM_LISTING_GRAPH_ROUTE
|
||||
import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.ITEM_LIST_ROUTE
|
||||
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.base.util.EventsEffect
|
||||
@@ -52,9 +52,10 @@ import com.bitwarden.authenticator.ui.platform.base.util.max
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.toDp
|
||||
import com.bitwarden.authenticator.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.bitwarden.authenticator.ui.platform.components.scrim.BitwardenAnimatedScrim
|
||||
import com.bitwarden.authenticator.ui.platform.feature.settings.SETTINGS_GRAPH_ROUTE
|
||||
import com.bitwarden.authenticator.ui.platform.feature.settings.SettingsGraphRoute
|
||||
import com.bitwarden.authenticator.ui.platform.feature.settings.navigateToSettingsGraph
|
||||
import com.bitwarden.authenticator.ui.platform.theme.RootTransitionProviders
|
||||
import com.bitwarden.ui.platform.theme.RootTransitionProviders
|
||||
import com.bitwarden.ui.platform.util.toObjectNavigationRoute
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@@ -161,7 +162,7 @@ private fun AuthenticatorNavBarScaffold(
|
||||
) { innerPadding ->
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = ITEM_LISTING_GRAPH_ROUTE,
|
||||
startDestination = ItemListingGraphRoute,
|
||||
modifier = Modifier
|
||||
.consumeWindowInsets(WindowInsets.navigationBars)
|
||||
.consumeWindowInsets(WindowInsets.ime)
|
||||
@@ -309,7 +310,7 @@ private sealed class AuthenticatorNavBarTab : Parcelable {
|
||||
override val iconRes get() = R.drawable.ic_verification_codes
|
||||
override val labelRes get() = R.string.verification_codes
|
||||
override val contentDescriptionRes get() = R.string.verification_codes
|
||||
override val route get() = ITEM_LIST_ROUTE
|
||||
override val route get() = ItemListingRoute.toObjectNavigationRoute()
|
||||
override val testTag get() = "VerificationCodesTab"
|
||||
}
|
||||
|
||||
@@ -322,9 +323,7 @@ private sealed class AuthenticatorNavBarTab : Parcelable {
|
||||
override val iconRes get() = R.drawable.ic_settings
|
||||
override val labelRes get() = R.string.settings
|
||||
override val contentDescriptionRes get() = R.string.settings
|
||||
|
||||
// TODO: Replace with constant when settings screen is complete.
|
||||
override val route get() = SETTINGS_GRAPH_ROUTE
|
||||
override val route get() = SettingsGraphRoute.toObjectNavigationRoute()
|
||||
override val testTag get() = "SettingsTab"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,14 @@ package com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithSlideTransitions
|
||||
import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
private const val QR_CODE_SCAN_ROUTE: String = "qr_code_scan"
|
||||
/**
|
||||
* The type-safe route for the QR code scan screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object QrCodeScanRoute
|
||||
|
||||
/**
|
||||
* Add the QR code scan screen to the nav graph.
|
||||
@@ -14,9 +19,7 @@ fun NavGraphBuilder.qrCodeScanDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToManualCodeEntryScreen: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = QR_CODE_SCAN_ROUTE,
|
||||
) {
|
||||
composableWithSlideTransitions<QrCodeScanRoute> {
|
||||
QrCodeScanScreen(
|
||||
onNavigateToManualCodeEntryScreen = onNavigateToManualCodeEntryScreen,
|
||||
onNavigateBack = onNavigateBack,
|
||||
@@ -30,5 +33,5 @@ fun NavGraphBuilder.qrCodeScanDestination(
|
||||
fun NavController.navigateToQrCodeScanScreen(
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
this.navigate(QR_CODE_SCAN_ROUTE, navOptions)
|
||||
this.navigate(route = QrCodeScanRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,14 @@ package com.bitwarden.authenticator.ui.authenticator.feature.search
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithSlideTransitions
|
||||
import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
const val ITEM_SEARCH_ROUTE = "item_search"
|
||||
/**
|
||||
* The type-safe route for the item search screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object ItemSearchRoute
|
||||
|
||||
/**
|
||||
* Add item search destination to the nav graph.
|
||||
@@ -12,9 +17,7 @@ const val ITEM_SEARCH_ROUTE = "item_search"
|
||||
fun NavGraphBuilder.itemSearchDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = ITEM_SEARCH_ROUTE,
|
||||
) {
|
||||
composableWithSlideTransitions<ItemSearchRoute> {
|
||||
ItemSearchScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -25,5 +28,5 @@ fun NavGraphBuilder.itemSearchDestination(
|
||||
* Navigate to the item search screen.
|
||||
*/
|
||||
fun NavController.navigateToSearch() {
|
||||
navigate(route = ITEM_SEARCH_ROUTE)
|
||||
navigate(route = ItemSearchRoute)
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package com.bitwarden.authenticator.ui.platform.base.util
|
||||
|
||||
import androidx.compose.animation.AnimatedContentScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NamedNavArgument
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavDeepLink
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import com.bitwarden.authenticator.ui.platform.theme.TransitionProviders
|
||||
|
||||
/**
|
||||
* A wrapper around [NavGraphBuilder.composable] that supplies slide up/down transitions.
|
||||
*/
|
||||
fun NavGraphBuilder.composableWithSlideTransitions(
|
||||
route: String,
|
||||
arguments: List<NamedNavArgument> = emptyList(),
|
||||
deepLinks: List<NavDeepLink> = emptyList(),
|
||||
content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit,
|
||||
) {
|
||||
this.composable(
|
||||
route = route,
|
||||
arguments = arguments,
|
||||
deepLinks = deepLinks,
|
||||
enterTransition = TransitionProviders.Enter.slideUp,
|
||||
exitTransition = TransitionProviders.Exit.stay,
|
||||
popEnterTransition = TransitionProviders.Enter.stay,
|
||||
popExitTransition = TransitionProviders.Exit.slideDown,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around [NavGraphBuilder.composable] that supplies "stay" transitions.
|
||||
*/
|
||||
fun NavGraphBuilder.composableWithStayTransitions(
|
||||
route: String,
|
||||
arguments: List<NamedNavArgument> = emptyList(),
|
||||
deepLinks: List<NavDeepLink> = emptyList(),
|
||||
content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit,
|
||||
) {
|
||||
this.composable(
|
||||
route = route,
|
||||
arguments = arguments,
|
||||
deepLinks = deepLinks,
|
||||
enterTransition = TransitionProviders.Enter.stay,
|
||||
exitTransition = TransitionProviders.Exit.stay,
|
||||
popEnterTransition = TransitionProviders.Enter.stay,
|
||||
popExitTransition = TransitionProviders.Exit.stay,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around [NavGraphBuilder.composable] that supplies push transitions.
|
||||
*
|
||||
* This is suitable for screens deeper within a hierarchy that uses push transitions; the root
|
||||
* screen of such a hierarchy should use [composableWithRootPushTransitions].
|
||||
*/
|
||||
fun NavGraphBuilder.composableWithPushTransitions(
|
||||
route: String,
|
||||
arguments: List<NamedNavArgument> = emptyList(),
|
||||
deepLinks: List<NavDeepLink> = emptyList(),
|
||||
content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit,
|
||||
) {
|
||||
this.composable(
|
||||
route = route,
|
||||
arguments = arguments,
|
||||
deepLinks = deepLinks,
|
||||
enterTransition = TransitionProviders.Enter.pushLeft,
|
||||
exitTransition = TransitionProviders.Exit.stay,
|
||||
popEnterTransition = TransitionProviders.Enter.stay,
|
||||
popExitTransition = TransitionProviders.Exit.pushRight,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around [NavGraphBuilder.composable] that supplies push transitions to the root screen
|
||||
* in a nested graph that uses push transitions.
|
||||
*/
|
||||
fun NavGraphBuilder.composableWithRootPushTransitions(
|
||||
route: String,
|
||||
arguments: List<NamedNavArgument> = emptyList(),
|
||||
deepLinks: List<NavDeepLink> = emptyList(),
|
||||
content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit,
|
||||
) {
|
||||
this.composable(
|
||||
route = route,
|
||||
arguments = arguments,
|
||||
deepLinks = deepLinks,
|
||||
enterTransition = TransitionProviders.Enter.stay,
|
||||
exitTransition = TransitionProviders.Exit.pushLeft,
|
||||
popEnterTransition = TransitionProviders.Enter.pushRight,
|
||||
popExitTransition = TransitionProviders.Exit.fadeOut,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
@@ -2,15 +2,20 @@ package com.bitwarden.authenticator.ui.platform.feature.debugmenu
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithPushTransitions
|
||||
import com.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
private const val DEBUG_MENU = "debug_menu"
|
||||
/**
|
||||
* The type-safe route for the debug screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object DebugRoute
|
||||
|
||||
/**
|
||||
* Navigate to the setup unlock screen.
|
||||
*/
|
||||
fun NavController.navigateToDebugMenuScreen() {
|
||||
this.navigate(DEBUG_MENU) {
|
||||
this.navigate(route = DebugRoute) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
@@ -21,9 +26,7 @@ fun NavController.navigateToDebugMenuScreen() {
|
||||
fun NavGraphBuilder.setupDebugMenuDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions(
|
||||
route = DEBUG_MENU,
|
||||
) {
|
||||
composableWithPushTransitions<DebugRoute> {
|
||||
DebugMenuScreen(onNavigateBack = onNavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,22 +13,23 @@ import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navOptions
|
||||
import com.bitwarden.authenticator.ui.auth.unlock.UNLOCK_ROUTE
|
||||
import com.bitwarden.authenticator.ui.auth.unlock.UnlockRoute
|
||||
import com.bitwarden.authenticator.ui.auth.unlock.navigateToUnlock
|
||||
import com.bitwarden.authenticator.ui.auth.unlock.unlockDestination
|
||||
import com.bitwarden.authenticator.ui.authenticator.feature.authenticator.AUTHENTICATOR_GRAPH_ROUTE
|
||||
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.SPLASH_ROUTE
|
||||
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
|
||||
import com.bitwarden.authenticator.ui.platform.feature.tutorial.TUTORIAL_ROUTE
|
||||
import com.bitwarden.authenticator.ui.platform.feature.tutorial.TutorialRoute
|
||||
import com.bitwarden.authenticator.ui.platform.feature.tutorial.navigateToTutorial
|
||||
import com.bitwarden.authenticator.ui.platform.feature.tutorial.tutorialDestination
|
||||
import com.bitwarden.authenticator.ui.platform.theme.NonNullEnterTransitionProvider
|
||||
import com.bitwarden.authenticator.ui.platform.theme.NonNullExitTransitionProvider
|
||||
import com.bitwarden.authenticator.ui.platform.theme.RootTransitionProviders
|
||||
import com.bitwarden.ui.platform.theme.NonNullEnterTransitionProvider
|
||||
import com.bitwarden.ui.platform.theme.NonNullExitTransitionProvider
|
||||
import com.bitwarden.ui.platform.theme.RootTransitionProviders
|
||||
import com.bitwarden.ui.platform.util.toObjectNavigationRoute
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
@@ -64,7 +65,7 @@ fun RootNavScreen(
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = SPLASH_ROUTE,
|
||||
startDestination = SplashRoute,
|
||||
enterTransition = { toEnterTransition()(this) },
|
||||
exitTransition = { toExitTransition()(this) },
|
||||
popEnterTransition = { toEnterTransition()(this) },
|
||||
@@ -93,10 +94,10 @@ fun RootNavScreen(
|
||||
}
|
||||
|
||||
val targetRoute = when (state.navState) {
|
||||
RootNavState.NavState.Splash -> SPLASH_ROUTE
|
||||
RootNavState.NavState.Locked -> UNLOCK_ROUTE
|
||||
RootNavState.NavState.Tutorial -> TUTORIAL_ROUTE
|
||||
RootNavState.NavState.Unlocked -> AUTHENTICATOR_GRAPH_ROUTE
|
||||
RootNavState.NavState.Splash -> SplashRoute
|
||||
RootNavState.NavState.Locked -> UnlockRoute
|
||||
RootNavState.NavState.Tutorial -> TutorialRoute
|
||||
RootNavState.NavState.Unlocked -> AuthenticatorGraphRoute
|
||||
}
|
||||
|
||||
val currentRoute = navController.currentDestination?.rootLevelRoute()
|
||||
@@ -104,7 +105,9 @@ fun RootNavScreen(
|
||||
// death. In this case, the NavHost already restores state, so we don't have to navigate.
|
||||
// However, if the route is correct but the underlying state is different, we should still
|
||||
// proceed in order to get a fresh version of that route.
|
||||
if (currentRoute == targetRoute && previousStateReference.get() == state) {
|
||||
if (currentRoute == targetRoute.toObjectNavigationRoute() &&
|
||||
previousStateReference.get() == state
|
||||
) {
|
||||
previousStateReference.set(state)
|
||||
return
|
||||
}
|
||||
@@ -167,7 +170,7 @@ private fun AnimatedContentTransitionScope<NavBackStackEntry>.toEnterTransition(
|
||||
when (targetState.destination.rootLevelRoute()) {
|
||||
else -> when (initialState.destination.rootLevelRoute()) {
|
||||
// Disable transitions when coming from the splash screen
|
||||
SPLASH_ROUTE -> RootTransitionProviders.Enter.none
|
||||
SplashRoute.toObjectNavigationRoute() -> RootTransitionProviders.Enter.none
|
||||
else -> RootTransitionProviders.Enter.fadeIn
|
||||
}
|
||||
}
|
||||
@@ -179,7 +182,7 @@ private fun AnimatedContentTransitionScope<NavBackStackEntry>.toEnterTransition(
|
||||
private fun AnimatedContentTransitionScope<NavBackStackEntry>.toExitTransition(): NonNullExitTransitionProvider =
|
||||
when (initialState.destination.rootLevelRoute()) {
|
||||
// Disable transitions when coming from the splash screen
|
||||
SPLASH_ROUTE -> RootTransitionProviders.Exit.none
|
||||
SplashRoute.toObjectNavigationRoute() -> RootTransitionProviders.Exit.none
|
||||
else -> when (targetState.destination.rootLevelRoute()) {
|
||||
else -> RootTransitionProviders.Exit.fadeOut
|
||||
}
|
||||
|
||||
@@ -4,13 +4,23 @@ import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navigation
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithRootPushTransitions
|
||||
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
|
||||
|
||||
const val SETTINGS_GRAPH_ROUTE = "settings_graph"
|
||||
private const val SETTINGS_ROUTE = "settings"
|
||||
/**
|
||||
* The type-safe route for the settings graph.
|
||||
*/
|
||||
@Serializable
|
||||
data object SettingsGraphRoute
|
||||
|
||||
/**
|
||||
* The type-safe route for the settings screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object SettingsRoute
|
||||
|
||||
/**
|
||||
* Add settings graph to the nav graph.
|
||||
@@ -21,13 +31,10 @@ fun NavGraphBuilder.settingsGraph(
|
||||
onNavigateToImport: () -> Unit,
|
||||
onNavigateToTutorial: () -> Unit,
|
||||
) {
|
||||
navigation(
|
||||
startDestination = SETTINGS_ROUTE,
|
||||
route = SETTINGS_GRAPH_ROUTE,
|
||||
navigation<SettingsGraphRoute>(
|
||||
startDestination = SettingsRoute,
|
||||
) {
|
||||
composableWithRootPushTransitions(
|
||||
route = SETTINGS_ROUTE,
|
||||
) {
|
||||
composableWithRootPushTransitions<SettingsRoute> {
|
||||
SettingsScreen(
|
||||
onNavigateToTutorial = onNavigateToTutorial,
|
||||
onNavigateToExport = onNavigateToExport,
|
||||
@@ -50,5 +57,5 @@ fun NavGraphBuilder.settingsGraph(
|
||||
* Navigate to the settings screen.
|
||||
*/
|
||||
fun NavController.navigateToSettingsGraph(navOptions: NavOptions? = null) {
|
||||
navigate(SETTINGS_GRAPH_ROUTE, navOptions)
|
||||
navigate(route = SettingsGraphRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ package com.bitwarden.authenticator.ui.platform.feature.settings.export
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithSlideTransitions
|
||||
import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Route for the export data screen.
|
||||
* The type-safe route for the export screen.
|
||||
*/
|
||||
const val EXPORT_ROUTE = "export"
|
||||
@Serializable
|
||||
data object ExportRoute
|
||||
|
||||
/**
|
||||
* Add the export data destination to the nav graph.
|
||||
@@ -16,7 +18,7 @@ const val EXPORT_ROUTE = "export"
|
||||
fun NavGraphBuilder.exportDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(EXPORT_ROUTE) {
|
||||
composableWithSlideTransitions<ExportRoute> {
|
||||
ExportScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -27,5 +29,5 @@ fun NavGraphBuilder.exportDestination(
|
||||
* Navigate to the export data screen.
|
||||
*/
|
||||
fun NavController.navigateToExport(navOptions: NavOptions? = null) {
|
||||
navigate(EXPORT_ROUTE, navOptions)
|
||||
navigate(route = ExportRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,14 @@ package com.bitwarden.authenticator.ui.platform.feature.settings.importing
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.composableWithSlideTransitions
|
||||
import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
const val IMPORT_ROUTE = "importing"
|
||||
/**
|
||||
* The type-safe route for the import screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object ImportRoute
|
||||
|
||||
/**
|
||||
* Add the import screen to the nav graph.
|
||||
@@ -13,7 +18,7 @@ const val IMPORT_ROUTE = "importing"
|
||||
fun NavGraphBuilder.importingDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(IMPORT_ROUTE) {
|
||||
composableWithSlideTransitions<ImportRoute> {
|
||||
ImportingScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
@@ -24,5 +29,5 @@ fun NavGraphBuilder.importingDestination(
|
||||
* Navigate to the Import destination.
|
||||
*/
|
||||
fun NavController.navigateToImporting(navOptions: NavOptions? = null) {
|
||||
navigate(IMPORT_ROUTE, navOptions)
|
||||
navigate(route = ImportRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,19 @@ import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
const val SPLASH_ROUTE: String = "splash"
|
||||
/**
|
||||
* The type-safe route for the splash screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object SplashRoute
|
||||
|
||||
/**
|
||||
* Add splash destinations to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.splashDestination() {
|
||||
composable(SPLASH_ROUTE) { SplashScreen() }
|
||||
composable<SplashRoute> { SplashScreen() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,5 +25,5 @@ fun NavGraphBuilder.splashDestination() {
|
||||
fun NavController.navigateToSplash(
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
navigate(SPLASH_ROUTE, navOptions)
|
||||
navigate(route = SplashRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
@@ -4,15 +4,25 @@ import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
const val TUTORIAL_ROUTE = "tutorial"
|
||||
const val SETTINGS_TUTORIAL_ROUTE = "settings/tutorial"
|
||||
/**
|
||||
* The type-safe route for the tutorial screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object TutorialRoute
|
||||
|
||||
/**
|
||||
* The type-safe route for the settings tutorial screen.
|
||||
*/
|
||||
@Serializable
|
||||
data object SettingsTutorialRoute
|
||||
|
||||
/**
|
||||
* Add the top level Tutorial screen to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.tutorialDestination(onTutorialFinished: () -> Unit) {
|
||||
composable(TUTORIAL_ROUTE) {
|
||||
composable<TutorialRoute> {
|
||||
TutorialScreen(
|
||||
onTutorialFinished = onTutorialFinished,
|
||||
)
|
||||
@@ -23,7 +33,7 @@ fun NavGraphBuilder.tutorialDestination(onTutorialFinished: () -> Unit) {
|
||||
* Add the Settings Tutorial screen to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.tutorialSettingsDestination(onTutorialFinished: () -> Unit) {
|
||||
composable(SETTINGS_TUTORIAL_ROUTE) {
|
||||
composable<SettingsTutorialRoute> {
|
||||
TutorialScreen(
|
||||
onTutorialFinished = onTutorialFinished,
|
||||
)
|
||||
@@ -34,12 +44,12 @@ fun NavGraphBuilder.tutorialSettingsDestination(onTutorialFinished: () -> Unit)
|
||||
* Navigate to the top level Tutorial screen.
|
||||
*/
|
||||
fun NavController.navigateToTutorial(navOptions: NavOptions? = null) {
|
||||
navigate(route = TUTORIAL_ROUTE, navOptions = navOptions)
|
||||
navigate(route = TutorialRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the Tutorial screen within Settings.
|
||||
*/
|
||||
fun NavController.navigateToSettingsTutorial(navOptions: NavOptions? = null) {
|
||||
navigate(route = SETTINGS_TUTORIAL_ROUTE, navOptions)
|
||||
navigate(route = SettingsTutorialRoute, navOptions = navOptions)
|
||||
}
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
package com.bitwarden.authenticator.ui.platform.theme
|
||||
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.compose.NavHost
|
||||
import com.bitwarden.authenticator.ui.platform.theme.RootTransitionProviders.Exit.stay
|
||||
|
||||
typealias EnterTransitionProvider =
|
||||
(@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)
|
||||
|
||||
typealias ExitTransitionProvider =
|
||||
(@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition?)
|
||||
|
||||
typealias NonNullEnterTransitionProvider =
|
||||
(@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition)
|
||||
|
||||
typealias NonNullExitTransitionProvider =
|
||||
(@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition)
|
||||
|
||||
/**
|
||||
* The default transition time (in milliseconds) for all fade transitions in the
|
||||
* [TransitionProviders].
|
||||
*/
|
||||
const val DEFAULT_FADE_TRANSITION_TIME_MS: Int = 300
|
||||
|
||||
/**
|
||||
* The default transition time (in milliseconds) for all slide transitions in the
|
||||
* [TransitionProviders].
|
||||
*/
|
||||
const val DEFAULT_SLIDE_TRANSITION_TIME_MS: Int = 450
|
||||
|
||||
/**
|
||||
* The default transition time (in milliseconds) for all slide transitions in the
|
||||
* [TransitionProviders].
|
||||
*/
|
||||
const val DEFAULT_PUSH_TRANSITION_TIME_MS: Int = 350
|
||||
|
||||
/**
|
||||
* The default transition time (in milliseconds) for all "stay"/no-op transitions in the
|
||||
* [TransitionProviders].
|
||||
*
|
||||
* This should be at least as large as any other transition that might also be happening during a
|
||||
* navigation.
|
||||
*/
|
||||
val DEFAULT_STAY_TRANSITION_TIME_MS: Int =
|
||||
maxOf(
|
||||
DEFAULT_FADE_TRANSITION_TIME_MS,
|
||||
DEFAULT_SLIDE_TRANSITION_TIME_MS,
|
||||
DEFAULT_PUSH_TRANSITION_TIME_MS,
|
||||
)
|
||||
|
||||
/**
|
||||
* Checks if the parent of the destination before and after the navigation is the same. This is
|
||||
* useful to ignore certain enter/exit transitions when navigating between distinct, nested flows.
|
||||
*/
|
||||
val AnimatedContentTransitionScope<NavBackStackEntry>.isSameGraphNavigation: Boolean
|
||||
get() = initialState.destination.parent == targetState.destination.parent
|
||||
|
||||
/**
|
||||
* Contains standard "transition providers" that may be used to specify the [EnterTransition] and
|
||||
* [ExitTransition] used when building a typical composable destination. These may return `null`
|
||||
* values in order to allow transitions between nested navigation graphs to be specified by
|
||||
* components higher up in the graph.
|
||||
*/
|
||||
object TransitionProviders {
|
||||
/**
|
||||
* The standard set of "enter" transition providers.
|
||||
*/
|
||||
object Enter {
|
||||
/**
|
||||
* Fades the new screen in.
|
||||
*
|
||||
* Note that this represents a `null` transition when navigating between different nested
|
||||
* navigation graphs.
|
||||
*/
|
||||
val fadeIn: EnterTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Enter
|
||||
.fadeIn(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the new screen in from the left of the screen.
|
||||
*/
|
||||
val pushLeft: EnterTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Enter
|
||||
.pushLeft(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the new screen in from the right of the screen.
|
||||
*/
|
||||
val pushRight: EnterTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Enter
|
||||
.pushRight(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the new screen in from the bottom of the screen.
|
||||
*
|
||||
* Note that this represents a `null` transition when navigating between different nested
|
||||
* navigation graphs.
|
||||
*/
|
||||
val slideUp: EnterTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Enter
|
||||
.slideUp(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "no-op" transition: this changes nothing about the screen but "lasts" as long as
|
||||
* other standard transitions in order to leave the screen in place such that it does not
|
||||
* immediately appear while the other screen transitions away.
|
||||
*
|
||||
* Note that this represents a `null` transition when navigating between different nested
|
||||
* navigation graphs.
|
||||
*/
|
||||
val stay: EnterTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Enter
|
||||
.stay(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard set of "exit" transition providers.
|
||||
*/
|
||||
object Exit {
|
||||
/**
|
||||
* Fades the current screen out.
|
||||
*
|
||||
* Note that this represents a `null` transition when navigating between different nested
|
||||
* navigation graphs.
|
||||
*/
|
||||
val fadeOut: ExitTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Exit
|
||||
.fadeOut(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the current screen out to the left of the screen.
|
||||
*/
|
||||
val pushLeft: ExitTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Exit
|
||||
.pushLeft(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the current screen out to the right of the screen.
|
||||
*/
|
||||
val pushRight: ExitTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Exit
|
||||
.pushRight(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the current screen down to the bottom of the screen.
|
||||
*
|
||||
* Note that this represents a `null` transition when navigating between different nested
|
||||
* navigation graphs.
|
||||
*/
|
||||
val slideDown: ExitTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Exit
|
||||
.slideDown(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "no-op" transition: this changes nothing about the screen but "lasts" as long as
|
||||
* other standard transitions in order to leave the screen in place such that it does not
|
||||
* immediately disappear while the other screen transitions into place.
|
||||
*
|
||||
* Note that this represents a `null` transition when navigating between different nested
|
||||
* navigation graphs.
|
||||
*/
|
||||
val stay: ExitTransitionProvider = {
|
||||
RootTransitionProviders
|
||||
.Exit
|
||||
.stay(this)
|
||||
.takeIf { isSameGraphNavigation }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains standard "transition providers" that may be used to specify the [EnterTransition] and
|
||||
* [ExitTransition] used when building a root [NavHost], which requires a non-null value.
|
||||
*/
|
||||
object RootTransitionProviders {
|
||||
/**
|
||||
* The standard set of "enter" transition providers.
|
||||
*/
|
||||
object Enter {
|
||||
/**
|
||||
* Fades the new screen in.
|
||||
*/
|
||||
val fadeIn: NonNullEnterTransitionProvider = {
|
||||
fadeIn(tween(DEFAULT_FADE_TRANSITION_TIME_MS))
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no transition for the entering screen.
|
||||
*/
|
||||
val none: NonNullEnterTransitionProvider = {
|
||||
EnterTransition.None
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the new screen in from the left of the screen.
|
||||
*/
|
||||
val pushLeft: NonNullEnterTransitionProvider = {
|
||||
val totalTransitionDurationMs = DEFAULT_PUSH_TRANSITION_TIME_MS
|
||||
slideInHorizontally(
|
||||
animationSpec = tween(durationMillis = totalTransitionDurationMs),
|
||||
initialOffsetX = { fullWidth -> fullWidth / 2 },
|
||||
) + fadeIn(
|
||||
animationSpec = tween(
|
||||
durationMillis = totalTransitionDurationMs / 2,
|
||||
delayMillis = totalTransitionDurationMs / 2,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the new screen in from the right of the screen.
|
||||
*/
|
||||
val pushRight: NonNullEnterTransitionProvider = {
|
||||
val totalTransitionDurationMs = DEFAULT_PUSH_TRANSITION_TIME_MS
|
||||
slideInHorizontally(
|
||||
animationSpec = tween(durationMillis = totalTransitionDurationMs),
|
||||
initialOffsetX = { fullWidth -> -fullWidth / 2 },
|
||||
) + fadeIn(
|
||||
animationSpec = tween(
|
||||
durationMillis = totalTransitionDurationMs / 2,
|
||||
delayMillis = totalTransitionDurationMs / 2,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the new screen in from the bottom of the screen.
|
||||
*/
|
||||
val slideUp: NonNullEnterTransitionProvider = {
|
||||
slideIntoContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Up,
|
||||
animationSpec = tween(DEFAULT_SLIDE_TRANSITION_TIME_MS),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A "no-op" transition: this changes nothing about the screen but "lasts" as long as
|
||||
* other standard transitions in order to leave the screen in place such that it does not
|
||||
* immediately appear while the other screen transitions away.
|
||||
*/
|
||||
val stay: NonNullEnterTransitionProvider = {
|
||||
fadeIn(
|
||||
animationSpec = tween(DEFAULT_STAY_TRANSITION_TIME_MS),
|
||||
initialAlpha = 1f,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard set of "exit" transition providers.
|
||||
*/
|
||||
object Exit {
|
||||
/**
|
||||
* Fades the current screen out.
|
||||
*/
|
||||
val fadeOut: NonNullExitTransitionProvider = {
|
||||
fadeOut(tween(DEFAULT_FADE_TRANSITION_TIME_MS))
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no transition for the exiting screen.
|
||||
*
|
||||
* Unlike the [stay] transition, this will immediately remove the outgoing screen even if
|
||||
* there is an ongoing enter transition happening for the new screen.
|
||||
*/
|
||||
val none: NonNullExitTransitionProvider = {
|
||||
ExitTransition.None
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the current screen out to the left of the screen.
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
val pushLeft: NonNullExitTransitionProvider = {
|
||||
val totalTransitionDurationMs = DEFAULT_PUSH_TRANSITION_TIME_MS
|
||||
val delayMs = totalTransitionDurationMs / 7
|
||||
val slideWithoutDelayMs = totalTransitionDurationMs - delayMs
|
||||
slideOutHorizontally(
|
||||
animationSpec = tween(
|
||||
durationMillis = slideWithoutDelayMs,
|
||||
delayMillis = delayMs,
|
||||
),
|
||||
targetOffsetX = { fullWidth -> -fullWidth / 2 },
|
||||
) + fadeOut(
|
||||
animationSpec = tween(
|
||||
durationMillis = totalTransitionDurationMs / 2,
|
||||
delayMillis = delayMs,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the current screen out to the right of the screen.
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
val pushRight: NonNullExitTransitionProvider = {
|
||||
val totalTransitionDurationMs = DEFAULT_PUSH_TRANSITION_TIME_MS
|
||||
val delayMs = totalTransitionDurationMs / 7
|
||||
val slideWithoutDelayMs = totalTransitionDurationMs - delayMs
|
||||
slideOutHorizontally(
|
||||
animationSpec = tween(
|
||||
durationMillis = slideWithoutDelayMs,
|
||||
delayMillis = delayMs,
|
||||
),
|
||||
targetOffsetX = { fullWidth -> fullWidth / 2 },
|
||||
) + fadeOut(
|
||||
animationSpec = tween(
|
||||
durationMillis = totalTransitionDurationMs / 2,
|
||||
delayMillis = delayMs,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides the current screen down to the bottom of the screen.
|
||||
*/
|
||||
val slideDown: NonNullExitTransitionProvider = {
|
||||
slideOutOfContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Down,
|
||||
animationSpec = tween(DEFAULT_SLIDE_TRANSITION_TIME_MS),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A "no-op" transition: this changes nothing about the screen but "lasts" as long as
|
||||
* other standard transitions in order to leave the screen in place such that it does not
|
||||
* immediately disappear while the other screen transitions into place.
|
||||
*/
|
||||
val stay: NonNullExitTransitionProvider = {
|
||||
fadeOut(
|
||||
animationSpec = tween(DEFAULT_STAY_TRANSITION_TIME_MS),
|
||||
targetAlpha = 0.99f,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user