Skip to content

Commit 04fa1ff

Browse files
committed
Refactor app to navigation3
1 parent b96b3e9 commit 04fa1ff

File tree

15 files changed

+178
-572
lines changed

15 files changed

+178
-572
lines changed

app/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,15 @@ android {
6868

6969
dependencies {
7070
implementation(projects.feature.interests.api)
71+
implementation(projects.feature.interests.impl)
7172
implementation(projects.feature.foryou.api)
73+
implementation(projects.feature.foryou.impl)
7274
implementation(projects.feature.bookmarks.api)
75+
implementation(projects.feature.bookmarks.impl)
7376
implementation(projects.feature.topic.api)
77+
implementation(projects.feature.topic.impl)
7478
implementation(projects.feature.search.api)
79+
implementation(projects.feature.search.impl)
7580
implementation(projects.feature.settings.api)
7681

7782
implementation(projects.core.common)
@@ -81,10 +86,13 @@ dependencies {
8186
implementation(projects.core.model)
8287
implementation(projects.core.navigation)
8388
implementation(projects.core.analytics)
89+
implementation(projects.core.navigation)
8490
implementation(projects.sync.work)
8591

8692
implementation(libs.androidx.activity.compose)
8793
implementation(libs.androidx.compose.material3)
94+
implementation(libs.androidx.navigation3.runtime)
95+
implementation(libs.androidx.navigation3.ui)
8896
implementation(libs.androidx.compose.material3.adaptive)
8997
implementation(libs.androidx.compose.material3.adaptive.layout)
9098
implementation(libs.androidx.compose.material3.adaptive.navigation)

app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
3232
import androidx.lifecycle.lifecycleScope
3333
import androidx.lifecycle.repeatOnLifecycle
3434
import androidx.metrics.performance.JankStats
35+
import androidx.navigation3.runtime.EntryProviderBuilder
3536
import androidx.tracing.trace
3637
import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading
3738
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
@@ -41,6 +42,7 @@ import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
4142
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
4243
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
4344
import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone
45+
import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack
4446
import com.google.samples.apps.nowinandroid.ui.NiaApp
4547
import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState
4648
import com.google.samples.apps.nowinandroid.util.isSystemInDarkTheme
@@ -72,9 +74,14 @@ class MainActivity : ComponentActivity() {
7274

7375
@Inject
7476
lateinit var userNewsResourceRepository: UserNewsResourceRepository
75-
7677
private val viewModel: MainActivityViewModel by viewModels()
7778

79+
@Inject
80+
lateinit var niaBackStack: NiaBackStack
81+
82+
@Inject
83+
lateinit var entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder<Any>.() -> Unit>
84+
7885
override fun onCreate(savedInstanceState: Bundle?) {
7986
val splashScreen = installSplashScreen()
8087
super.onCreate(savedInstanceState)
@@ -137,6 +144,7 @@ class MainActivity : ComponentActivity() {
137144
networkMonitor = networkMonitor,
138145
userNewsResourceRepository = userNewsResourceRepository,
139146
timeZoneMonitor = timeZoneMonitor,
147+
niaBackStack = niaBackStack,
140148
)
141149

142150
val currentTimeZone by appState.currentTimeZone.collectAsStateWithLifecycle()
@@ -150,7 +158,10 @@ class MainActivity : ComponentActivity() {
150158
androidTheme = themeSettings.androidTheme,
151159
disableDynamicTheming = themeSettings.disableDynamicTheming,
152160
) {
153-
NiaApp(appState)
161+
NiaApp(
162+
appState,
163+
entryProviderBuilders
164+
)
154165
}
155166
}
156167
}

app/src/main/kotlin/com/google/samples/apps/nowinandroid/di/BackStackProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ object NiaAppNavigation {
3030
@Provides
3131
@Singleton
3232
fun provideNiaBackStack(): NiaBackStack =
33-
NiaBackStack(startKey = TopLevelDestination.FOR_YOU)
33+
NiaBackStack(startKey = TopLevelDestination.FOR_YOU.key)
3434
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.samples.apps.nowinandroid.navigation
18+
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.runtime.compositionLocalOf
21+
import androidx.compose.runtime.snapshots.SnapshotStateList
22+
import androidx.navigation3.runtime.EntryProviderBuilder
23+
import androidx.navigation3.runtime.NavEntryDecorator
24+
import androidx.navigation3.runtime.entryProvider
25+
import androidx.navigation3.ui.NavDisplay
26+
import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack
27+
28+
@Composable
29+
fun NiaNavDisplay(
30+
niaBackStack: NiaBackStack,
31+
entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder<Any>.() -> Unit>,
32+
) {
33+
NavDisplay(
34+
backStack = niaBackStack.backStack,
35+
onBack = { niaBackStack.removeLast() },
36+
entryProvider = entryProvider {
37+
entryProviderBuilders.forEach { builder ->
38+
builder()
39+
}
40+
},
41+
)
42+
}

app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigat
2828
import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.topicScreen
2929
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.INTERESTS
3030
import com.google.samples.apps.nowinandroid.ui.NiaAppState
31-
import com.google.samples.apps.nowinandroid.ui.interests2pane.interestsListDetailScreen
31+
import com.google.samples.apps.nowinandroid.feature.interests.impl.interestsListDetailScreen
3232

3333
/**
3434
* Top-level navigation graph. Navigation is organized as explained at

app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ enum class TopLevelDestination(
5050
@StringRes val titleTextId: Int,
5151
val route: KClass<*>,
5252
val baseRoute: KClass<*> = route,
53+
val key: Any
5354
) {
5455
FOR_YOU(
5556
selectedIcon = NiaIcons.Upcoming,
@@ -58,19 +59,22 @@ enum class TopLevelDestination(
5859
titleTextId = R.string.app_name,
5960
route = ForYouRoute::class,
6061
baseRoute = ForYouBaseRoute::class,
62+
key = ForYouBaseRoute
6163
),
6264
BOOKMARKS(
6365
selectedIcon = NiaIcons.Bookmarks,
6466
unselectedIcon = NiaIcons.BookmarksBorder,
6567
iconTextId = bookmarksR.string.feature_bookmarks_impl_title,
6668
titleTextId = bookmarksR.string.feature_bookmarks_impl_title,
6769
route = BookmarksRoute::class,
70+
key = BookmarksRoute
6871
),
6972
INTERESTS(
7073
selectedIcon = NiaIcons.Grid3x3,
7174
unselectedIcon = NiaIcons.Grid3x3,
7275
iconTextId = searchR.string.feature_search_api_interests,
7376
titleTextId = searchR.string.feature_search_api_interests,
7477
route = InterestsRoute::class,
78+
key = InterestsRoute(null)
7579
),
7680
}

app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,14 @@ import androidx.compose.material3.Icon
3333
import androidx.compose.material3.MaterialTheme
3434
import androidx.compose.material3.Scaffold
3535
import androidx.compose.material3.SnackbarDuration.Indefinite
36-
import androidx.compose.material3.SnackbarDuration.Short
3736
import androidx.compose.material3.SnackbarHost
3837
import androidx.compose.material3.SnackbarHostState
39-
import androidx.compose.material3.SnackbarResult.ActionPerformed
4038
import androidx.compose.material3.Text
4139
import androidx.compose.material3.TopAppBarDefaults
4240
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
4341
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
4442
import androidx.compose.runtime.Composable
43+
import androidx.compose.runtime.CompositionLocalProvider
4544
import androidx.compose.runtime.LaunchedEffect
4645
import androidx.compose.runtime.getValue
4746
import androidx.compose.runtime.mutableStateOf
@@ -63,6 +62,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
6362
import androidx.navigation.NavDestination
6463
import androidx.navigation.NavDestination.Companion.hasRoute
6564
import androidx.navigation.NavDestination.Companion.hierarchy
65+
import androidx.navigation3.runtime.EntryProviderBuilder
6666
import com.google.samples.apps.nowinandroid.R
6767
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground
6868
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground
@@ -71,15 +71,17 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopAp
7171
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
7272
import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors
7373
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors
74+
import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.LocalSnackbarHostState
7475
import com.google.samples.apps.nowinandroid.feature.settings.api.SettingsDialog
75-
import com.google.samples.apps.nowinandroid.navigation.NiaNavHost
76+
import com.google.samples.apps.nowinandroid.navigation.NiaNavDisplay
7677
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination
7778
import kotlin.reflect.KClass
7879
import com.google.samples.apps.nowinandroid.feature.settings.api.R as settingsR
7980

8081
@Composable
8182
fun NiaApp(
8283
appState: NiaAppState,
84+
entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder<Any>.() -> Unit>,
8385
modifier: Modifier = Modifier,
8486
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(),
8587
) {
@@ -109,15 +111,16 @@ fun NiaApp(
109111
)
110112
}
111113
}
112-
113-
NiaApp(
114-
appState = appState,
115-
snackbarHostState = snackbarHostState,
116-
showSettingsDialog = showSettingsDialog,
117-
onSettingsDismissed = { showSettingsDialog = false },
118-
onTopAppBarActionClick = { showSettingsDialog = true },
119-
windowAdaptiveInfo = windowAdaptiveInfo,
120-
)
114+
CompositionLocalProvider(LocalSnackbarHostState provides snackbarHostState) {
115+
NiaApp(
116+
appState = appState,
117+
entryProviderBuilders = entryProviderBuilders,
118+
showSettingsDialog = showSettingsDialog,
119+
onSettingsDismissed = { showSettingsDialog = false },
120+
onTopAppBarActionClick = { showSettingsDialog = true },
121+
windowAdaptiveInfo = windowAdaptiveInfo,
122+
)
123+
}
121124
}
122125
}
123126
}
@@ -129,7 +132,7 @@ fun NiaApp(
129132
)
130133
internal fun NiaApp(
131134
appState: NiaAppState,
132-
snackbarHostState: SnackbarHostState,
135+
entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder<Any>.() -> Unit>,
133136
showSettingsDialog: Boolean,
134137
onSettingsDismissed: () -> Unit,
135138
onTopAppBarActionClick: () -> Unit,
@@ -138,20 +141,25 @@ internal fun NiaApp(
138141
) {
139142
val unreadDestinations by appState.topLevelDestinationsWithUnreadResources
140143
.collectAsStateWithLifecycle()
141-
val currentDestination = appState.currentDestination
144+
val currentTopLevelKey = appState.currentTopLevelDestination
145+
142146

143147
if (showSettingsDialog) {
144148
SettingsDialog(
145149
onDismiss = { onSettingsDismissed() },
146150
)
147151
}
148152

153+
val snackbarHostState = LocalSnackbarHostState.current
154+
149155
NiaNavigationSuiteScaffold(
150156
navigationSuiteItems = {
151157
appState.topLevelDestinations.forEach { destination ->
152158
val hasUnread = unreadDestinations.contains(destination)
153-
val selected = currentDestination
154-
.isRouteInHierarchy(destination.baseRoute)
159+
// val selected = currentDestination
160+
// .isRouteInHierarchy(destination.baseRoute)
161+
val selected = destination.key == currentTopLevelKey
162+
println("cfok destination:$destination, currentDest:$currentTopLevelKey")
155163
item(
156164
selected = selected,
157165
onClick = { appState.navigateToTopLevelDestination(destination) },
@@ -225,7 +233,7 @@ internal fun NiaApp(
225233
containerColor = Color.Transparent,
226234
),
227235
onActionClick = { onTopAppBarActionClick() },
228-
onNavigationClick = { appState.navigateToSearch() },
236+
onNavigationClick = { appState.navigateToSearchNav3() },
229237
)
230238
}
231239

@@ -239,15 +247,13 @@ internal fun NiaApp(
239247
},
240248
),
241249
) {
242-
NiaNavHost(
243-
appState = appState,
244-
onShowSnackbar = { message, action ->
245-
snackbarHostState.showSnackbar(
246-
message = message,
247-
actionLabel = action,
248-
duration = Short,
249-
) == ActionPerformed
250-
},
250+
// NiaNavHost(
251+
// appState = appState,
252+
// onShowSnackbar = onShowSnackbar,
253+
// )
254+
NiaNavDisplay(
255+
niaBackStack = appState.niaBackStack,
256+
entryProviderBuilders,
251257
)
252258
}
253259

0 commit comments

Comments
 (0)