Skip to content

Commit e31d2ef

Browse files
committed
Refactor app to navigation3
1 parent 6c57fce commit e31d2ef

File tree

14 files changed

+158
-572
lines changed

14 files changed

+158
-572
lines changed

app/build.gradle.kts

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

7373
dependencies {
7474
implementation(projects.feature.interests.api)
75+
implementation(projects.feature.interests.impl)
7576
implementation(projects.feature.foryou.api)
77+
implementation(projects.feature.foryou.impl)
7678
implementation(projects.feature.bookmarks.api)
79+
implementation(projects.feature.bookmarks.impl)
7780
implementation(projects.feature.topic.api)
81+
implementation(projects.feature.topic.impl)
7882
implementation(projects.feature.search.api)
83+
implementation(projects.feature.search.impl)
7984
implementation(projects.feature.settings.api)
8085

8186
implementation(projects.core.common)
@@ -85,10 +90,13 @@ dependencies {
8590
implementation(projects.core.model)
8691
implementation(projects.core.navigation)
8792
implementation(projects.core.analytics)
93+
implementation(projects.core.navigation)
8894
implementation(projects.sync.work)
8995

9096
implementation(libs.androidx.activity.compose)
9197
implementation(libs.androidx.compose.material3)
98+
implementation(libs.androidx.navigation3.runtime)
99+
implementation(libs.androidx.navigation3.ui)
92100
implementation(libs.androidx.compose.material3.adaptive)
93101
implementation(libs.androidx.compose.material3.adaptive.layout)
94102
implementation(libs.androidx.compose.material3.adaptive.navigation)

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

Lines changed: 11 additions & 1 deletion
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,8 @@ 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.navigation.LocalEntryProviderBuilder
46+
import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack
4447
import com.google.samples.apps.nowinandroid.ui.NiaApp
4548
import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState
4649
import com.google.samples.apps.nowinandroid.util.isSystemInDarkTheme
@@ -72,9 +75,14 @@ class MainActivity : ComponentActivity() {
7275

7376
@Inject
7477
lateinit var userNewsResourceRepository: UserNewsResourceRepository
75-
7678
private val viewModel: MainActivityViewModel by viewModels()
7779

80+
@Inject
81+
lateinit var niaBackStack: NiaBackStack
82+
83+
@Inject
84+
lateinit var entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder<Any>.() -> Unit>
85+
7886
override fun onCreate(savedInstanceState: Bundle?) {
7987
val splashScreen = installSplashScreen()
8088
super.onCreate(savedInstanceState)
@@ -137,13 +145,15 @@ class MainActivity : ComponentActivity() {
137145
networkMonitor = networkMonitor,
138146
userNewsResourceRepository = userNewsResourceRepository,
139147
timeZoneMonitor = timeZoneMonitor,
148+
niaBackStack = niaBackStack,
140149
)
141150

142151
val currentTimeZone by appState.currentTimeZone.collectAsStateWithLifecycle()
143152

144153
CompositionLocalProvider(
145154
LocalAnalyticsHelper provides analyticsHelper,
146155
LocalTimeZone provides currentTimeZone,
156+
LocalEntryProviderBuilder provides entryProviderBuilders
147157
) {
148158
NiaTheme(
149159
darkTheme = themeSettings.darkTheme,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.di
1919
import androidx.compose.runtime.snapshots.SnapshotStateList
2020
import com.google.samples.apps.nowinandroid.core.navigation.BackStackFactory
2121
import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack
22-
import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute
22+
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination
2323
import dagger.Module
2424
import dagger.Provides
2525
import dagger.hilt.InstallIn
@@ -37,5 +37,5 @@ object NiaAppNavigation {
3737
@Provides
3838
@Singleton
3939
fun provideNiaBackStack(backStack: SnapshotStateList<Any>): NiaBackStack =
40-
NiaBackStack(backStack, startKey = ForYouRoute)
40+
NiaBackStack(backStack, startKey = TopLevelDestination.FOR_YOU.key)
4141
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
) {
32+
NavDisplay(
33+
backStack = niaBackStack.backStack,
34+
onBack = { niaBackStack.removeLast() },
35+
entryProvider = entryProvider {
36+
LocalEntryProviderBuilder.current.forEach { builder ->
37+
builder()
38+
}
39+
},
40+
)
41+
}
42+
43+
internal val LocalEntryProviderBuilder = compositionLocalOf<Set<@JvmSuppressWildcards EntryProviderBuilder<Any>.() -> Unit>> {
44+
emptySet()
45+
}

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: 27 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
@@ -71,8 +70,9 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopAp
7170
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
7271
import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors
7372
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors
73+
import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.LocalSnackbarHostState
7474
import com.google.samples.apps.nowinandroid.feature.settings.api.SettingsDialog
75-
import com.google.samples.apps.nowinandroid.navigation.NiaNavHost
75+
import com.google.samples.apps.nowinandroid.navigation.NiaNavDisplay
7676
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination
7777
import kotlin.reflect.KClass
7878
import com.google.samples.apps.nowinandroid.feature.settings.api.R as settingsR
@@ -109,15 +109,15 @@ fun NiaApp(
109109
)
110110
}
111111
}
112-
113-
NiaApp(
114-
appState = appState,
115-
snackbarHostState = snackbarHostState,
116-
showSettingsDialog = showSettingsDialog,
117-
onSettingsDismissed = { showSettingsDialog = false },
118-
onTopAppBarActionClick = { showSettingsDialog = true },
119-
windowAdaptiveInfo = windowAdaptiveInfo,
120-
)
112+
CompositionLocalProvider(LocalSnackbarHostState provides snackbarHostState) {
113+
NiaApp(
114+
appState = appState,
115+
showSettingsDialog = showSettingsDialog,
116+
onSettingsDismissed = { showSettingsDialog = false },
117+
onTopAppBarActionClick = { showSettingsDialog = true },
118+
windowAdaptiveInfo = windowAdaptiveInfo,
119+
)
120+
}
121121
}
122122
}
123123
}
@@ -129,7 +129,6 @@ fun NiaApp(
129129
)
130130
internal fun NiaApp(
131131
appState: NiaAppState,
132-
snackbarHostState: SnackbarHostState,
133132
showSettingsDialog: Boolean,
134133
onSettingsDismissed: () -> Unit,
135134
onTopAppBarActionClick: () -> Unit,
@@ -138,20 +137,25 @@ internal fun NiaApp(
138137
) {
139138
val unreadDestinations by appState.topLevelDestinationsWithUnreadResources
140139
.collectAsStateWithLifecycle()
141-
val currentDestination = appState.currentDestination
140+
val currentTopLevelKey = appState.currentTopLevelDestination
141+
142142

143143
if (showSettingsDialog) {
144144
SettingsDialog(
145145
onDismiss = { onSettingsDismissed() },
146146
)
147147
}
148148

149+
val snackbarHostState = LocalSnackbarHostState.current
150+
149151
NiaNavigationSuiteScaffold(
150152
navigationSuiteItems = {
151153
appState.topLevelDestinations.forEach { destination ->
152154
val hasUnread = unreadDestinations.contains(destination)
153-
val selected = currentDestination
154-
.isRouteInHierarchy(destination.baseRoute)
155+
// val selected = currentDestination
156+
// .isRouteInHierarchy(destination.baseRoute)
157+
val selected = destination.key == currentTopLevelKey
158+
println("cfok destination:$destination, currentDest:$currentTopLevelKey")
155159
item(
156160
selected = selected,
157161
onClick = { appState.navigateToTopLevelDestination(destination) },
@@ -225,7 +229,7 @@ internal fun NiaApp(
225229
containerColor = Color.Transparent,
226230
),
227231
onActionClick = { onTopAppBarActionClick() },
228-
onNavigationClick = { appState.navigateToSearch() },
232+
onNavigationClick = { appState.navigateToSearchNav3() },
229233
)
230234
}
231235

@@ -239,15 +243,12 @@ internal fun NiaApp(
239243
},
240244
),
241245
) {
242-
NiaNavHost(
243-
appState = appState,
244-
onShowSnackbar = { message, action ->
245-
snackbarHostState.showSnackbar(
246-
message = message,
247-
actionLabel = action,
248-
duration = Short,
249-
) == ActionPerformed
250-
},
246+
// NiaNavHost(
247+
// appState = appState,
248+
// onShowSnackbar = onShowSnackbar,
249+
// )
250+
NiaNavDisplay(
251+
niaBackStack = appState.niaBackStack,
251252
)
252253
}
253254

0 commit comments

Comments
 (0)