Skip to content

Commit d4087b7

Browse files
authored
Merge pull request #1681 from android/dt/improve-navigation
Fix topic chip navigation from ForYou screen
2 parents ec0868c + 18bb7a4 commit d4087b7

File tree

23 files changed

+307
-169
lines changed

23 files changed

+307
-169
lines changed

app-nia-catalog/dependencies/releaseRuntimeClasspath.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
androidx.activity:activity-compose:1.9.2
2-
androidx.activity:activity-ktx:1.9.2
3-
androidx.activity:activity:1.9.2
1+
androidx.activity:activity-compose:1.9.3
2+
androidx.activity:activity-ktx:1.9.3
3+
androidx.activity:activity:1.9.3
44
androidx.annotation:annotation-experimental:1.4.0
55
androidx.annotation:annotation-jvm:1.8.0
66
androidx.annotation:annotation:1.8.0

app/dependencies/prodReleaseRuntimeClasspath.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
androidx.activity:activity-compose:1.9.2
2-
androidx.activity:activity-ktx:1.9.2
3-
androidx.activity:activity:1.9.2
1+
androidx.activity:activity-compose:1.9.3
2+
androidx.activity:activity-ktx:1.9.3
3+
androidx.activity:activity:1.9.3
44
androidx.annotation:annotation-experimental:1.4.1
55
androidx.annotation:annotation-jvm:1.8.1
66
androidx.annotation:annotation:1.8.1
@@ -196,11 +196,11 @@ com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
196196
com.google.j2objc:j2objc-annotations:1.3
197197
com.google.protobuf:protobuf-javalite:4.26.1
198198
com.google.protobuf:protobuf-kotlin-lite:4.26.1
199-
com.squareup.retrofit2:converter-kotlinx-serialization:2.11.0
200199
com.squareup.okhttp3:logging-interceptor:4.12.0
201200
com.squareup.okhttp3:okhttp:4.12.0
202201
com.squareup.okio:okio-jvm:3.9.0
203202
com.squareup.okio:okio:3.9.0
203+
com.squareup.retrofit2:converter-kotlinx-serialization:2.11.0
204204
com.squareup.retrofit2:retrofit:2.11.0
205205
io.coil-kt:coil-base:2.7.0
206206
io.coil-kt:coil-compose-base:2.7.0

app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616

1717
package com.google.samples.apps.nowinandroid.ui
1818

19+
import androidx.compose.ui.semantics.SemanticsActions.ScrollBy
1920
import androidx.compose.ui.test.assertCountEquals
2021
import androidx.compose.ui.test.assertIsOn
2122
import androidx.compose.ui.test.assertIsSelected
2223
import androidx.compose.ui.test.hasTestTag
2324
import androidx.compose.ui.test.hasText
2425
import androidx.compose.ui.test.junit4.createAndroidComposeRule
26+
import androidx.compose.ui.test.onAllNodesWithTag
2527
import androidx.compose.ui.test.onAllNodesWithText
28+
import androidx.compose.ui.test.onFirst
2629
import androidx.compose.ui.test.onNodeWithContentDescription
2730
import androidx.compose.ui.test.onNodeWithTag
2831
import androidx.compose.ui.test.onNodeWithText
@@ -32,6 +35,7 @@ import androidx.test.espresso.Espresso
3235
import androidx.test.espresso.NoActivityResumedException
3336
import com.google.samples.apps.nowinandroid.MainActivity
3437
import com.google.samples.apps.nowinandroid.R
38+
import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository
3539
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
3640
import com.google.samples.apps.nowinandroid.core.model.data.Topic
3741
import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule
@@ -75,6 +79,9 @@ class NavigationTest {
7579
@Inject
7680
lateinit var topicsRepository: TopicsRepository
7781

82+
@Inject
83+
lateinit var newsRepository: NewsRepository
84+
7885
// The strings used for matching in these tests
7986
private val navigateUp by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_navigate_up)
8087
private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title)
@@ -267,4 +274,44 @@ class NavigationTest {
267274
onNodeWithTag("topic:${topic.id}").assertExists()
268275
}
269276
}
277+
278+
@Test
279+
fun navigatingToTopicFromForYou_showsTopicDetails() {
280+
composeTestRule.apply {
281+
// Get the first news resource
282+
val newsResource = runBlocking {
283+
newsRepository.getNewsResources().first().first()
284+
}
285+
286+
// Get its first topic and follow it
287+
val topic = newsResource.topics.first()
288+
onNodeWithText(topic.name).performClick()
289+
290+
// Get the news feed and scroll to the news resource
291+
// Note: Possible flakiness. If the content of the news resource is long then the topic
292+
// tag might not be visible meaning it cannot be clicked
293+
onNodeWithTag("forYou:feed")
294+
.performScrollToNode(hasTestTag("newsResourceCard:${newsResource.id}"))
295+
.fetchSemanticsNode()
296+
.apply {
297+
val newsResourceCardNode = onNodeWithTag("newsResourceCard:${newsResource.id}")
298+
.fetchSemanticsNode()
299+
config[ScrollBy].action?.invoke(
300+
0f,
301+
// to ensure the bottom of the card is visible,
302+
// manually scroll the difference between the height of
303+
// the scrolling node and the height of the card
304+
(newsResourceCardNode.size.height - size.height).coerceAtLeast(0).toFloat(),
305+
)
306+
}
307+
308+
// Click the first topic tag
309+
onAllNodesWithTag("topicTag:${topic.id}", useUnmergedTree = true)
310+
.onFirst()
311+
.performClick()
312+
313+
// Verify that we're on the correct topic details screen
314+
onNodeWithTag("topic:${topic.id}").assertExists()
315+
}
316+
}
270317
}

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import androidx.compose.runtime.Composable
2020
import androidx.compose.ui.Modifier
2121
import androidx.navigation.compose.NavHost
2222
import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.bookmarksScreen
23-
import com.google.samples.apps.nowinandroid.feature.foryou.navigation.ForYouRoute
24-
import com.google.samples.apps.nowinandroid.feature.foryou.navigation.forYouScreen
23+
import com.google.samples.apps.nowinandroid.feature.foryou.navigation.ForYouBaseRoute
24+
import com.google.samples.apps.nowinandroid.feature.foryou.navigation.forYouSection
2525
import com.google.samples.apps.nowinandroid.feature.interests.navigation.navigateToInterests
2626
import com.google.samples.apps.nowinandroid.feature.search.navigation.searchScreen
27+
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
28+
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen
2729
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.INTERESTS
2830
import com.google.samples.apps.nowinandroid.ui.NiaAppState
2931
import com.google.samples.apps.nowinandroid.ui.interests2pane.interestsListDetailScreen
@@ -44,10 +46,18 @@ fun NiaNavHost(
4446
val navController = appState.navController
4547
NavHost(
4648
navController = navController,
47-
startDestination = ForYouRoute,
49+
startDestination = ForYouBaseRoute,
4850
modifier = modifier,
4951
) {
50-
forYouScreen(onTopicClick = navController::navigateToInterests)
52+
forYouSection(
53+
onTopicClick = navController::navigateToTopic,
54+
) {
55+
topicScreen(
56+
showBackButton = true,
57+
onBackClick = navController::popBackStack,
58+
onTopicClick = navController::navigateToTopic,
59+
)
60+
}
5161
bookmarksScreen(
5262
onTopicClick = navController::navigateToInterests,
5363
onShowSnackbar = onShowSnackbar,

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
2121
import com.google.samples.apps.nowinandroid.R
2222
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
2323
import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.BookmarksRoute
24+
import com.google.samples.apps.nowinandroid.feature.foryou.navigation.ForYouBaseRoute
2425
import com.google.samples.apps.nowinandroid.feature.foryou.navigation.ForYouRoute
2526
import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsRoute
2627
import kotlin.reflect.KClass
@@ -29,23 +30,34 @@ import com.google.samples.apps.nowinandroid.feature.foryou.R as forYouR
2930
import com.google.samples.apps.nowinandroid.feature.search.R as searchR
3031

3132
/**
32-
* Type for the top level destinations in the application. Each of these destinations
33-
* can contain one or more screens (based on the window size). Navigation from one screen to the
34-
* next within a single destination will be handled directly in composables.
33+
* Type for the top level destinations in the application. Contains metadata about the destination
34+
* that is used in the top app bar and common navigation UI.
35+
*
36+
* @param selectedIcon The icon to be displayed in the navigation UI when this destination is
37+
* selected.
38+
* @param unselectedIcon The icon to be displayed in the navigation UI when this destination is
39+
* not selected.
40+
* @param iconTextId Text that to be displayed in the navigation UI.
41+
* @param titleTextId Text that is displayed on the top app bar.
42+
* @param route The route to use when navigating to this destination.
43+
* @param baseRoute The highest ancestor of this destination. Defaults to [route], meaning that
44+
* there is a single destination in that section of the app (no nested destinations).
3545
*/
3646
enum class TopLevelDestination(
3747
val selectedIcon: ImageVector,
3848
val unselectedIcon: ImageVector,
3949
@StringRes val iconTextId: Int,
4050
@StringRes val titleTextId: Int,
4151
val route: KClass<*>,
52+
val baseRoute: KClass<*> = route,
4253
) {
4354
FOR_YOU(
4455
selectedIcon = NiaIcons.Upcoming,
4556
unselectedIcon = NiaIcons.UpcomingBorder,
4657
iconTextId = forYouR.string.feature_foryou_title,
4758
titleTextId = R.string.app_name,
4859
route = ForYouRoute::class,
60+
baseRoute = ForYouBaseRoute::class,
4961
),
5062
BOOKMARKS(
5163
selectedIcon = NiaIcons.Bookmarks,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ internal fun NiaApp(
152152
appState.topLevelDestinations.forEach { destination ->
153153
val hasUnread = unreadDestinations.contains(destination)
154154
val selected = currentDestination
155-
.isRouteInHierarchy(destination.route)
155+
.isRouteInHierarchy(destination.baseRoute)
156156
item(
157157
selected = selected,
158158
onClick = { appState.navigateToTopLevelDestination(destination) },

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class NiaAppState(
9090
val currentTopLevelDestination: TopLevelDestination?
9191
@Composable get() {
9292
return TopLevelDestination.entries.firstOrNull { topLevelDestination ->
93-
currentDestination?.hasRoute(route = topLevelDestination.route) ?: false
93+
currentDestination?.hasRoute(route = topLevelDestination.route) == true
9494
}
9595
}
9696

-6.67 KB
Loading
-9.02 KB
Loading
1.4 KB
Loading

0 commit comments

Comments
 (0)