From fc0a54704d50dbf10d314ad5177e4c4a5465cd2b Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Tue, 25 Oct 2022 23:57:37 +0200 Subject: [PATCH 01/10] SavedStateHandle extensions fixed --- .../android/common/navigation/Navigator.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt index aae8e2609..dc5595ce3 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt @@ -52,15 +52,19 @@ interface Navigator { * @param destination The current destination. */ fun SavedStateHandle.getStateFlow(destination: DestinationId, initial: A?): StateFlow = - @Suppress("UNCHECKED_CAST") - getStateFlow(destination.name, initial) + getStateFlow(destination.name, initial) /** * Returns the argument for the current destination. * * @param destination The current destination. */ -@Suppress("UNCHECKED_CAST") -fun SavedStateHandle.get(destination: DestinationId): A? = - @Suppress("DEPRECATION") - get(destination.name)?.get(destination.name) as? A \ No newline at end of file +fun SavedStateHandle.getOrNull(destination: DestinationId): A? = get(destination.name) + +/** + * Returns the argument for the current destination. + * + * @param destination The current destination. + */ +fun SavedStateHandle.get(destination: DestinationId): A = + get(destination.name) ?: error("Argument for ${destination.name} not found") \ No newline at end of file From 88101e27267c4462f14c1a4054d10e464b7f2d1f Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Tue, 25 Oct 2022 23:57:59 +0200 Subject: [PATCH 02/10] Improved destinations API --- .../android/common/navigation/NavigationDestination.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationDestination.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationDestination.kt index de749241f..38c1e3bef 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationDestination.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationDestination.kt @@ -62,6 +62,10 @@ data class NavigationDestination( operator fun plus(other: NavigationDestination): NavigationDestinations { return listOf(this, other).asDestinations() } + + operator fun plus(other: NavigationDestinations): NavigationDestinations { + return other + this + } } /** @@ -79,6 +83,10 @@ class NavigationDestinations( operator fun plus(other: NavigationDestinations): NavigationDestinations { return NavigationDestinations(values + other.values) } + + operator fun plus(other: NavigationDestination): NavigationDestinations { + return NavigationDestinations(values + other) + } } /** From 8acc34e241264901099c213081c48dd032e967b9 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 26 Oct 2022 00:13:11 +0200 Subject: [PATCH 03/10] Simple Navigation View Model --- .../viewmodel/SimpleNavigationViewModel.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 navigation/src/main/java/no/nordicsemi/android/common/navigation/viewmodel/SimpleNavigationViewModel.kt diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/viewmodel/SimpleNavigationViewModel.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/viewmodel/SimpleNavigationViewModel.kt new file mode 100644 index 000000000..a3cbc086e --- /dev/null +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/viewmodel/SimpleNavigationViewModel.kt @@ -0,0 +1,31 @@ +package no.nordicsemi.android.common.navigation.viewmodel + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import no.nordicsemi.android.common.navigation.DestinationId +import no.nordicsemi.android.common.navigation.Navigator +import no.nordicsemi.android.common.navigation.get +import no.nordicsemi.android.common.navigation.getOrNull +import javax.inject.Inject + +@Suppress("unused") +@HiltViewModel +open class SimpleNavigationViewModel @Inject constructor( + navigator: Navigator, + private val savedStateHandle: SavedStateHandle, +): ViewModel(), Navigator by navigator { + + /** + * Returns the parameter of the current destination, or null, if hasn't been set. + */ + protected fun DestinationId.getParameterOrNull(): A? = + savedStateHandle.getOrNull(this) + + /** + * Returns the parameter of the current destination. + */ + protected fun DestinationId.getParameter(): A = + savedStateHandle.get(this) + +} \ No newline at end of file From d74ef32b34c5ce29c5650d71fd8737769bcec466 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 26 Oct 2022 00:26:00 +0200 Subject: [PATCH 04/10] Removal of NavigationDestinations in favor or a List --- .../navigation/NavigationDestination.kt | 50 ++----------------- .../common/navigation/NavigationView.kt | 6 +-- 2 files changed, 8 insertions(+), 48 deletions(-) diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationDestination.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationDestination.kt index 38c1e3bef..73817d194 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationDestination.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationDestination.kt @@ -59,33 +59,12 @@ data class NavigationDestination( val content: @Composable () -> Unit ) { - operator fun plus(other: NavigationDestination): NavigationDestinations { - return listOf(this, other).asDestinations() + operator fun plus(other: NavigationDestination): List { + return listOf(this, other) } - operator fun plus(other: NavigationDestinations): NavigationDestinations { - return other + this - } -} - -/** - * A collection of destinations. - * - * @property values List of destinations within a component. - */ -class NavigationDestinations( - val values: List, -) { - constructor( - destination: NavigationDestination, - ) : this(listOf(destination)) - - operator fun plus(other: NavigationDestinations): NavigationDestinations { - return NavigationDestinations(values + other.values) - } - - operator fun plus(other: NavigationDestination): NavigationDestinations { - return NavigationDestinations(values + other) + operator fun plus(other: List): List { + return listOf(this) + other } } @@ -110,23 +89,4 @@ fun createSimpleDestination(name: String): DestinationId = Destinati fun defineDestination( id: DestinationId<*, *>, content: @Composable () -> Unit -): NavigationDestination = NavigationDestination(id, content) - -/** - * Helper method for creating a [NavigationDestinations]. - * - * This destination can be routed using global router specified - * in the [NavigationView]. - */ -fun List.asDestinations() = - NavigationDestinations(this) - -/** - * Helper method for creating a [NavigationDestinations] - * from a single destination. - * - * This destination can be routed using global router specified - * in the [NavigationView]. - */ -fun NavigationDestination.asDestinations() = - NavigationDestinations(this) \ No newline at end of file +): NavigationDestination = NavigationDestination(id, content) \ No newline at end of file diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt index ec60821f8..f6713d684 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt @@ -53,7 +53,7 @@ import no.nordicsemi.android.common.navigation.internal.* */ @Composable fun NavigationView( - destinations: NavigationDestinations, + destinations: List, ) { val navHostController = rememberNavController() @@ -84,9 +84,9 @@ fun NavigationView( NavHost( navController = navHostController, - startDestination = destinations.values.first().id.name, + startDestination = destinations.first().id.name, ) { - destinations.values.forEach { destination -> + destinations.forEach { destination -> composable( route = destination.id.name, ) { From 3a564d6061b8b1ec30e4e7721c181cb0aaa92560 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 26 Oct 2022 10:37:37 +0200 Subject: [PATCH 05/10] Clear separation for nullable and non-nullable destination parameters --- .../no/nordicsemi/android/common/navigation/Navigator.kt | 7 ++++--- .../common/navigation/internal/NavigationManager.kt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt index dc5595ce3..c8501a625 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt @@ -23,7 +23,7 @@ interface Navigator { * @param to The destination to navigate to. * @param args An optional argument to pass to the destination. */ - fun navigateTo(to: DestinationId, args: A? = null) + fun navigateTo(to: DestinationId, args: A) /** * Navigates up to previous destination, or finishes the Activity. @@ -59,12 +59,13 @@ fun SavedStateHandle.getStateFlow(destination: DestinationId, initial: * * @param destination The current destination. */ -fun SavedStateHandle.getOrNull(destination: DestinationId): A? = get(destination.name) +fun SavedStateHandle.getOrNull(destination: DestinationId): A? = + get(destination.name) /** * Returns the argument for the current destination. * * @param destination The current destination. */ -fun SavedStateHandle.get(destination: DestinationId): A = +fun SavedStateHandle.get(destination: DestinationId): A = get(destination.name) ?: error("Argument for ${destination.name} not found") \ No newline at end of file diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationManager.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationManager.kt index 8bf4630d5..5d08b6d72 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationManager.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationManager.kt @@ -63,7 +63,7 @@ internal class NavigationManager @Inject constructor( } } ?: throw IllegalStateException("SavedStateHandle is not set") - override fun navigateTo(to: DestinationId, args: A?) { + override fun navigateTo(to: DestinationId, args: A) { executor?.navigate(NavigationTarget(to, args)) } From 9085c5e92c32ff79c22fea6f8a439ec070f7165d Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 26 Oct 2022 10:38:06 +0200 Subject: [PATCH 06/10] Improved API of SimpleNavigationViewModel --- .../viewmodel/SimpleNavigationViewModel.kt | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/viewmodel/SimpleNavigationViewModel.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/viewmodel/SimpleNavigationViewModel.kt index a3cbc086e..3002264c0 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/viewmodel/SimpleNavigationViewModel.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/viewmodel/SimpleNavigationViewModel.kt @@ -19,13 +19,27 @@ open class SimpleNavigationViewModel @Inject constructor( /** * Returns the parameter of the current destination, or null, if hasn't been set. */ - protected fun DestinationId.getParameterOrNull(): A? = - savedStateHandle.getOrNull(this) + fun nullableParameterOf(destinationId: DestinationId): A? { + return savedStateHandle.getOrNull(destinationId) + } + + /** + * Returns the parameter of the current destination, or null, if hasn't been set. + */ + fun parameterOf(destinationId: DestinationId): A { + return savedStateHandle.get(destinationId) + } + + /** + * Returns the parameter of the current destination, or null, if hasn't been set. + */ + protected fun DestinationId.getParameterOrNull(): A? = + nullableParameterOf(this) /** * Returns the parameter of the current destination. */ - protected fun DestinationId.getParameter(): A = - savedStateHandle.get(this) + protected fun DestinationId.getParameter(): A = + parameterOf(this) } \ No newline at end of file From 121176b8e5d96a9e85283207745ca9a5db092707 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 26 Oct 2022 12:22:03 +0200 Subject: [PATCH 07/10] Returning NavigationResult as a result of navigation --- .../common/navigation/NavigationResult.kt | 11 +++++++ .../common/navigation/NavigationView.kt | 9 ++++-- .../android/common/navigation/Navigator.kt | 16 +++++----- .../navigation/internal/NavigationExecutor.kt | 30 +++++++++++++++++-- .../navigation/internal/NavigationManager.kt | 30 ++++--------------- .../internal/NavigationViewModel.kt | 20 +++++++------ 6 files changed, 71 insertions(+), 45 deletions(-) create mode 100644 navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationResult.kt diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationResult.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationResult.kt new file mode 100644 index 000000000..8c260cd10 --- /dev/null +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationResult.kt @@ -0,0 +1,11 @@ +package no.nordicsemi.android.common.navigation + +/** + * The navigation result. + */ +sealed class NavigationResult { + /** Navigation was cancelled. */ + class Cancelled : NavigationResult() + /** The navigation has returned a result. */ + data class Success(val value: R) : NavigationResult() +} \ No newline at end of file diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt index f6713d684..41888784d 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt @@ -42,7 +42,10 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import no.nordicsemi.android.common.navigation.internal.* +import no.nordicsemi.android.common.navigation.internal.Cancelled +import no.nordicsemi.android.common.navigation.internal.NavigationViewModel +import no.nordicsemi.android.common.navigation.internal.NavigationViewModel.Event +import no.nordicsemi.android.common.navigation.internal.navigate /** * A navigation view allows navigating between different destinations. @@ -63,8 +66,8 @@ fun NavigationView( val event by navigation.events.collectAsState() event?.let { e -> when (e) { - is NavigateTo -> navHostController.navigate(e.route, e.args) - is NavigateUp -> { + is Event.NavigateTo -> navHostController.navigate(e.route, e.args) + is Event.NavigateUp -> { val activity = LocalContext.current as Activity // Navigate up to the previous destination, passing the result. navHostController.currentBackStackEntry?.destination?.route?.let { route -> diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt index c8501a625..bbd787fe3 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt @@ -1,3 +1,5 @@ +@file:Suppress("unused") + package no.nordicsemi.android.common.navigation import android.net.Uri @@ -5,25 +7,25 @@ import android.os.Bundle import androidx.lifecycle.SavedStateHandle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import kotlinx.parcelize.RawValue interface Navigator { /** * Creates a flow that will emit the results of the navigation from the given destination. * - * Null is emitted when the navigation was cancelled. - * * @param from The origin destination to listen for results from. */ - fun resultFrom(from: DestinationId<*, R>): Flow + fun resultFrom(from: DestinationId<*, R>): Flow> /** * Requests navigation to the given destination. An optional parameter can be passed. * * @param to The destination to navigate to. - * @param args An optional argument to pass to the destination. + * @param args An optional argument to pass to the destination. The argument will be saved + * in [SavedStateHandle], therefore it must be savable to a [Bundle]. */ - fun navigateTo(to: DestinationId, args: A) + fun navigateTo(to: DestinationId, args: @RawValue A) /** * Navigates up to previous destination, or finishes the Activity. @@ -35,10 +37,10 @@ interface Navigator { * * @param from The destination from which navigating up. * @param result The result, which will be passed to the previous destination. - * The returned type will be saved in [SavedStateHandle], therefore it must be + * The returned object will be saved in [SavedStateHandle], therefore it must be * savable to a [Bundle]. */ - fun navigateUpWithResult(from: DestinationId<*, R>, result: R) + fun navigateUpWithResult(from: DestinationId<*, R>, result: @RawValue R) /** * Opens the given link in a browser. diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationExecutor.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationExecutor.kt index e8f4f01ad..b50e58dbd 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationExecutor.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationExecutor.kt @@ -1,7 +1,33 @@ package no.nordicsemi.android.common.navigation.internal import android.os.Bundle +import android.os.Parcelable +import androidx.core.os.bundleOf import androidx.lifecycle.SavedStateHandle +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.RawValue +import no.nordicsemi.android.common.navigation.DestinationId + +/** + * Navigation target. This class wraps the destination and the parameter. + * + * @property destination The destination id. + * @property args Optional + */ +internal data class NavigationTarget( + val destination: DestinationId, + val args: @RawValue A +) { + fun toBundle() = bundleOf(destination.name to args) +} + +internal sealed class NavigationResultState +@Parcelize +internal object Initial : NavigationResultState(), Parcelable +@Parcelize +internal object Cancelled : NavigationResultState(), Parcelable +@Parcelize +internal data class Success(val value: @RawValue R) : NavigationResultState(), Parcelable /** * A navigation executor that can be used to navigate to next destination, or back. @@ -12,7 +38,7 @@ internal interface NavigationExecutor { * * @param target The target target with an optional parameter. */ - fun navigate(target: NavigationTarget) + fun navigate(target: NavigationTarget) /** * Navigate up to the previous destination passing the given result. @@ -21,5 +47,5 @@ internal interface NavigationExecutor { * The returned type will be saved in [SavedStateHandle], therefore it must be * savable to a [Bundle]. */ - fun navigateUpWithResult(result: NavigationResult) + fun navigateUpWithResult(result: NavigationResultState) } \ No newline at end of file diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationManager.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationManager.kt index 5d08b6d72..354736857 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationManager.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationManager.kt @@ -3,35 +3,17 @@ package no.nordicsemi.android.common.navigation.internal import android.content.Context import android.content.Intent import android.net.Uri -import android.os.Parcelable import android.util.Log -import androidx.core.os.bundleOf import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ActivityRetainedScoped import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.transform -import kotlinx.parcelize.Parcelize -import kotlinx.parcelize.RawValue import no.nordicsemi.android.common.navigation.DestinationId +import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import javax.inject.Inject -internal data class NavigationTarget( - val destination: DestinationId<*, *>, - val args: @RawValue Any? -) { - fun toBundle() = args?.let { bundleOf(destination.name to args) } -} - -internal sealed class NavigationResult -@Parcelize -internal object Initial : NavigationResult(), Parcelable -@Parcelize -internal object Cancelled : NavigationResult(), Parcelable -@Parcelize -internal class Success(val value: @RawValue Any) : NavigationResult(), Parcelable - /** * A navigation manager that can be used to navigate to next destination, or back. * @@ -47,18 +29,18 @@ internal class NavigationManager @Inject constructor( internal var executor: NavigationExecutor? = null internal var savedStateHandle: SavedStateHandle? = null - override fun resultFrom(from: DestinationId<*, R>): Flow = + override fun resultFrom(from: DestinationId<*, R>): Flow> = @Suppress("UNCHECKED_CAST") savedStateHandle?.run { - getStateFlow(from.name, Initial) + getStateFlow(from.name, Initial) .transform { result -> when (result) { // Ignore the initial value. is Initial -> {} // Return success result. - is Success -> emit(result.value as R) + is Success<*> -> emit(NavigationResult.Success(result.value as R)) // Return null when cancelled. - is Cancelled -> emit(null) + is Cancelled -> emit(NavigationResult.Cancelled()) } } } ?: throw IllegalStateException("SavedStateHandle is not set") @@ -68,7 +50,7 @@ internal class NavigationManager @Inject constructor( } override fun navigateUpWithResult(from: DestinationId<*, R>, result: R) { - executor?.navigateUpWithResult(Success(result as Any)) + executor?.navigateUpWithResult(Success(result)) } override fun navigateUp() { diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationViewModel.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationViewModel.kt index 290dd67ea..0249ae6ff 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationViewModel.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/internal/NavigationViewModel.kt @@ -9,15 +9,17 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import javax.inject.Inject -internal sealed class NavigationEvent -internal data class NavigateTo(val route: String, val args: Bundle?) : NavigationEvent() -internal data class NavigateUp(val result: Any?) : NavigationEvent() - @HiltViewModel internal class NavigationViewModel @Inject constructor( private val navigationManager: NavigationManager, ): ViewModel(), NavigationExecutor { - private val _events = MutableStateFlow(null) + /** The navigation events class. */ + sealed class Event { + data class NavigateTo(val route: String, val args: Bundle?) : Event() + data class NavigateUp(val result: Any?) : Event() + } + + private val _events = MutableStateFlow(null) val events = _events.asStateFlow() init { @@ -43,12 +45,12 @@ internal class NavigationViewModel @Inject constructor( _events.update { null } } - override fun navigate(target: NavigationTarget) { - _events.update { NavigateTo(target.destination.name, target.toBundle()) } + override fun navigate(target: NavigationTarget) { + _events.update { Event.NavigateTo(target.destination.name, target.toBundle()) } } - override fun navigateUpWithResult(result: NavigationResult) { - _events.update { NavigateUp(result) } + override fun navigateUpWithResult(result: NavigationResultState) { + _events.update { Event.NavigateUp(result) } } override fun onCleared() { From 1a5bc73deca5423c6e651f0604827e55420b0fb8 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 26 Oct 2022 12:22:53 +0200 Subject: [PATCH 08/10] Sample app migration to new navigation --- .../android/common/test/MainActivity.kt | 6 +-- .../android/common/test/main/Main.kt | 8 ++-- .../common/test/main/page/BasicViewsPage.kt | 9 ++-- .../android/common/test/scanner/Scanner.kt | 46 ++++++------------- 4 files changed, 26 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt b/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt index 71008672d..621e3503a 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt @@ -35,8 +35,8 @@ import android.os.Bundle import androidx.activity.compose.setContent import dagger.hilt.android.AndroidEntryPoint import no.nordicsemi.android.common.navigation.NavigationView -import no.nordicsemi.android.common.test.main.MainDestinations -import no.nordicsemi.android.common.test.scanner.ScannerDestinations +import no.nordicsemi.android.common.test.main.MainDestination +import no.nordicsemi.android.common.test.scanner.ScannerDestination import no.nordicsemi.android.common.theme.NordicActivity import no.nordicsemi.android.common.theme.NordicTheme @@ -48,7 +48,7 @@ class MainActivity : NordicActivity() { setContent { NordicTheme { - NavigationView(MainDestinations + ScannerDestinations) + NavigationView(MainDestination + ScannerDestination) } } } diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt index a07b080d4..228c8f389 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt @@ -24,11 +24,9 @@ val Main = createSimpleDestination("main") * * Optionally, this can define a local Router for routing navigation within the module. */ -private val MainDestination = defineDestination(Main) { - MainScreen() - } - -val MainDestinations = MainDestination.asDestinations() +val MainDestination = defineDestination(Main) { + MainScreen() +} @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt index c0c3e70a8..e1b3c55ac 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt @@ -21,9 +21,8 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.mapNotNull -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* +import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.test.R import no.nordicsemi.android.common.test.scanner.Scanner @@ -58,7 +57,9 @@ class BasicPageViewModel @Inject constructor( init { navigator.resultFrom(Scanner) // Filter out results of cancelled navigation. - .mapNotNull { it } + .filter { it is NavigationResult.Success } + .map { it as NavigationResult.Success } + .map { it.value } // Save the result in SavedStateHandle. .onEach { savedStateHandle[DEVICE_KEY] = it } // And finally, launch the flow in the ViewModelScope. diff --git a/app/src/main/java/no/nordicsemi/android/common/test/scanner/Scanner.kt b/app/src/main/java/no/nordicsemi/android/common/test/scanner/Scanner.kt index 14bbe36d6..f275c8bb3 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/scanner/Scanner.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/scanner/Scanner.kt @@ -1,44 +1,28 @@ package no.nordicsemi.android.common.test.scanner import android.os.ParcelUuid -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import no.nordicsemi.android.common.navigation.* +import no.nordicsemi.android.common.navigation.createDestination +import no.nordicsemi.android.common.navigation.defineDestination +import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewModel import no.nordicsemi.android.common.ui.scanner.DeviceSelected import no.nordicsemi.android.common.ui.scanner.ScannerScreen -import no.nordicsemi.android.common.ui.scanner.ScannerScreenResult import no.nordicsemi.android.common.ui.scanner.ScanningCancelled import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice -import javax.inject.Inject -val Scanner = createDestination("scanner") +val Scanner = createDestination("scanner") -private val ScannerDestination = defineDestination(Scanner) { - val vm = hiltViewModel() - val uuid by vm.uuid.collectAsState() +val ScannerDestination = defineDestination(Scanner) { + val vm: SimpleNavigationViewModel = hiltViewModel() + val uuid = vm.nullableParameterOf(Scanner) - ScannerScreen(uuid = uuid) { - vm.onEvent(it) - } -} - -val ScannerDestinations = ScannerDestination.asDestinations() - -@HiltViewModel -private class ScannerViewModel @Inject constructor( - private val navigator: Navigator, - savedStateHandle: SavedStateHandle, -) : ViewModel() { - val uuid = savedStateHandle.getStateFlow(Scanner, null) - - fun onEvent(event: ScannerScreenResult) { - when (event) { - is DeviceSelected -> navigator.navigateUpWithResult(Scanner, event.device) - ScanningCancelled -> navigator.navigateUp() + ScannerScreen( + uuid = uuid, + onResult = { result -> + when (result) { + is DeviceSelected -> vm.navigateUpWithResult(Scanner, result.device) + is ScanningCancelled -> vm.navigateUp() + } } - } + ) } \ No newline at end of file From 0eb4ced4bfcf8ea168f2255c5b3d1e7f0e354d0a Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 26 Oct 2022 12:56:32 +0200 Subject: [PATCH 09/10] Error message fixed --- .../java/no/nordicsemi/android/common/navigation/Navigator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt index bbd787fe3..00ee5708d 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/Navigator.kt @@ -70,4 +70,4 @@ fun SavedStateHandle.getOrNull(destination: DestinationId): A? = * @param destination The current destination. */ fun SavedStateHandle.get(destination: DestinationId): A = - get(destination.name) ?: error("Argument for ${destination.name} not found") \ No newline at end of file + get(destination.name) ?: error("Destination '${destination.name}' requires a non-nullable argument") \ No newline at end of file From 564ca02be25064b1bd8145b64d6af0898afb06a2 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 26 Oct 2022 13:00:49 +0200 Subject: [PATCH 10/10] Minor improvement --- .../nordicsemi/android/common/test/main/page/BasicViewsPage.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt index e1b3c55ac..c242ba0be 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt @@ -57,8 +57,7 @@ class BasicPageViewModel @Inject constructor( init { navigator.resultFrom(Scanner) // Filter out results of cancelled navigation. - .filter { it is NavigationResult.Success } - .map { it as NavigationResult.Success } + .mapNotNull { it as? NavigationResult.Success } .map { it.value } // Save the result in SavedStateHandle. .onEach { savedStateHandle[DEVICE_KEY] = it }