Skip to content

Commit 0e6b26d

Browse files
committed
[#617] Extract navigation component out of main activity and implement separate navEntry class
1 parent 00cdba1 commit 0e6b26d

File tree

6 files changed

+96
-92
lines changed

6 files changed

+96
-92
lines changed

template-compose/app/src/main/java/co/nimblehq/template/compose/di/modules/main/MainActivityModule.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package co.nimblehq.template.compose.di.modules.main
22

3-
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
43
import co.nimblehq.template.compose.navigation.EntryProviderInstaller
54
import co.nimblehq.template.compose.navigation.Navigator
65
import co.nimblehq.template.compose.navigation.NavigatorImpl
7-
import co.nimblehq.template.compose.ui.screens.main.home.Home
8-
import co.nimblehq.template.compose.ui.screens.main.home.HomeScreen
6+
import co.nimblehq.template.compose.navigation.entries.Home
7+
import co.nimblehq.template.compose.navigation.entries.homeScreenEntry
98
import dagger.Module
109
import dagger.Provides
1110
import dagger.hilt.InstallIn
@@ -24,11 +23,6 @@ object MainActivityModule {
2423
@IntoSet
2524
@Provides
2625
fun provideEntryProviderInstaller(navigator: Navigator): EntryProviderInstaller = {
27-
entry<Home> {
28-
HomeScreen(
29-
viewModel = hiltViewModel(),
30-
navigator = navigator
31-
)
32-
}
26+
homeScreenEntry(navigator)
3327
}
3428
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package co.nimblehq.template.compose.navigation
2+
3+
import androidx.compose.animation.ContentTransform
4+
import androidx.compose.animation.core.tween
5+
import androidx.compose.animation.slideInHorizontally
6+
import androidx.compose.animation.slideOutHorizontally
7+
import androidx.compose.animation.togetherWith
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.runtime.CompositionLocalProvider
10+
import androidx.compose.runtime.remember
11+
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
12+
import androidx.navigation3.runtime.entryProvider
13+
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
14+
import androidx.navigation3.ui.NavDisplay
15+
import co.nimblehq.template.compose.util.LocalResultEventBus
16+
import co.nimblehq.template.compose.util.ResultEventBus
17+
import kotlinx.collections.immutable.ImmutableSet
18+
19+
private const val TWEEN_DURATION_IN_MILLIS = 500
20+
21+
@Composable
22+
fun AppNavigation(
23+
navigator: Navigator,
24+
entryProviderScopes: ImmutableSet<EntryProviderInstaller>,
25+
) {
26+
val eventBus = remember { ResultEventBus() }
27+
CompositionLocalProvider(LocalResultEventBus.provides(eventBus)) {
28+
NavDisplay(
29+
backStack = navigator.backStack,
30+
onBack = { navigator.goBack() },
31+
entryDecorators = listOf(
32+
rememberSaveableStateHolderNavEntryDecorator(),
33+
rememberViewModelStoreNavEntryDecorator()
34+
),
35+
entryProvider = entryProvider {
36+
entryProviderScopes.forEach { builder -> this.builder() }
37+
},
38+
transitionSpec = { horizontalSlideTransition(isPop = false) },
39+
popTransitionSpec = { horizontalSlideTransition(isPop = true) },
40+
predictivePopTransitionSpec = { horizontalSlideTransition(isPop = true) }
41+
)
42+
}
43+
}
44+
45+
private fun horizontalSlideTransition(isPop: Boolean): ContentTransform =
46+
slideInHorizontally(
47+
initialOffsetX = { if (isPop) -it else it },
48+
animationSpec = tween(TWEEN_DURATION_IN_MILLIS)
49+
) togetherWith slideOutHorizontally(
50+
targetOffsetX = { if (isPop) it else -it },
51+
animationSpec = tween(TWEEN_DURATION_IN_MILLIS)
52+
)
53+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@file:Suppress("MatchingDeclarationName")
2+
3+
package co.nimblehq.template.compose.navigation.entries
4+
5+
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
6+
import androidx.navigation3.runtime.EntryProviderScope
7+
import androidx.navigation3.runtime.NavKey
8+
import co.nimblehq.template.compose.navigation.Navigator
9+
import co.nimblehq.template.compose.ui.screens.main.home.HomeScreen
10+
11+
data object Home : NavKey
12+
13+
fun EntryProviderScope<Any>.homeScreenEntry(navigator: Navigator) {
14+
entry<Home> {
15+
HomeScreen(viewModel = hiltViewModel(), navigator = navigator)
16+
}
17+
}

template-compose/app/src/main/java/co/nimblehq/template/compose/ui/screens/MainActivity.kt

Lines changed: 19 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,19 @@ import android.net.Uri
55
import android.os.Bundle
66
import androidx.activity.ComponentActivity
77
import androidx.activity.compose.setContent
8-
import androidx.compose.animation.core.tween
9-
import androidx.compose.animation.slideInHorizontally
10-
import androidx.compose.animation.slideOutHorizontally
11-
import androidx.compose.animation.togetherWith
12-
import androidx.compose.runtime.CompositionLocalProvider
13-
import androidx.compose.runtime.remember
14-
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
15-
import androidx.navigation3.runtime.entryProvider
16-
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
17-
import androidx.navigation3.ui.NavDisplay
188
import co.nimblehq.template.compose.extensions.setEdgeToEdgeConfig
9+
import co.nimblehq.template.compose.navigation.AppNavigation
1910
import co.nimblehq.template.compose.navigation.EntryProviderInstaller
2011
import co.nimblehq.template.compose.navigation.Navigator
2112
import co.nimblehq.template.compose.ui.theme.ComposeTheme
2213
import co.nimblehq.template.compose.util.DeepLinkMatcher
2314
import co.nimblehq.template.compose.util.DeepLinkPattern
2415
import co.nimblehq.template.compose.util.DeepLinkRequest
2516
import co.nimblehq.template.compose.util.KeyDecoder
26-
import co.nimblehq.template.compose.util.LocalResultEventBus
27-
import co.nimblehq.template.compose.util.ResultEventBus
2817
import dagger.hilt.android.AndroidEntryPoint
18+
import kotlinx.collections.immutable.toImmutableSet
2919
import javax.inject.Inject
3020

31-
private const val TWEEN_DURATION_IN_MILLIS = 500
32-
3321
@AndroidEntryPoint
3422
class MainActivity : ComponentActivity() {
3523

@@ -57,49 +45,11 @@ class MainActivity : ComponentActivity() {
5745
setEdgeToEdgeConfig()
5846
handleNewIntent(intent)
5947
setContent {
60-
val eventBus = remember { ResultEventBus() }
61-
6248
ComposeTheme {
63-
CompositionLocalProvider(LocalResultEventBus.provides(eventBus)) {
64-
NavDisplay(
65-
backStack = navigator.backStack,
66-
onBack = { navigator.goBack() },
67-
entryDecorators = listOf(
68-
rememberSaveableStateHolderNavEntryDecorator(),
69-
rememberViewModelStoreNavEntryDecorator()
70-
),
71-
entryProvider = entryProvider {
72-
entryProviderScopes.forEach { builder -> this.builder() }
73-
},
74-
transitionSpec = {
75-
slideInHorizontally(
76-
initialOffsetX = { it },
77-
animationSpec = tween(TWEEN_DURATION_IN_MILLIS)
78-
) togetherWith slideOutHorizontally(
79-
targetOffsetX = { -it },
80-
animationSpec = tween(TWEEN_DURATION_IN_MILLIS)
81-
)
82-
},
83-
popTransitionSpec = {
84-
slideInHorizontally(
85-
initialOffsetX = { -it },
86-
animationSpec = tween(TWEEN_DURATION_IN_MILLIS)
87-
) togetherWith slideOutHorizontally(
88-
targetOffsetX = { it },
89-
animationSpec = tween(TWEEN_DURATION_IN_MILLIS)
90-
)
91-
},
92-
predictivePopTransitionSpec = {
93-
slideInHorizontally(
94-
initialOffsetX = { -it },
95-
animationSpec = tween(TWEEN_DURATION_IN_MILLIS)
96-
) togetherWith slideOutHorizontally(
97-
targetOffsetX = { it },
98-
animationSpec = tween(TWEEN_DURATION_IN_MILLIS)
99-
)
100-
}
101-
)
102-
}
49+
AppNavigation(
50+
navigator = navigator,
51+
entryProviderScopes = entryProviderScopes.toImmutableSet()
52+
)
10353
}
10454
}
10555
}
@@ -110,25 +60,19 @@ class MainActivity : ComponentActivity() {
11060
}
11161

11262
private fun handleNewIntent(intent: Intent) {
113-
val uri: Uri? = intent.data
114-
val deepLinkNavKey: Any? = uri?.let {
115-
val request = DeepLinkRequest(uri)
116-
val match = deepLinkPatterns.firstNotNullOfOrNull { pattern ->
117-
DeepLinkMatcher(request, pattern).match()
118-
}
119-
try {
120-
match?.let {
121-
KeyDecoder(match.args).decodeSerializableValue(match.serializer)
122-
}
123-
} catch (_: Exception) {
124-
intent.data = null
125-
null
126-
}
127-
}
63+
val uri = intent.data ?: return
64+
val deepLinkNavKey = resolveDeepLinkNavKey(uri)
65+
intent.data = null
66+
if (deepLinkNavKey != null) navigator.goTo(deepLinkNavKey)
67+
}
12868

129-
if (deepLinkNavKey != null) {
130-
navigator.goTo(deepLinkNavKey)
131-
intent.data = null
132-
}
69+
private fun resolveDeepLinkNavKey(uri: Uri): Any? {
70+
val request = DeepLinkRequest(uri)
71+
val match = deepLinkPatterns.firstNotNullOfOrNull { pattern ->
72+
DeepLinkMatcher(request, pattern).match()
73+
} ?: return null
74+
return try {
75+
KeyDecoder(match.args).decodeSerializableValue(match.serializer)
76+
} catch (_: Exception) { null }
13377
}
13478
}

template-compose/app/src/main/java/co/nimblehq/template/compose/ui/screens/main/home/HomeScreen.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
@file:Suppress("MatchingDeclarationName")
2-
31
package co.nimblehq.template.compose.ui.screens.main.home
42

53
import androidx.compose.foundation.layout.Arrangement
@@ -17,7 +15,6 @@ import androidx.compose.ui.text.style.TextAlign
1715
import androidx.compose.ui.tooling.preview.Preview
1816
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
1917
import androidx.lifecycle.compose.collectAsStateWithLifecycle
20-
import androidx.navigation3.runtime.NavKey
2118
import co.nimblehq.template.compose.R
2219
import co.nimblehq.template.compose.extensions.collectAsEffect
2320
import co.nimblehq.template.compose.navigation.Navigator
@@ -26,11 +23,10 @@ import co.nimblehq.template.compose.ui.models.UiModel
2623
import co.nimblehq.template.compose.ui.showToast
2724
import co.nimblehq.template.compose.ui.theme.AppTheme.dimensions
2825
import co.nimblehq.template.compose.ui.theme.ComposeTheme
29-
import kotlinx.collections.immutable.*
26+
import kotlinx.collections.immutable.ImmutableList
27+
import kotlinx.collections.immutable.persistentListOf
3028
import timber.log.Timber
3129

32-
data object Home : NavKey
33-
3430
@Composable
3531
fun HomeScreen(
3632
navigator: Navigator,
@@ -51,7 +47,7 @@ fun HomeScreen(
5147
@Composable
5248
private fun HomeScreenContent(
5349
title: String,
54-
uiModels: ImmutableList<UiModel>
50+
uiModels: ImmutableList<UiModel>,
5551
) {
5652
Column(
5753
modifier = Modifier.fillMaxSize(),

template-compose/app/src/test/java/co/nimblehq/template/compose/ui/screens/FakeNavigator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package co.nimblehq.template.compose.ui.screens
33
import androidx.compose.runtime.mutableStateListOf
44
import androidx.compose.runtime.snapshots.SnapshotStateList
55
import co.nimblehq.template.compose.navigation.Navigator
6-
import co.nimblehq.template.compose.ui.screens.main.home.Home
6+
import co.nimblehq.template.compose.navigation.entries.Home
77
import kotlin.reflect.KClass
88

99
class FakeNavigator(startDestination: Any = Home) : Navigator {

0 commit comments

Comments
 (0)