From 37c33fa24b2963ed4f72c3470a5cbbcb5868d266 Mon Sep 17 00:00:00 2001 From: stslex Date: Wed, 6 Sep 2023 09:00:22 +0300 Subject: [PATCH 1/3] init new navigation --- .../java/com/stslex93/notes/ui/AppInit.kt | 6 +- .../com/stslex93/notes/ui/MainActivity.kt | 4 +- .../com/stslex93/notes/ui/NavigationHost.kt | 15 ++-- core/navigation/build.gradle.kts | 1 + .../core/navigation/di/NavigationComponent.kt | 4 +- .../di/NavigationComponentBuilder.kt | 6 +- .../core/navigation/di/NavigationModule.kt | 2 +- .../core/navigation/model/NavigationScreen.kt | 78 ++++++++-------- .../navigation/navigator/NavigatorImpl.kt | 65 ++++++-------- .../notes/core/navigation/v2/NoteEditArgs.kt | 10 +++ .../navigation/v2/compose/ComposeGraph.kt | 89 +++++++++++++++++++ .../v2/controller/ComposeNavigator.kt | 18 ++++ .../v2/controller/NavExtrasHostController.kt | 84 +++++++++++++++++ .../navigation/v2/navigator/NavigatorImpl.kt | 30 +++++++ .../core/navigation/v2/screen/AppNavScreen.kt | 24 +++++ .../core/navigation/v2/screen/HostScreen.kt | 9 ++ .../notes/core/ui/base/NavigationScreen.kt | 23 +++++ .../stslex93/notes/core/ui/di/Navigator.kt | 9 +- .../com/stslex93/notes/core/ui/di/Screen.kt | 3 - .../feature/edit/ui/EditNoteViewModel.kt | 7 +- .../notes/feature/edit/ui/init/EditGraph.kt | 46 ++++++---- .../feature/home/navigation/HomeGraph.kt | 8 +- 22 files changed, 417 insertions(+), 124 deletions(-) create mode 100644 core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/NoteEditArgs.kt create mode 100644 core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt create mode 100644 core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/ComposeNavigator.kt create mode 100644 core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/NavExtrasHostController.kt create mode 100644 core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/navigator/NavigatorImpl.kt create mode 100644 core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/AppNavScreen.kt create mode 100644 core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/HostScreen.kt create mode 100644 core/ui/src/main/java/com/stslex93/notes/core/ui/base/NavigationScreen.kt delete mode 100644 core/ui/src/main/java/com/stslex93/notes/core/ui/di/Screen.kt diff --git a/app/src/main/java/com/stslex93/notes/ui/AppInit.kt b/app/src/main/java/com/stslex93/notes/ui/AppInit.kt index 0de90b6e..96529365 100644 --- a/app/src/main/java/com/stslex93/notes/ui/AppInit.kt +++ b/app/src/main/java/com/stslex93/notes/ui/AppInit.kt @@ -9,12 +9,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.navigation.NavHostController import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController @Composable fun AppInit( - navController: NavHostController, + navController: NavExtrasHostController, modifier: Modifier = Modifier ) { val systemUiController = rememberSystemUiController() @@ -34,7 +34,7 @@ fun AppInit( .background(MaterialTheme.colorScheme.background) ) { NavigationHost( - navHostController = navController, + navController = navController, modifier = modifier ) } diff --git a/app/src/main/java/com/stslex93/notes/ui/MainActivity.kt b/app/src/main/java/com/stslex93/notes/ui/MainActivity.kt index 7e8d6c65..56c5933c 100644 --- a/app/src/main/java/com/stslex93/notes/ui/MainActivity.kt +++ b/app/src/main/java/com/stslex93/notes/ui/MainActivity.kt @@ -4,8 +4,8 @@ import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat -import androidx.navigation.compose.rememberNavController import com.stslex93.notes.core.navigation.di.NavigationComponentBuilder +import com.stslex93.notes.core.navigation.v2.controller.rememberNavExtrasController import com.stslex93.notes.core.ui.di.MainUiApi import com.stslex93.notes.core.ui.di.NavigationApi import com.stslex93.notes.core.ui.theme.AppTheme @@ -21,7 +21,7 @@ class MainActivity : AppCompatActivity(), MainUiApi { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { - val navController = rememberNavController() + val navController = rememberNavExtrasController() _navigationApi = NavigationComponentBuilder .build(navController) .also { api -> diff --git a/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt b/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt index 6023240a..206b5a9a 100644 --- a/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt +++ b/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt @@ -1,23 +1,22 @@ package com.stslex93.notes.ui +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import com.stslex93.notes.core.navigation.model.AppDestination +import com.stslex93.notes.core.navigation.v2.compose.NavExtrasHost +import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController import com.stslex93.notes.feature.edit.ui.init.editGraph import com.stslex93.notes.feature.edit_label.navigation.graph.editLabelGraph import com.stslex93.notes.feature.home.navigation.homeGraph @Composable fun NavigationHost( - navHostController: NavHostController, + navController: NavExtrasHostController, modifier: Modifier = Modifier, - startDestination: AppDestination = AppDestination.HOME ) { - NavHost( - navController = navHostController, - startDestination = startDestination.route + Text(text = "text") + NavExtrasHost( + navController = navController, ) { homeGraph(modifier) editGraph(modifier) diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts index 58974338..f8eff64c 100644 --- a/core/navigation/build.gradle.kts +++ b/core/navigation/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("notes.android.library") id("notes.android.library.compose") + id("kotlin-parcelize") } dependencies { diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationComponent.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationComponent.kt index 2aef3eb1..195fe311 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationComponent.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationComponent.kt @@ -1,6 +1,6 @@ package com.stslex93.notes.core.navigation.di -import androidx.navigation.NavHostController +import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController import com.stslex93.notes.core.ui.di.NavigationApi import dagger.BindsInstance import dagger.Component @@ -14,7 +14,7 @@ interface NavigationComponent : NavigationApi { interface Builder { @BindsInstance - fun controller(navHostController: NavHostController): Builder + fun controller(navController: NavExtrasHostController): Builder fun build(): NavigationApi } diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationComponentBuilder.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationComponentBuilder.kt index 6344a144..61f62133 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationComponentBuilder.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationComponentBuilder.kt @@ -1,14 +1,14 @@ package com.stslex93.notes.core.navigation.di -import androidx.navigation.NavHostController +import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController import com.stslex93.notes.core.ui.di.NavigationApi object NavigationComponentBuilder { fun build( - navHostController: NavHostController + navController: NavExtrasHostController ): NavigationApi = DaggerNavigationComponent .builder() - .controller(navHostController) + .controller(navController) .build() } \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationModule.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationModule.kt index 46448e37..dcbe5c3a 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationModule.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/di/NavigationModule.kt @@ -1,6 +1,6 @@ package com.stslex93.notes.core.navigation.di -import com.stslex93.notes.core.navigation.navigator.NavigatorImpl +import com.stslex93.notes.core.navigation.v2.navigator.NavigatorImpl import com.stslex93.notes.core.ui.di.Navigator import dagger.Binds import dagger.Module diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/model/NavigationScreen.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/model/NavigationScreen.kt index b08ae944..8ecc9f9b 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/model/NavigationScreen.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/model/NavigationScreen.kt @@ -2,42 +2,42 @@ package com.stslex93.notes.core.navigation.model import com.stslex93.notes.core.ui.di.Screen -sealed class NavigationScreen : Screen { - - abstract val screen: AppDestination - - val screenRoute: String - get() = "${screen.route}${appArgs.argumentsForRoute}" - - open val isSingleTop: Boolean - get() = false - - open val appArgs: AppArguments - get() = AppArguments.Empty - - data object HomeScreen : NavigationScreen() { - override val screen: AppDestination = AppDestination.HOME - override val isSingleTop: Boolean = true - } - - data class EditNoteScreen( - private val noteId: Int, - private val isEdit: Boolean - ) : NavigationScreen() { - override val screen: AppDestination = AppDestination.NOTE_EDIT - override val appArgs: AppArguments = AppArguments.NoteEdit(noteId, isEdit) - } - - data class EditLabelScreen( - private val noteIds: Set - ) : NavigationScreen() { - override val screen = AppDestination.LABEL_EDIT - override val appArgs = AppArguments.LabelEdit(noteIds) - } - - data object PopBackStack : NavigationScreen() { - - override val screen: AppDestination = AppDestination.UNDEFINED - override val appArgs: AppArguments = AppArguments.Empty - } -} \ No newline at end of file +//sealed class NavigationScreen : Screen { +// +// abstract val screen: AppDestination +// +// val screenRoute: String +// get() = "${screen.route}${appArgs.argumentsForRoute}" +// +// open val isSingleTop: Boolean +// get() = false +// +// open val appArgs: AppArguments +// get() = AppArguments.Empty +// +// data object HomeScreen : NavigationScreen() { +// override val screen: AppDestination = AppDestination.HOME +// override val isSingleTop: Boolean = true +// } +// +// data class EditNoteScreen( +// private val noteId: Int, +// private val isEdit: Boolean +// ) : NavigationScreen() { +// override val screen: AppDestination = AppDestination.NOTE_EDIT +// override val appArgs: AppArguments = AppArguments.NoteEdit(noteId, isEdit) +// } +// +// data class EditLabelScreen( +// private val noteIds: Set +// ) : NavigationScreen() { +// override val screen = AppDestination.LABEL_EDIT +// override val appArgs = AppArguments.LabelEdit(noteIds) +// } +// +// data object PopBackStack : NavigationScreen() { +// +// override val screen: AppDestination = AppDestination.UNDEFINED +// override val appArgs: AppArguments = AppArguments.Empty +// } +//} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/navigator/NavigatorImpl.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/navigator/NavigatorImpl.kt index 05e32a3a..237fa8df 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/navigator/NavigatorImpl.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/navigator/NavigatorImpl.kt @@ -1,38 +1,31 @@ package com.stslex93.notes.core.navigation.navigator -import androidx.navigation.NavHostController -import com.stslex.aproselection.core.core.Logger -import com.stslex93.notes.core.navigation.model.NavigationScreen -import com.stslex93.notes.core.ui.di.Navigator -import com.stslex93.notes.core.ui.di.Screen -import javax.inject.Inject - -class NavigatorImpl @Inject constructor( - private val navController: NavHostController -) : Navigator { - - override fun invoke(screen: Screen) { - when (screen) { - is NavigationScreen.PopBackStack -> navController.popBackStack() - is NavigationScreen -> navigateScreen(screen) - else -> { - Logger.debug("unresolve navigation route", this::class.simpleName) - } - } - } - - private fun navigateScreen(screen: NavigationScreen) { - val currentRoute = navController.currentDestination?.route ?: return - if (currentRoute == screen.screen.navigationRoute) return - - navController.navigate(screen.screenRoute) { - if (screen.isSingleTop.not()) return@navigate - - popUpTo(currentRoute) { - inclusive = true - saveState = true - } - launchSingleTop = true - } - } -} \ No newline at end of file +//class NavigatorImpl @Inject constructor( +// private val navController: NavHostController +//) : Navigator { +// +// override fun invoke(screen: Screen) { +// when (screen) { +// is NavigationScreen.PopBackStack -> navController.popBackStack() +// is NavigationScreen -> navigateScreen(screen) +// else -> { +// Logger.debug("unresolve navigation route", this::class.simpleName) +// } +// } +// } +// +// private fun navigateScreen(screen: NavigationScreen) { +// val currentRoute = navController.currentDestination?.route ?: return +// if (currentRoute == screen.screen.navigationRoute) return +// +// navController.navigate(screen.screenRoute) { +// if (screen.isSingleTop.not()) return@navigate +// +// popUpTo(currentRoute) { +// inclusive = true +// saveState = true +// } +// launchSingleTop = true +// } +// } +//} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/NoteEditArgs.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/NoteEditArgs.kt new file mode 100644 index 00000000..6bc62b89 --- /dev/null +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/NoteEditArgs.kt @@ -0,0 +1,10 @@ +package com.stslex93.notes.core.navigation.v2 + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class NoteEditArgs( + val noteId: Int, + val isEdit: Boolean +) : Parcelable \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt new file mode 100644 index 00000000..8e06df30 --- /dev/null +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt @@ -0,0 +1,89 @@ +package com.stslex93.notes.core.navigation.v2.compose + +import android.os.Build +import android.os.Bundle +import android.os.Parcelable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.navigation.NamedNavArgument +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavDeepLink +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.createGraph +import androidx.navigation.get +import androidx.navigation.navArgument +import com.stslex93.notes.core.navigation.v2.controller.ComposeNavigator +import com.stslex93.notes.core.navigation.v2.controller.Destination +import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController +import com.stslex93.notes.core.navigation.v2.screen.HostScreen + +fun NavGraphBuilder.composableArg( + route: String, + arguments: List = emptyList(), + deepLinks: List = emptyList(), + content: @Composable (backStackEntry: NavBackStackEntry, arguments: Bundle?) -> Unit +) { + addDestination( + Destination(provider[ComposeNavigator::class], content).apply { + this.route = route + arguments.forEach { (argumentName, argument) -> + addArgument(argumentName, argument) + } + deepLinks.forEach { deepLink -> + addDeepLink(deepLink) + } + } + ) +} + +inline fun NavGraphBuilder.composableArg( + screen: HostScreen, + crossinline content: @Composable (data: T?) -> Unit +) { + composableArg( + route = screen.route, + arguments = listOf(navArgument(screen.key) { + NavType.ParcelableType(T::class.java) + }), + deepLinks = emptyList() + ) { _, arguments -> + val data = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + arguments?.getParcelable(screen.key, T::class.java) + } else { + arguments?.getParcelable(screen.key) + } + content(data) + } +} + +fun NavGraphBuilder.composable( + screen: HostScreen, + content: @Composable (backStackEntry: NavBackStackEntry) -> Unit +) { + composableArg( + route = screen.route, + arguments = emptyList(), + deepLinks = emptyList() + ) { backStackEntry, _ -> + content(backStackEntry) + } +} + +@Composable +fun NavExtrasHost( + navController: NavExtrasHostController, + modifier: Modifier = Modifier, + route: String? = null, + builder: NavGraphBuilder.() -> Unit +) { + NavHost( + navController, + remember(route, navController.startDestination.route, builder) { + navController.createGraph(navController.startDestination.route, route, builder) + }, + modifier + ) +} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/ComposeNavigator.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/ComposeNavigator.kt new file mode 100644 index 00000000..1e9d6529 --- /dev/null +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/ComposeNavigator.kt @@ -0,0 +1,18 @@ +package com.stslex93.notes.core.navigation.v2.controller + +import android.os.Bundle +import androidx.compose.runtime.Composable +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavDestination +import androidx.navigation.Navigator + +@Navigator.Name("composable") +class ComposeNavigator : Navigator() { + override fun createDestination(): Destination = Destination(this) { _, _ -> } +} + +@NavDestination.ClassType(Composable::class) +class Destination( + navigator: ComposeNavigator, + internal val content: @Composable (backStackEntry: NavBackStackEntry, arguments: Bundle?) -> Unit +) : NavDestination(navigator) \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/NavExtrasHostController.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/NavExtrasHostController.kt new file mode 100644 index 00000000..19ed77ee --- /dev/null +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/NavExtrasHostController.kt @@ -0,0 +1,84 @@ +package com.stslex93.notes.core.navigation.v2.controller + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.platform.LocalContext +import androidx.navigation.NavDestination +import androidx.navigation.NavHostController +import androidx.navigation.NavOptions +import androidx.navigation.NavOptionsBuilder +import androidx.navigation.Navigator +import androidx.navigation.compose.DialogNavigator +import com.stslex93.notes.core.navigation.v2.screen.AppNavScreen +import com.stslex93.notes.core.ui.base.NavigationScreen +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + +class NavExtrasHostController( + context: Context, + val startDestination: NavigationScreen +) : NavHostController(context) { + + private val _currentScreensBackStack: MutableStateFlow> = + MutableStateFlow(mutableMapOf(startDestination.route to startDestination)) + + val currentScreensBackStack: StateFlow> = + _currentScreensBackStack.asStateFlow() + + override fun popBackStack(): Boolean { + _currentScreensBackStack.update { screensMap -> + screensMap.apply { remove(currentDestination?.route) } + } + return super.popBackStack() + } + + fun navigate(screen: NavigationScreen, navOptions: NavOptions? = null) { + _currentScreensBackStack.update { it.apply { put(screen.route, screen) } } + navigate(route = screen.route, navOptions = navOptions) + } + + fun navigate(screen: NavigationScreen, builder: NavOptionsBuilder.() -> Unit) { + navigate(route = screen.route, builder = builder) + } +} + +private fun NavExtrasControllerSaver( + context: Context, + startDestination: NavigationScreen +): Saver = Saver( + save = { it.saveState() }, + restore = { createNavExtrasController(context, startDestination).apply { restoreState(it) } } +) + +private fun createNavExtrasController( + context: Context, + startDestination: NavigationScreen +) = NavExtrasHostController( + context = context, + startDestination = startDestination +).apply { + navigatorProvider.addNavigator(ComposeNavigator()) + navigatorProvider.addNavigator(DialogNavigator()) +} + +@Composable +fun rememberNavExtrasController( + startDestination: NavigationScreen = AppNavScreen.Home, + vararg navigators: Navigator +): NavExtrasHostController { + val context = LocalContext.current + return rememberSaveable( + inputs = navigators, + saver = NavExtrasControllerSaver(context, startDestination) + ) { + createNavExtrasController(context, startDestination) + }.apply { + for (navigator in navigators) { + navigatorProvider.addNavigator(navigator) + } + } +} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/navigator/NavigatorImpl.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/navigator/NavigatorImpl.kt new file mode 100644 index 00000000..2f0b92a7 --- /dev/null +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/navigator/NavigatorImpl.kt @@ -0,0 +1,30 @@ +package com.stslex93.notes.core.navigation.v2.navigator + +import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController +import com.stslex93.notes.core.ui.base.NavigationScreen +import com.stslex93.notes.core.ui.di.Navigator +import javax.inject.Inject + +class NavigatorImpl @Inject constructor( + private val navController: NavExtrasHostController +) : Navigator { + + override fun navigate(screen: NavigationScreen) { + navController.navigate(screen = screen) { + navController.currentDestination + ?.route + ?.takeIf { screen.isSingleTop } + ?.let { currentRoute -> + popUpTo(currentRoute) { + inclusive = true + saveState = true + } + launchSingleTop = true + } + } + } + + override fun popBackStack() { + navController.popBackStack() + } +} diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/AppNavScreen.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/AppNavScreen.kt new file mode 100644 index 00000000..65327d4f --- /dev/null +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/AppNavScreen.kt @@ -0,0 +1,24 @@ +package com.stslex93.notes.core.navigation.v2.screen + +import com.stslex93.notes.core.navigation.v2.NoteEditArgs +import com.stslex93.notes.core.ui.base.NavigationScreen +import com.stslex93.notes.core.ui.base.saveBundle + +sealed class AppNavScreen( + route: String, + extra: Extra? = null +) : NavigationScreen(route, extra.saveBundle) { + + data object Home : AppNavScreen( + HostScreen.HOME.route + ) { + override val isSingleTop: Boolean = true + } + + data class NoteEdit( + val noteEdit: NoteEditArgs + ) : AppNavScreen( + route = HostScreen.NOTE_EDIT.route, + extra = Extra(HostScreen.NOTE_EDIT.key, noteEdit) + ) +} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/HostScreen.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/HostScreen.kt new file mode 100644 index 00000000..689ca9b3 --- /dev/null +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/HostScreen.kt @@ -0,0 +1,9 @@ +package com.stslex93.notes.core.navigation.v2.screen + +enum class HostScreen { + HOME, + NOTE_EDIT; + + val route: String = name.lowercase() + val key: String = "${name.lowercase()}_key" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/stslex93/notes/core/ui/base/NavigationScreen.kt b/core/ui/src/main/java/com/stslex93/notes/core/ui/base/NavigationScreen.kt new file mode 100644 index 00000000..b4338857 --- /dev/null +++ b/core/ui/src/main/java/com/stslex93/notes/core/ui/base/NavigationScreen.kt @@ -0,0 +1,23 @@ +package com.stslex93.notes.core.ui.base + +import android.os.Bundle +import android.os.Parcelable +import androidx.core.os.bundleOf + +abstract class NavigationScreen( + open val route: String, + open val arguments: Bundle? = null +) { + constructor(route: String, extra: Extra) : this(route, extra.saveBundle) + + data class Extra(val key: String, val parcelable: Parcelable) + + open val isSingleTop: Boolean = false +} + +val NavigationScreen.Extra?.saveBundle: Bundle + get() = bundleOf().apply { + this@saveBundle?.let { extra -> + putParcelable(extra.key, extra.parcelable) + } + } \ No newline at end of file diff --git a/core/ui/src/main/java/com/stslex93/notes/core/ui/di/Navigator.kt b/core/ui/src/main/java/com/stslex93/notes/core/ui/di/Navigator.kt index 9fdd9b6d..fb15cae9 100644 --- a/core/ui/src/main/java/com/stslex93/notes/core/ui/di/Navigator.kt +++ b/core/ui/src/main/java/com/stslex93/notes/core/ui/di/Navigator.kt @@ -1,5 +1,10 @@ package com.stslex93.notes.core.ui.di -fun interface Navigator { - operator fun invoke(screen: Screen) +import com.stslex93.notes.core.ui.base.NavigationScreen + +interface Navigator { + + fun navigate(screen: NavigationScreen) + + fun popBackStack() } diff --git a/core/ui/src/main/java/com/stslex93/notes/core/ui/di/Screen.kt b/core/ui/src/main/java/com/stslex93/notes/core/ui/di/Screen.kt deleted file mode 100644 index dd44090b..00000000 --- a/core/ui/src/main/java/com/stslex93/notes/core/ui/di/Screen.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.stslex93.notes.core.ui.di - -interface Screen \ No newline at end of file diff --git a/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/EditNoteViewModel.kt b/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/EditNoteViewModel.kt index 6ccc0281..6158e484 100644 --- a/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/EditNoteViewModel.kt +++ b/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/EditNoteViewModel.kt @@ -1,7 +1,6 @@ package com.stslex93.notes.feature.edit.ui -import com.stslex93.notes.core.navigation.model.AppArguments -import com.stslex93.notes.core.navigation.model.NavigationScreen +import com.stslex93.notes.core.navigation.v2.NoteEditArgs import com.stslex93.notes.core.ui.base.BaseViewModel import com.stslex93.notes.core.ui.di.Navigator import com.stslex93.notes.feature.edit.ui.store.EditStore @@ -12,7 +11,7 @@ class EditNoteViewModel @Inject constructor( private val navigator: Navigator, ) : BaseViewModel(store) { - fun init(arguments: AppArguments.NoteEdit) { + fun init(arguments: NoteEditArgs) { sendAction( EditStore.Action.Init( id = arguments.noteId, @@ -22,6 +21,6 @@ class EditNoteViewModel @Inject constructor( } fun popBackStack() { - navigator(NavigationScreen.PopBackStack) + navigator.popBackStack() } } \ No newline at end of file diff --git a/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt b/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt index c55747f7..96768c93 100644 --- a/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt +++ b/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt @@ -3,29 +3,18 @@ package com.stslex93.notes.feature.edit.ui.init import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import com.stslex93.notes.core.navigation.model.AppArguments import com.stslex93.notes.core.navigation.model.AppDestination import com.stslex93.notes.core.navigation.utils.NavExt.composableArguments -import com.stslex93.notes.core.navigation.utils.NavExt.parseArguments +import com.stslex93.notes.core.navigation.v2.NoteEditArgs +import com.stslex93.notes.core.navigation.v2.compose.composableArg +import com.stslex93.notes.core.navigation.v2.screen.HostScreen import com.stslex93.notes.feature.edit.di.setupComponent fun NavGraphBuilder.editGraph( modifier: Modifier = Modifier, ) { - composable( - route = AppDestination.NOTE_EDIT.navigationRoute, - arguments = AppDestination.NOTE_EDIT.composableArguments - ) { navBackStackEntry -> - - val arguments = AppDestination.NOTE_EDIT - .parseArguments(navBackStackEntry) - .let { args -> - AppArguments.NoteEdit( - noteId = args[0].toIntOrNull() ?: -1, - isEdit = args[1].toBooleanStrictOrNull() ?: false - ) - } + composableArg(HostScreen.NOTE_EDIT) { noteEditArgs -> + val arguments = noteEditArgs ?: NoteEditArgs(-1, false) val viewModel = setupComponent(arguments.hashCode().toString()) @@ -38,4 +27,29 @@ fun NavGraphBuilder.editGraph( viewModel = viewModel ) } +// composable( +// route = AppDestination.NOTE_EDIT.navigationRoute, +// arguments = AppDestination.NOTE_EDIT.composableArguments +// ) { navBackStackEntry -> +// +// val arguments = AppDestination.NOTE_EDIT +// .parseArguments(navBackStackEntry) +// .let { args -> +// AppArguments.NoteEdit( +// noteId = args[0].toIntOrNull() ?: -1, +// isEdit = args[1].toBooleanStrictOrNull() ?: false +// ) +// } +// +// val viewModel = setupComponent(arguments.hashCode().toString()) +// +// LaunchedEffect(Unit) { +// viewModel.init(arguments) +// } +// +// InitEditScreen( +// modifier = modifier, +// viewModel = viewModel +// ) +// } } \ No newline at end of file diff --git a/feature/home/src/main/java/com/stslex93/notes/feature/home/navigation/HomeGraph.kt b/feature/home/src/main/java/com/stslex93/notes/feature/home/navigation/HomeGraph.kt index 8dfcf228..599a3072 100644 --- a/feature/home/src/main/java/com/stslex93/notes/feature/home/navigation/HomeGraph.kt +++ b/feature/home/src/main/java/com/stslex93/notes/feature/home/navigation/HomeGraph.kt @@ -2,17 +2,15 @@ package com.stslex93.notes.feature.home.navigation import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import com.stslex93.notes.core.navigation.model.AppDestination +import com.stslex93.notes.core.navigation.v2.compose.composable +import com.stslex93.notes.core.navigation.v2.screen.HostScreen import com.stslex93.notes.feature.home.di.setupComponent import com.stslex93.notes.feature.home.ui.init.HomeScreeInit fun NavGraphBuilder.homeGraph( modifier: Modifier = Modifier, ) { - composable( - route = AppDestination.HOME.navigationRoute - ) { + composable(HostScreen.HOME) { HomeScreeInit( modifier = modifier, viewModel = setupComponent() From 6743df1f1cb53a8dc1ab28710690743f4e37aa92 Mon Sep 17 00:00:00 2001 From: stslex Date: Wed, 6 Sep 2023 23:14:43 +0300 Subject: [PATCH 2/3] init --- .../com/stslex93/notes/ui/NavigationHost.kt | 2 +- .../navigation/v2/compose/ComposeGraph.kt | 61 +------- .../core/navigation/v2/compose/NavHost.kt | 146 ++++++++++++++++++ .../v2/controller/ComposeNavigator.kt | 9 ++ .../notes/feature/edit/ui/init/EditGraph.kt | 20 ++- .../feature/home/navigation/HomeGraph.kt | 2 +- 6 files changed, 167 insertions(+), 73 deletions(-) create mode 100644 core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/NavHost.kt diff --git a/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt b/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt index 206b5a9a..80a21227 100644 --- a/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt +++ b/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt @@ -22,4 +22,4 @@ fun NavigationHost( editGraph(modifier) editLabelGraph(modifier) } -} \ No newline at end of file +} diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt index 8e06df30..5c1aa6ee 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt @@ -1,26 +1,16 @@ package com.stslex93.notes.core.navigation.v2.compose -import android.os.Build import android.os.Bundle -import android.os.Parcelable import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier import androidx.navigation.NamedNavArgument import androidx.navigation.NavBackStackEntry import androidx.navigation.NavDeepLink import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavType -import androidx.navigation.compose.NavHost -import androidx.navigation.createGraph import androidx.navigation.get -import androidx.navigation.navArgument import com.stslex93.notes.core.navigation.v2.controller.ComposeNavigator import com.stslex93.notes.core.navigation.v2.controller.Destination -import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController -import com.stslex93.notes.core.navigation.v2.screen.HostScreen -fun NavGraphBuilder.composableArg( +fun NavGraphBuilder.composable( route: String, arguments: List = emptyList(), deepLinks: List = emptyList(), @@ -38,52 +28,3 @@ fun NavGraphBuilder.composableArg( } ) } - -inline fun NavGraphBuilder.composableArg( - screen: HostScreen, - crossinline content: @Composable (data: T?) -> Unit -) { - composableArg( - route = screen.route, - arguments = listOf(navArgument(screen.key) { - NavType.ParcelableType(T::class.java) - }), - deepLinks = emptyList() - ) { _, arguments -> - val data = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - arguments?.getParcelable(screen.key, T::class.java) - } else { - arguments?.getParcelable(screen.key) - } - content(data) - } -} - -fun NavGraphBuilder.composable( - screen: HostScreen, - content: @Composable (backStackEntry: NavBackStackEntry) -> Unit -) { - composableArg( - route = screen.route, - arguments = emptyList(), - deepLinks = emptyList() - ) { backStackEntry, _ -> - content(backStackEntry) - } -} - -@Composable -fun NavExtrasHost( - navController: NavExtrasHostController, - modifier: Modifier = Modifier, - route: String? = null, - builder: NavGraphBuilder.() -> Unit -) { - NavHost( - navController, - remember(route, navController.startDestination.route, builder) { - navController.createGraph(navController.startDestination.route, route, builder) - }, - modifier - ) -} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/NavHost.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/NavHost.kt new file mode 100644 index 00000000..a8fb5a60 --- /dev/null +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/NavHost.kt @@ -0,0 +1,146 @@ +package com.stslex93.notes.core.navigation.v2.compose + +import android.os.Bundle +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner +import androidx.compose.animation.Crossfade +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveableStateHolder +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner +import androidx.navigation.NavDestination +import androidx.navigation.NavGraph +import androidx.navigation.NavGraphBuilder +import androidx.navigation.Navigator +import androidx.navigation.compose.DialogHost +import androidx.navigation.compose.DialogNavigator +import androidx.navigation.compose.LocalOwnersProvider +import androidx.navigation.createGraph +import androidx.navigation.get +import com.stslex93.notes.core.navigation.v2.controller.ComposeNavigator +import com.stslex93.notes.core.navigation.v2.controller.Destination +import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController +import kotlinx.coroutines.flow.map + +@Composable +fun NavExtrasHost( + navController: NavExtrasHostController, + modifier: Modifier = Modifier, + route: String? = null, + builder: NavGraphBuilder.() -> Unit +) { + NavHost( + navController, + remember(route, navController.startDestination.route, builder) { + navController.createGraph(navController.startDestination.route, route, builder) + }, + modifier + ) +} + +@Composable +fun NavHost( + navController: NavExtrasHostController, + graph: NavGraph, + modifier: Modifier = Modifier +) { + val lifecycleOwner = LocalLifecycleOwner.current + val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { + "NavHost requires a ViewModelStoreOwner to be provided via LocalViewModelStoreOwner" + } + val onBackPressedDispatcherOwner = LocalOnBackPressedDispatcherOwner.current + val onBackPressedDispatcher = onBackPressedDispatcherOwner?.onBackPressedDispatcher + + // Setup the navController with proper owners + navController.setLifecycleOwner(lifecycleOwner) + navController.setViewModelStore(viewModelStoreOwner.viewModelStore) + if (onBackPressedDispatcher != null) { + navController.setOnBackPressedDispatcher(onBackPressedDispatcher) + } + // Ensure that the NavController only receives back events while + // the NavHost is in composition + DisposableEffect(navController) { + navController.enableOnBackPressed(true) + onDispose { + navController.enableOnBackPressed(false) + } + } + + // Then set the graph + navController.graph = graph + + val saveableStateHolder = rememberSaveableStateHolder() + + // Find the ComposeNavigator, returning early if it isn't found + // (such as is the case when using TestNavHostController) + val composeNavigator = navController.navigatorProvider.get>( + ComposeNavigator.NAME + ) as? ComposeNavigator ?: return + val visibleEntries by remember(navController.visibleEntries) { + navController.visibleEntries.map { + it.filter { entry -> + entry.destination.navigatorName == ComposeNavigator.NAME + } + } + }.collectAsState(emptyList()) + + val screensBackStack by navController.currentScreensBackStack.collectAsState() + + val backStackEntry = visibleEntries.lastOrNull() + + var initialCrossfade by remember { mutableStateOf(true) } + if (backStackEntry != null) { + // while in the scope of the composable, we provide the navBackStackEntry as the + // ViewModelStoreOwner and LifecycleOwner + Crossfade( + targetState = backStackEntry.id, + modifier = modifier, + label = "crossfadde" + ) { + val lastEntry = visibleEntries.last { entry -> + it == entry.id + } + // We are disposing on a Unit as we only want to dispose when the CrossFade completes + DisposableEffect(Unit) { + if (initialCrossfade) { + // There's no animation for the initial crossfade, + // so we can instantly mark the transition as complete + visibleEntries.forEach { entry -> + composeNavigator.onTransitionComplete(entry) + } + initialCrossfade = false + } + onDispose { + visibleEntries.forEach { entry -> + composeNavigator.onTransitionComplete(entry) + } + } + } + + lastEntry.LocalOwnersProvider(saveableStateHolder) { + val previousScreenExtras = screensBackStack[lastEntry.destination.route]?.arguments + + val newLastEntryArguments = (lastEntry.arguments ?: Bundle()).apply { + if (previousScreenExtras != null) { + putAll(previousScreenExtras) + } + } + + (lastEntry.destination as Destination).content(lastEntry, newLastEntryArguments) + } + } + } + + val dialogNavigator = navController.navigatorProvider.get>( + ComposeNavigator.NAME + ) as? DialogNavigator ?: return + + // Show any dialog destinations + DialogHost(dialogNavigator) +} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/ComposeNavigator.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/ComposeNavigator.kt index 1e9d6529..244a7742 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/ComposeNavigator.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/ComposeNavigator.kt @@ -8,7 +8,16 @@ import androidx.navigation.Navigator @Navigator.Name("composable") class ComposeNavigator : Navigator() { + override fun createDestination(): Destination = Destination(this) { _, _ -> } + + fun onTransitionComplete(entry: NavBackStackEntry) { + state.markTransitionComplete(entry) + } + + internal companion object { + const val NAME = "composable" + } } @NavDestination.ClassType(Composable::class) diff --git a/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt b/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt index 96768c93..be0de492 100644 --- a/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt +++ b/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt @@ -3,24 +3,22 @@ package com.stslex93.notes.feature.edit.ui.init import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder -import com.stslex93.notes.core.navigation.model.AppDestination -import com.stslex93.notes.core.navigation.utils.NavExt.composableArguments import com.stslex93.notes.core.navigation.v2.NoteEditArgs -import com.stslex93.notes.core.navigation.v2.compose.composableArg +import com.stslex93.notes.core.navigation.v2.compose.composable import com.stslex93.notes.core.navigation.v2.screen.HostScreen import com.stslex93.notes.feature.edit.di.setupComponent fun NavGraphBuilder.editGraph( modifier: Modifier = Modifier, ) { - composableArg(HostScreen.NOTE_EDIT) { noteEditArgs -> - val arguments = noteEditArgs ?: NoteEditArgs(-1, false) - - val viewModel = setupComponent(arguments.hashCode().toString()) - - LaunchedEffect(Unit) { - viewModel.init(arguments) - } + composable(HostScreen.NOTE_EDIT.route) { _, _ -> +// val arguments = noteEditArgs ?: NoteEditArgs(-1, false) +// + val viewModel = setupComponent("") +// +// LaunchedEffect(Unit) { +// viewModel.init(arguments) +// } InitEditScreen( modifier = modifier, diff --git a/feature/home/src/main/java/com/stslex93/notes/feature/home/navigation/HomeGraph.kt b/feature/home/src/main/java/com/stslex93/notes/feature/home/navigation/HomeGraph.kt index 599a3072..7ab98a24 100644 --- a/feature/home/src/main/java/com/stslex93/notes/feature/home/navigation/HomeGraph.kt +++ b/feature/home/src/main/java/com/stslex93/notes/feature/home/navigation/HomeGraph.kt @@ -10,7 +10,7 @@ import com.stslex93.notes.feature.home.ui.init.HomeScreeInit fun NavGraphBuilder.homeGraph( modifier: Modifier = Modifier, ) { - composable(HostScreen.HOME) { + composable(HostScreen.HOME.route) { _, _ -> HomeScreeInit( modifier = modifier, viewModel = setupComponent() From 7f5fde5b90369bf2bd28a926f72cbf1f64676f91 Mon Sep 17 00:00:00 2001 From: stslex Date: Thu, 7 Sep 2023 08:55:31 +0300 Subject: [PATCH 3/3] fix white screen --- .../com/stslex93/notes/ui/NavigationHost.kt | 2 - .../navigation/v2/compose/ComposeGraph.kt | 34 ++++++++++++++++ .../core/navigation/v2/compose/NavHost.kt | 10 +++-- .../v2/controller/NavExtrasHostController.kt | 4 +- .../navigation/v2/navigator/NavigatorImpl.kt | 30 +++++++++----- .../core/navigation/v2/screen/AppNavScreen.kt | 33 +++++++-------- .../notes/feature/edit/ui/init/EditGraph.kt | 40 ++++--------------- 7 files changed, 87 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt b/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt index 80a21227..827a6781 100644 --- a/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt +++ b/app/src/main/java/com/stslex93/notes/ui/NavigationHost.kt @@ -1,6 +1,5 @@ package com.stslex93.notes.ui -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.stslex93.notes.core.navigation.v2.compose.NavExtrasHost @@ -14,7 +13,6 @@ fun NavigationHost( navController: NavExtrasHostController, modifier: Modifier = Modifier, ) { - Text(text = "text") NavExtrasHost( navController = navController, ) { diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt index 5c1aa6ee..7b7daeef 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/ComposeGraph.kt @@ -1,6 +1,8 @@ package com.stslex93.notes.core.navigation.v2.compose +import android.os.Build import android.os.Bundle +import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.navigation.NamedNavArgument import androidx.navigation.NavBackStackEntry @@ -9,6 +11,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.get import com.stslex93.notes.core.navigation.v2.controller.ComposeNavigator import com.stslex93.notes.core.navigation.v2.controller.Destination +import com.stslex93.notes.core.navigation.v2.screen.HostScreen fun NavGraphBuilder.composable( route: String, @@ -28,3 +31,34 @@ fun NavGraphBuilder.composable( } ) } + +inline fun NavGraphBuilder.composable( + screen: HostScreen, + arguments: List = emptyList(), + deepLinks: List = emptyList(), + crossinline content: @Composable (backStackEntry: NavBackStackEntry, arguments: T?) -> Unit +) { + composable( + route = screen.route, + arguments = arguments, + deepLinks = deepLinks, + ) { backStackEntry, args -> + val data = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + args?.getParcelable(screen.key, T::class.java) + } else { + args?.getParcelable(screen.key) + } + content(backStackEntry, data) + } +} + +inline fun NavGraphBuilder.composable( + screen: HostScreen, + crossinline content: @Composable (data: T) -> Unit +) { + composable(screen) { _, arguments -> + arguments?.let { data -> + content(data) + } + } +} diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/NavHost.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/NavHost.kt index a8fb5a60..448a6cf0 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/NavHost.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/compose/NavHost.kt @@ -1,6 +1,7 @@ package com.stslex93.notes.core.navigation.v2.compose import android.os.Bundle +import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.animation.Crossfade import androidx.compose.runtime.Composable @@ -36,7 +37,7 @@ fun NavExtrasHost( builder: NavGraphBuilder.() -> Unit ) { NavHost( - navController, + navController = navController, remember(route, navController.startDestination.route, builder) { navController.createGraph(navController.startDestination.route, route, builder) }, @@ -60,11 +61,14 @@ fun NavHost( // Setup the navController with proper owners navController.setLifecycleOwner(lifecycleOwner) navController.setViewModelStore(viewModelStoreOwner.viewModelStore) + if (onBackPressedDispatcher != null) { navController.setOnBackPressedDispatcher(onBackPressedDispatcher) } + // Ensure that the NavController only receives back events while // the NavHost is in composition + DisposableEffect(navController) { navController.enableOnBackPressed(true) onDispose { @@ -103,9 +107,9 @@ fun NavHost( modifier = modifier, label = "crossfadde" ) { - val lastEntry = visibleEntries.last { entry -> + val lastEntry = visibleEntries.lastOrNull { entry -> it == entry.id - } + } ?: return@Crossfade // We are disposing on a Unit as we only want to dispose when the CrossFade completes DisposableEffect(Unit) { if (initialCrossfade) { diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/NavExtrasHostController.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/NavExtrasHostController.kt index 19ed77ee..4f3d7bc2 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/NavExtrasHostController.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/controller/NavExtrasHostController.kt @@ -11,7 +11,7 @@ import androidx.navigation.NavOptions import androidx.navigation.NavOptionsBuilder import androidx.navigation.Navigator import androidx.navigation.compose.DialogNavigator -import com.stslex93.notes.core.navigation.v2.screen.AppNavScreen +import com.stslex93.notes.core.navigation.v2.screen.Home import com.stslex93.notes.core.ui.base.NavigationScreen import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -67,7 +67,7 @@ private fun createNavExtrasController( @Composable fun rememberNavExtrasController( - startDestination: NavigationScreen = AppNavScreen.Home, + startDestination: NavigationScreen = Home, vararg navigators: Navigator ): NavExtrasHostController { val context = LocalContext.current diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/navigator/NavigatorImpl.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/navigator/NavigatorImpl.kt index 2f0b92a7..107ecd9c 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/navigator/NavigatorImpl.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/navigator/NavigatorImpl.kt @@ -1,27 +1,37 @@ package com.stslex93.notes.core.navigation.v2.navigator +import androidx.navigation.NavOptions import com.stslex93.notes.core.navigation.v2.controller.NavExtrasHostController import com.stslex93.notes.core.ui.base.NavigationScreen import com.stslex93.notes.core.ui.di.Navigator import javax.inject.Inject +import javax.inject.Singleton +@Singleton class NavigatorImpl @Inject constructor( private val navController: NavExtrasHostController ) : Navigator { override fun navigate(screen: NavigationScreen) { - navController.navigate(screen = screen) { - navController.currentDestination - ?.route - ?.takeIf { screen.isSingleTop } - ?.let { currentRoute -> - popUpTo(currentRoute) { - inclusive = true + + val options = navController.currentDestination + ?.route + ?.takeIf { screen.isSingleTop } + ?.let { currentRoute -> + NavOptions.Builder().apply { + setPopUpTo( + route = currentRoute, + inclusive = true, saveState = true - } - launchSingleTop = true + ) + setLaunchSingleTop(true) } - } + }?.build() + + navController.navigate( + screen = screen, + navOptions = options + ) } override fun popBackStack() { diff --git a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/AppNavScreen.kt b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/AppNavScreen.kt index 65327d4f..52fee411 100644 --- a/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/AppNavScreen.kt +++ b/core/navigation/src/main/java/com/stslex93/notes/core/navigation/v2/screen/AppNavScreen.kt @@ -4,21 +4,22 @@ import com.stslex93.notes.core.navigation.v2.NoteEditArgs import com.stslex93.notes.core.ui.base.NavigationScreen import com.stslex93.notes.core.ui.base.saveBundle -sealed class AppNavScreen( - route: String, - extra: Extra? = null -) : NavigationScreen(route, extra.saveBundle) { +data object Home : NavigationScreen(HostScreen.HOME.route) { - data object Home : AppNavScreen( - HostScreen.HOME.route - ) { - override val isSingleTop: Boolean = true - } + override val isSingleTop: Boolean = true +} - data class NoteEdit( - val noteEdit: NoteEditArgs - ) : AppNavScreen( - route = HostScreen.NOTE_EDIT.route, - extra = Extra(HostScreen.NOTE_EDIT.key, noteEdit) - ) -} \ No newline at end of file +data class NoteEdit( + val noteEdit: NoteEditArgs +) : NavigationScreen( + route = HostScreen.NOTE_EDIT.route, + extra = Extra(HostScreen.NOTE_EDIT.key, noteEdit) +) + +//sealed class AppNavScreen( +// route: String, +// extra: Extra? = null +//) : NavigationScreen(route, extra.saveBundle) { +// +// +//} \ No newline at end of file diff --git a/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt b/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt index be0de492..140b9edb 100644 --- a/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt +++ b/feature/edit/src/main/java/com/stslex93/notes/feature/edit/ui/init/EditGraph.kt @@ -11,43 +11,17 @@ import com.stslex93.notes.feature.edit.di.setupComponent fun NavGraphBuilder.editGraph( modifier: Modifier = Modifier, ) { - composable(HostScreen.NOTE_EDIT.route) { _, _ -> -// val arguments = noteEditArgs ?: NoteEditArgs(-1, false) -// - val viewModel = setupComponent("") -// -// LaunchedEffect(Unit) { -// viewModel.init(arguments) -// } + composable(HostScreen.NOTE_EDIT) { arguments -> + + val viewModel = setupComponent(arguments.hashCode().toString()) + + LaunchedEffect(Unit) { + viewModel.init(arguments) + } InitEditScreen( modifier = modifier, viewModel = viewModel ) } -// composable( -// route = AppDestination.NOTE_EDIT.navigationRoute, -// arguments = AppDestination.NOTE_EDIT.composableArguments -// ) { navBackStackEntry -> -// -// val arguments = AppDestination.NOTE_EDIT -// .parseArguments(navBackStackEntry) -// .let { args -> -// AppArguments.NoteEdit( -// noteId = args[0].toIntOrNull() ?: -1, -// isEdit = args[1].toBooleanStrictOrNull() ?: false -// ) -// } -// -// val viewModel = setupComponent(arguments.hashCode().toString()) -// -// LaunchedEffect(Unit) { -// viewModel.init(arguments) -// } -// -// InitEditScreen( -// modifier = modifier, -// viewModel = viewModel -// ) -// } } \ No newline at end of file