Merge branch 'android:main' into main

pull/1808/head
CaptainZ 6 months ago committed by GitHub
commit b7605ab03d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -9,48 +9,48 @@ androidx.arch.core:core-common:2.2.0
androidx.arch.core:core-runtime:2.2.0
androidx.autofill:autofill:1.0.0
androidx.browser:browser:1.8.0
androidx.collection:collection-jvm:1.5.0-beta01
androidx.collection:collection-ktx:1.5.0-beta01
androidx.collection:collection:1.5.0-beta01
androidx.compose.animation:animation-android:1.8.0-alpha07
androidx.compose.animation:animation-core-android:1.8.0-alpha07
androidx.compose.animation:animation-core:1.8.0-alpha07
androidx.compose.animation:animation:1.8.0-alpha07
androidx.compose.foundation:foundation-android:1.8.0-alpha07
androidx.compose.foundation:foundation-layout-android:1.8.0-alpha07
androidx.compose.foundation:foundation-layout:1.8.0-alpha07
androidx.compose.foundation:foundation:1.8.0-alpha07
androidx.compose.material3.adaptive:adaptive-android:1.0.0
androidx.compose.material3.adaptive:adaptive:1.0.0
androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.1
androidx.compose.material3:material3-adaptive-navigation-suite:1.3.1
androidx.compose.material3:material3-android:1.3.1
androidx.compose.material3:material3:1.3.1
androidx.compose.material:material-icons-core-android:1.7.6
androidx.compose.material:material-icons-core:1.7.6
androidx.compose.material:material-icons-extended-android:1.7.6
androidx.compose.material:material-icons-extended:1.7.6
androidx.compose.material:material-ripple-android:1.7.6
androidx.compose.material:material-ripple:1.7.6
androidx.compose.runtime:runtime-android:1.8.0-alpha07
androidx.compose.runtime:runtime-saveable-android:1.8.0-alpha07
androidx.compose.runtime:runtime-saveable:1.8.0-alpha07
androidx.compose.runtime:runtime:1.8.0-alpha07
androidx.compose.ui:ui-android:1.8.0-alpha07
androidx.compose.ui:ui-geometry-android:1.8.0-alpha07
androidx.compose.ui:ui-geometry:1.8.0-alpha07
androidx.compose.ui:ui-graphics-android:1.8.0-alpha07
androidx.compose.ui:ui-graphics:1.8.0-alpha07
androidx.compose.ui:ui-text-android:1.8.0-alpha07
androidx.compose.ui:ui-text:1.8.0-alpha07
androidx.compose.ui:ui-tooling-preview-android:1.8.0-alpha07
androidx.compose.ui:ui-tooling-preview:1.8.0-alpha07
androidx.compose.ui:ui-unit-android:1.8.0-alpha07
androidx.compose.ui:ui-unit:1.8.0-alpha07
androidx.compose.ui:ui-util-android:1.8.0-alpha07
androidx.compose.ui:ui-util:1.8.0-alpha07
androidx.compose.ui:ui:1.8.0-alpha07
androidx.compose:compose-bom:2024.12.01
androidx.collection:collection-jvm:1.5.0-beta03
androidx.collection:collection-ktx:1.5.0-beta03
androidx.collection:collection:1.5.0-beta03
androidx.compose.animation:animation-android:1.8.0-beta02
androidx.compose.animation:animation-core-android:1.8.0-beta02
androidx.compose.animation:animation-core:1.8.0-beta02
androidx.compose.animation:animation:1.8.0-beta02
androidx.compose.foundation:foundation-android:1.8.0-beta02
androidx.compose.foundation:foundation-layout-android:1.8.0-beta02
androidx.compose.foundation:foundation-layout:1.8.0-beta02
androidx.compose.foundation:foundation:1.8.0-beta02
androidx.compose.material3.adaptive:adaptive-android:1.1.0-rc01
androidx.compose.material3.adaptive:adaptive:1.1.0-rc01
androidx.compose.material3:material3-adaptive-navigation-suite-android:1.4.0-alpha08
androidx.compose.material3:material3-adaptive-navigation-suite:1.4.0-alpha08
androidx.compose.material3:material3-android:1.4.0-alpha08
androidx.compose.material3:material3:1.4.0-alpha08
androidx.compose.material:material-icons-core-android:1.7.8
androidx.compose.material:material-icons-core:1.7.8
androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.0-beta02
androidx.compose.material:material-ripple:1.8.0-beta02
androidx.compose.runtime:runtime-android:1.8.0-beta02
androidx.compose.runtime:runtime-saveable-android:1.8.0-beta02
androidx.compose.runtime:runtime-saveable:1.8.0-beta02
androidx.compose.runtime:runtime:1.8.0-beta02
androidx.compose.ui:ui-android:1.8.0-beta02
androidx.compose.ui:ui-geometry-android:1.8.0-beta02
androidx.compose.ui:ui-geometry:1.8.0-beta02
androidx.compose.ui:ui-graphics-android:1.8.0-beta02
androidx.compose.ui:ui-graphics:1.8.0-beta02
androidx.compose.ui:ui-text-android:1.8.0-beta02
androidx.compose.ui:ui-text:1.8.0-beta02
androidx.compose.ui:ui-tooling-preview-android:1.8.0-beta02
androidx.compose.ui:ui-tooling-preview:1.8.0-beta02
androidx.compose.ui:ui-unit-android:1.8.0-beta02
androidx.compose.ui:ui-unit:1.8.0-beta02
androidx.compose.ui:ui-util-android:1.8.0-beta02
androidx.compose.ui:ui-util:1.8.0-beta02
androidx.compose.ui:ui:1.8.0-beta02
androidx.compose:compose-bom-alpha:2025.02.00
androidx.concurrent:concurrent-futures:1.1.0
androidx.core:core-ktx:1.13.1
androidx.core:core:1.13.1
@ -60,6 +60,8 @@ androidx.emoji2:emoji2:1.4.0
androidx.exifinterface:exifinterface:1.3.7
androidx.fragment:fragment:1.5.1
androidx.graphics:graphics-path:1.0.1
androidx.graphics:graphics-shapes-android:1.0.1
androidx.graphics:graphics-shapes:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.lifecycle:lifecycle-common-java8:2.8.7
androidx.lifecycle:lifecycle-common-jvm:2.8.7

@ -86,6 +86,7 @@ dependencies {
implementation(projects.sync.work)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material3.adaptive)
implementation(libs.androidx.compose.material3.adaptive.layout)
implementation(libs.androidx.compose.material3.adaptive.navigation)

@ -10,55 +10,55 @@ androidx.arch.core:core-common:2.2.0
androidx.arch.core:core-runtime:2.2.0
androidx.autofill:autofill:1.0.0
androidx.browser:browser:1.8.0
androidx.collection:collection-jvm:1.5.0-beta01
androidx.collection:collection-ktx:1.5.0-beta01
androidx.collection:collection:1.5.0-beta01
androidx.compose.animation:animation-android:1.8.0-alpha07
androidx.compose.animation:animation-core-android:1.8.0-alpha07
androidx.compose.animation:animation-core:1.8.0-alpha07
androidx.compose.animation:animation:1.8.0-alpha07
androidx.compose.foundation:foundation-android:1.8.0-alpha07
androidx.compose.foundation:foundation-layout-android:1.8.0-alpha07
androidx.compose.foundation:foundation-layout:1.8.0-alpha07
androidx.compose.foundation:foundation:1.8.0-alpha07
androidx.compose.material3.adaptive:adaptive-android:1.0.0
androidx.compose.material3.adaptive:adaptive-layout-android:1.0.0
androidx.compose.material3.adaptive:adaptive-layout:1.0.0
androidx.compose.material3.adaptive:adaptive-navigation-android:1.0.0
androidx.compose.material3.adaptive:adaptive-navigation:1.0.0
androidx.compose.material3.adaptive:adaptive:1.0.0
androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.1
androidx.compose.material3:material3-adaptive-navigation-suite:1.3.1
androidx.compose.material3:material3-android:1.3.1
androidx.compose.material3:material3-window-size-class-android:1.3.1
androidx.compose.material3:material3-window-size-class:1.3.1
androidx.compose.material3:material3:1.3.1
androidx.compose.material:material-icons-core-android:1.7.6
androidx.compose.material:material-icons-core:1.7.6
androidx.compose.material:material-icons-extended-android:1.7.6
androidx.compose.material:material-icons-extended:1.7.6
androidx.compose.material:material-ripple-android:1.7.6
androidx.compose.material:material-ripple:1.7.6
androidx.compose.runtime:runtime-android:1.8.0-alpha07
androidx.compose.runtime:runtime-saveable-android:1.8.0-alpha07
androidx.compose.runtime:runtime-saveable:1.8.0-alpha07
androidx.compose.runtime:runtime-tracing:1.8.0-alpha07
androidx.compose.runtime:runtime:1.8.0-alpha07
androidx.compose.ui:ui-android:1.8.0-alpha07
androidx.compose.ui:ui-geometry-android:1.8.0-alpha07
androidx.compose.ui:ui-geometry:1.8.0-alpha07
androidx.compose.ui:ui-graphics-android:1.8.0-alpha07
androidx.compose.ui:ui-graphics:1.8.0-alpha07
androidx.compose.ui:ui-text-android:1.8.0-alpha07
androidx.compose.ui:ui-text:1.8.0-alpha07
androidx.compose.ui:ui-tooling-preview-android:1.8.0-alpha07
androidx.compose.ui:ui-tooling-preview:1.8.0-alpha07
androidx.compose.ui:ui-unit-android:1.8.0-alpha07
androidx.compose.ui:ui-unit:1.8.0-alpha07
androidx.compose.ui:ui-util-android:1.8.0-alpha07
androidx.compose.ui:ui-util:1.8.0-alpha07
androidx.compose.ui:ui:1.8.0-alpha07
androidx.compose:compose-bom:2024.12.01
androidx.collection:collection-jvm:1.5.0-beta03
androidx.collection:collection-ktx:1.5.0-beta03
androidx.collection:collection:1.5.0-beta03
androidx.compose.animation:animation-android:1.8.0-beta02
androidx.compose.animation:animation-core-android:1.8.0-beta02
androidx.compose.animation:animation-core:1.8.0-beta02
androidx.compose.animation:animation:1.8.0-beta02
androidx.compose.foundation:foundation-android:1.8.0-beta02
androidx.compose.foundation:foundation-layout-android:1.8.0-beta02
androidx.compose.foundation:foundation-layout:1.8.0-beta02
androidx.compose.foundation:foundation:1.8.0-beta02
androidx.compose.material3.adaptive:adaptive-android:1.1.0-rc01
androidx.compose.material3.adaptive:adaptive-layout-android:1.1.0-rc01
androidx.compose.material3.adaptive:adaptive-layout:1.1.0-rc01
androidx.compose.material3.adaptive:adaptive-navigation-android:1.1.0-rc01
androidx.compose.material3.adaptive:adaptive-navigation:1.1.0-rc01
androidx.compose.material3.adaptive:adaptive:1.1.0-rc01
androidx.compose.material3:material3-adaptive-navigation-suite-android:1.4.0-alpha08
androidx.compose.material3:material3-adaptive-navigation-suite:1.4.0-alpha08
androidx.compose.material3:material3-android:1.4.0-alpha08
androidx.compose.material3:material3-window-size-class-android:1.4.0-alpha08
androidx.compose.material3:material3-window-size-class:1.4.0-alpha08
androidx.compose.material3:material3:1.4.0-alpha08
androidx.compose.material:material-icons-core-android:1.7.8
androidx.compose.material:material-icons-core:1.7.8
androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.0-beta02
androidx.compose.material:material-ripple:1.8.0-beta02
androidx.compose.runtime:runtime-android:1.8.0-beta02
androidx.compose.runtime:runtime-saveable-android:1.8.0-beta02
androidx.compose.runtime:runtime-saveable:1.8.0-beta02
androidx.compose.runtime:runtime-tracing:1.8.0-beta02
androidx.compose.runtime:runtime:1.8.0-beta02
androidx.compose.ui:ui-android:1.8.0-beta02
androidx.compose.ui:ui-geometry-android:1.8.0-beta02
androidx.compose.ui:ui-geometry:1.8.0-beta02
androidx.compose.ui:ui-graphics-android:1.8.0-beta02
androidx.compose.ui:ui-graphics:1.8.0-beta02
androidx.compose.ui:ui-text-android:1.8.0-beta02
androidx.compose.ui:ui-text:1.8.0-beta02
androidx.compose.ui:ui-tooling-preview-android:1.8.0-beta02
androidx.compose.ui:ui-tooling-preview:1.8.0-beta02
androidx.compose.ui:ui-unit-android:1.8.0-beta02
androidx.compose.ui:ui-unit:1.8.0-beta02
androidx.compose.ui:ui-util-android:1.8.0-beta02
androidx.compose.ui:ui-util:1.8.0-beta02
androidx.compose.ui:ui:1.8.0-beta02
androidx.compose:compose-bom-alpha:2025.02.00
androidx.concurrent:concurrent-futures-ktx:1.1.0
androidx.concurrent:concurrent-futures:1.1.0
androidx.core:core-ktx:1.15.0
@ -84,6 +84,8 @@ androidx.emoji2:emoji2:1.4.0
androidx.exifinterface:exifinterface:1.3.7
androidx.fragment:fragment:1.5.4
androidx.graphics:graphics-path:1.0.1
androidx.graphics:graphics-shapes-android:1.0.1
androidx.graphics:graphics-shapes:1.0.1
androidx.hilt:hilt-common:1.2.0
androidx.hilt:hilt-navigation-compose:1.2.0
androidx.hilt:hilt-navigation:1.2.0

@ -17,47 +17,53 @@
package com.google.samples.apps.nowinandroid.ui.interests2pane
import androidx.activity.compose.BackHandler
import androidx.annotation.Keep
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.VerticalDragHandle
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
import androidx.compose.material3.adaptive.layout.PaneExpansionAnchor
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem
import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective
import androidx.compose.material3.adaptive.layout.defaultDragHandleSemantics
import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState
import androidx.compose.material3.adaptive.navigation.BackNavigationBehavior
import androidx.compose.material3.adaptive.navigation.NavigableListDetailPaneScaffold
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldPredictiveBackHandler
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder
import com.google.samples.apps.nowinandroid.feature.topic.TopicScreen
import com.google.samples.apps.nowinandroid.feature.topic.TopicViewModel
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicRoute
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import java.util.UUID
import kotlin.math.max
@Serializable internal object TopicPlaceholderRoute
// TODO: Remove @Keep when https://issuetracker.google.com/353898971 is fixed
@Keep
@Serializable internal object DetailPaneNavHostRoute
fun NavGraphBuilder.interestsListDetailScreen() {
composable<InterestsRoute> {
InterestsListDetailScreen()
@ -93,69 +99,138 @@ internal fun InterestsListDetailScreen(
},
),
)
BackHandler(listDetailNavigator.canNavigateBack()) {
listDetailNavigator.navigateBack()
val coroutineScope = rememberCoroutineScope()
val paneExpansionState = rememberPaneExpansionState(
anchors = listOf(
PaneExpansionAnchor.Proportion(0f),
PaneExpansionAnchor.Proportion(0.5f),
PaneExpansionAnchor.Proportion(1f),
),
)
ThreePaneScaffoldPredictiveBackHandler(
listDetailNavigator,
BackNavigationBehavior.PopUntilScaffoldValueChange,
)
BackHandler(
paneExpansionState.currentAnchor == PaneExpansionAnchor.Proportion(0f) &&
listDetailNavigator.isListPaneVisible() &&
listDetailNavigator.isDetailPaneVisible(),
) {
coroutineScope.launch {
paneExpansionState.animateTo(PaneExpansionAnchor.Proportion(1f))
}
}
var nestedNavHostStartRoute by remember {
var topicRoute by remember {
val route = selectedTopicId?.let { TopicRoute(id = it) } ?: TopicPlaceholderRoute
mutableStateOf(route)
}
var nestedNavKey by rememberSaveable(
stateSaver = Saver({ it.toString() }, UUID::fromString),
) {
mutableStateOf(UUID.randomUUID())
}
val nestedNavController = key(nestedNavKey) {
rememberNavController()
}
fun onTopicClickShowDetailPane(topicId: String) {
onTopicClick(topicId)
if (listDetailNavigator.isDetailPaneVisible()) {
// If the detail pane was visible, then use the nestedNavController navigate call
// directly
nestedNavController.navigateToTopic(topicId) {
popUpTo<DetailPaneNavHostRoute>()
topicRoute = TopicRoute(id = topicId)
coroutineScope.launch {
listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
}
if (paneExpansionState.currentAnchor == PaneExpansionAnchor.Proportion(1f)) {
coroutineScope.launch {
paneExpansionState.animateTo(PaneExpansionAnchor.Proportion(0f))
}
} else {
// Otherwise, recreate the NavHost entirely, and start at the new destination
nestedNavHostStartRoute = TopicRoute(id = topicId)
nestedNavKey = UUID.randomUUID()
}
listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
}
ListDetailPaneScaffold(
value = listDetailNavigator.scaffoldValue,
directive = listDetailNavigator.scaffoldDirective,
val mutableInteractionSource = remember { MutableInteractionSource() }
val minPaneWidth = 300.dp
NavigableListDetailPaneScaffold(
navigator = listDetailNavigator,
listPane = {
AnimatedPane {
Box(
modifier = Modifier.clipToBounds()
.layout { measurable, constraints ->
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
val placeable = measurable.measure(
constraints.copy(
minWidth = minPaneWidth.roundToPx(),
maxWidth = width,
),
)
layout(constraints.maxWidth, placeable.height) {
placeable.placeRelative(
x = 0,
y = 0,
)
}
},
) {
InterestsRoute(
onTopicClick = ::onTopicClickShowDetailPane,
highlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(),
shouldHighlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(),
)
}
}
},
detailPane = {
AnimatedPane {
key(nestedNavKey) {
NavHost(
navController = nestedNavController,
startDestination = nestedNavHostStartRoute,
route = DetailPaneNavHostRoute::class,
Box(
modifier = Modifier.clipToBounds()
.layout { measurable, constraints ->
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
val placeable = measurable.measure(
constraints.copy(
minWidth = minPaneWidth.roundToPx(),
maxWidth = width,
),
)
layout(constraints.maxWidth, placeable.height) {
placeable.placeRelative(
x = constraints.maxWidth -
max(constraints.maxWidth, placeable.width),
y = 0,
)
}
},
) {
topicScreen(
AnimatedContent(topicRoute) { route ->
when (route) {
is TopicRoute -> {
TopicScreen(
showBackButton = !listDetailNavigator.isListPaneVisible(),
onBackClick = listDetailNavigator::navigateBack,
onBackClick = {
coroutineScope.launch {
listDetailNavigator.navigateBack()
}
},
onTopicClick = ::onTopicClickShowDetailPane,
viewModel = hiltViewModel<TopicViewModel, TopicViewModel.Factory>(
key = route.id,
) { factory ->
factory.create(route.id)
},
)
composable<TopicPlaceholderRoute> {
}
is TopicPlaceholderRoute -> {
TopicDetailPlaceholder()
}
}
}
}
}
},
paneExpansionState = paneExpansionState,
paneExpansionDragHandle = {
VerticalDragHandle(
modifier = Modifier.paneExpansionDraggable(
state = paneExpansionState,
minTouchTargetSize = LocalMinimumInteractiveComponentSize.current,
interactionSource = mutableInteractionSource,
semanticsProperties = paneExpansionState.defaultDragHandleSemantics(),
),
interactionSource = mutableInteractionSource,
)
},
)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 703 B

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

@ -38,7 +38,7 @@ import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
fun InterestsRoute(
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
highlightSelectedTopic: Boolean = false,
shouldHighlightSelectedTopic: Boolean = false,
viewModel: InterestsViewModel = hiltViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
@ -50,7 +50,7 @@ fun InterestsRoute(
viewModel.onTopicClick(it)
onTopicClick(it)
},
highlightSelectedTopic = highlightSelectedTopic,
shouldHighlightSelectedTopic = shouldHighlightSelectedTopic,
modifier = modifier,
)
}
@ -61,7 +61,7 @@ internal fun InterestsScreen(
followTopic: (String, Boolean) -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
highlightSelectedTopic: Boolean = false,
shouldHighlightSelectedTopic: Boolean = false,
) {
Column(
modifier = modifier,
@ -70,7 +70,6 @@ internal fun InterestsScreen(
when (uiState) {
InterestsUiState.Loading ->
NiaLoadingWheel(
modifier = modifier,
contentDesc = stringResource(id = R.string.feature_interests_loading),
)
@ -80,8 +79,7 @@ internal fun InterestsScreen(
onTopicClick = onTopicClick,
onFollowButtonClick = followTopic,
selectedTopicId = uiState.selectedTopicId,
highlightSelectedTopic = highlightSelectedTopic,
modifier = modifier,
shouldHighlightSelectedTopic = shouldHighlightSelectedTopic,
)
is InterestsUiState.Empty -> InterestsEmptyScreen()

@ -49,7 +49,7 @@ fun TopicsTabContent(
modifier: Modifier = Modifier,
withBottomSpacer: Boolean = true,
selectedTopicId: String? = null,
highlightSelectedTopic: Boolean = false,
shouldHighlightSelectedTopic: Boolean = false,
) {
Box(
modifier = modifier
@ -66,7 +66,7 @@ fun TopicsTabContent(
topics.forEach { followableTopic ->
val topicId = followableTopic.topic.id
item(key = topicId) {
val isSelected = highlightSelectedTopic && topicId == selectedTopicId
val isSelected = shouldHighlightSelectedTopic && topicId == selectedTopicId
InterestsItem(
name = followableTopic.topic.name,
following = followableTopic.isFollowed,
@ -75,6 +75,7 @@ fun TopicsTabContent(
onClick = { onTopicClick(topicId) },
onFollowButtonClick = { onFollowButtonClick(topicId, it) },
isSelected = isSelected,
modifier = Modifier.fillMaxWidth(),
)
}
}

@ -16,10 +16,8 @@
package com.google.samples.apps.nowinandroid.feature.topic
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.toRoute
import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
@ -29,7 +27,9 @@ import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.result.Result
import com.google.samples.apps.nowinandroid.core.result.asResult
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicRoute
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@ -38,18 +38,14 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class TopicViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
@HiltViewModel(assistedFactory = TopicViewModel.Factory::class)
class TopicViewModel @AssistedInject constructor(
private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository,
userNewsResourceRepository: UserNewsResourceRepository,
@Assisted val topicId: String,
) : ViewModel() {
val topicId = savedStateHandle.toRoute<TopicRoute>().id
val topicUiState: StateFlow<TopicUiState> = topicUiState(
topicId = topicId,
userDataRepository = userDataRepository,
@ -89,6 +85,13 @@ class TopicViewModel @Inject constructor(
userDataRepository.setNewsResourceViewed(newsResourceId, viewed)
}
}
@AssistedFactory
interface Factory {
fun create(
topicId: String,
): TopicViewModel
}
}
private fun topicUiState(

@ -16,11 +16,14 @@
package com.google.samples.apps.nowinandroid.feature.topic.navigation
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import com.google.samples.apps.nowinandroid.feature.topic.TopicScreen
import com.google.samples.apps.nowinandroid.feature.topic.TopicViewModel
import kotlinx.serialization.Serializable
@Serializable data class TopicRoute(val id: String)
@ -36,11 +39,17 @@ fun NavGraphBuilder.topicScreen(
onBackClick: () -> Unit,
onTopicClick: (String) -> Unit,
) {
composable<TopicRoute> {
composable<TopicRoute> { entry ->
val id = entry.toRoute<TopicRoute>().id
TopicScreen(
showBackButton = showBackButton,
onBackClick = onBackClick,
onTopicClick = onTopicClick,
viewModel = hiltViewModel<TopicViewModel, TopicViewModel.Factory>(
key = id,
) { factory ->
factory.create(id)
},
)
}
}

@ -16,8 +16,6 @@
package com.google.samples.apps.nowinandroid.feature.topic
import androidx.lifecycle.SavedStateHandle
import androidx.navigation.testing.invoke
import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
@ -26,7 +24,6 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepo
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicRoute
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
@ -37,22 +34,13 @@ import kotlinx.datetime.Instant
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import kotlin.test.assertEquals
import kotlin.test.assertIs
/**
* To learn more about how this test handles Flows created with stateIn, see
* https://developer.android.com/kotlin/flow/test#statein
*
* These tests use Robolectric because the subject under test (the ViewModel) uses
* `SavedStateHandle.toRoute` which has a dependency on `android.os.Bundle`.
*
* TODO: Remove Robolectric if/when AndroidX Navigation API is updated to remove Android dependency.
* * See b/340966212.
*/
@RunWith(RobolectricTestRunner::class)
class TopicViewModelTest {
@get:Rule
@ -70,12 +58,10 @@ class TopicViewModelTest {
@Before
fun setup() {
viewModel = TopicViewModel(
savedStateHandle = SavedStateHandle(
route = TopicRoute(id = testInputTopics[0].topic.id),
),
userDataRepository = userDataRepository,
topicsRepository = topicsRepository,
userNewsResourceRepository = userNewsResourceRepository,
topicId = testInputTopics[0].topic.id,
)
}

@ -7,8 +7,9 @@ androidTools = "31.7.3"
androidxActivity = "1.9.3"
androidxAppCompat = "1.7.0"
androidxBrowser = "1.8.0"
androidxComposeBom = "2024.12.01"
androidxComposeBom = "2025.02.00"
androidxComposeFoundation = "1.8.0-alpha07"
androidxComposeMaterial3Adaptive = "1.1.0-rc01"
androidxComposeRuntimeTracing = "1.7.6"
androidxCore = "1.15.0"
androidxCoreSplashscreen = "1.0.1"
@ -69,15 +70,15 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" }
androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" }
androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "androidxBrowser" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom-alpha", version.ref = "androidxComposeBom" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "androidxComposeFoundation" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }
androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-compose-material3-navigationSuite = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite" }
androidx-compose-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive" }
androidx-compose-material3-adaptive-layout = { group = "androidx.compose.material3.adaptive", name = "adaptive-layout" }
androidx-compose-material3-adaptive-navigation = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation" }
androidx-compose-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive", version.ref = "androidxComposeMaterial3Adaptive" }
androidx-compose-material3-adaptive-layout = { group = "androidx.compose.material3.adaptive", name = "adaptive-layout", version.ref = "androidxComposeMaterial3Adaptive" }
androidx-compose-material3-adaptive-navigation = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation", version.ref = "androidxComposeMaterial3Adaptive" }
androidx-compose-material3-windowSizeClass = { group = "androidx.compose.material3", name = "material3-window-size-class" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" }

Loading…
Cancel
Save