From 4594d18accee1c6d9be9bade871ff2e519c51297 Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Fri, 27 Sep 2024 11:19:05 -0700 Subject: [PATCH] Update to material3-adaptive 1.1 Change-Id: I4b3b68face17149ab87135df28baa41cd5bacb0f --- app/build.gradle.kts | 1 + .../InterestsListDetailScreen.kt | 122 +++++++++++++++--- .../apps/nowinandroid/KotlinAndroid.kt | 2 +- .../nowinandroid/core/ui/NewsResourceCard.kt | 27 ++-- .../feature/interests/InterestsScreen.kt | 2 - .../feature/interests/TabContent.kt | 1 + gradle/libs.versions.toml | 11 +- 7 files changed, 123 insertions(+), 43 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5434df8c2..de8e346a7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -89,6 +89,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) diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt index 669c6300a..93ed303b8 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt @@ -18,6 +18,10 @@ package com.google.samples.apps.nowinandroid.ui.interests2pane import androidx.activity.compose.BackHandler import androidx.annotation.Keep +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 @@ -25,8 +29,10 @@ 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.rememberPaneExpansionState import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator import androidx.compose.runtime.Composable @@ -34,9 +40,14 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.rememberSaveable 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 @@ -49,8 +60,10 @@ import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder 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 @@ -93,8 +106,20 @@ internal fun InterestsListDetailScreen( }, ), ) + val coroutineScope = rememberCoroutineScope() + + val paneExpansionState = rememberPaneExpansionState( + anchors = listOf( + PaneExpansionAnchor.Proportion(0f), + PaneExpansionAnchor.Proportion(0.5f), + PaneExpansionAnchor.Proportion(1f), + ), + ) + BackHandler(listDetailNavigator.canNavigateBack()) { - listDetailNavigator.navigateBack() + coroutineScope.launch { + listDetailNavigator.navigateBack() + } } var nestedNavHostStartRoute by remember { @@ -123,40 +148,99 @@ internal fun InterestsListDetailScreen( nestedNavHostStartRoute = TopicRoute(id = topicId) nestedNavKey = UUID.randomUUID() } - listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail) + coroutineScope.launch { + listDetailNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail) + } } + val mutableInteractionSource = remember { MutableInteractionSource() } + val minPaneWidth = 300.dp + ListDetailPaneScaffold( value = listDetailNavigator.scaffoldValue, directive = listDetailNavigator.scaffoldDirective, listPane = { AnimatedPane { - InterestsRoute( - onTopicClick = ::onTopicClickShowDetailPane, - highlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(), - ) + 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(), + ) + } } }, detailPane = { AnimatedPane { - key(nestedNavKey) { - NavHost( - navController = nestedNavController, - startDestination = nestedNavHostStartRoute, - route = DetailPaneNavHostRoute::class, - ) { - topicScreen( - showBackButton = !listDetailNavigator.isListPaneVisible(), - onBackClick = listDetailNavigator::navigateBack, - onTopicClick = ::onTopicClickShowDetailPane, - ) - composable { - TopicDetailPlaceholder() + 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, + ) + } + }, + ) { + key(nestedNavKey) { + NavHost( + navController = nestedNavController, + startDestination = nestedNavHostStartRoute, + route = DetailPaneNavHostRoute::class, + ) { + topicScreen( + showBackButton = !listDetailNavigator.isListPaneVisible(), + onBackClick = { + coroutineScope.launch { + listDetailNavigator.navigateBack() + } + }, + onTopicClick = ::onTopicClickShowDetailPane, + ) + composable { + TopicDetailPlaceholder() + } } } } } }, + paneExpansionState = paneExpansionState, + paneExpansionDragHandle = { + VerticalDragHandle( + modifier = Modifier.paneExpansionDraggable( + state = paneExpansionState, + minTouchTargetSize = LocalMinimumInteractiveComponentSize.current, + interactionSource = mutableInteractionSource, + ), + interactionSource = mutableInteractionSource, + ) + }, ) } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt index 57f026029..c37d91c56 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt @@ -36,7 +36,7 @@ internal fun Project.configureKotlinAndroid( commonExtension: CommonExtension<*, *, *, *, *, *>, ) { commonExtension.apply { - compileSdk = 34 + compileSdk = 35 defaultConfig { minSdk = 21 diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt index 2395eb156..eb4865be1 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt @@ -24,7 +24,6 @@ import androidx.compose.foundation.Canvas import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.draganddrop.dragAndDropSource -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -139,21 +138,17 @@ fun NewsResourceCardExpanded( userNewsResource.title, modifier = Modifier .fillMaxWidth((.8f)) - .dragAndDropSource { - detectTapGestures( - onLongPress = { - startTransfer( - DragAndDropTransferData( - ClipData.newPlainText( - sharingLabel, - sharingContent, - ), - flags = dragAndDropFlags, - ), - ) - }, - ) - }, + .dragAndDropSource( + transferData = { + DragAndDropTransferData( + ClipData.newPlainText( + sharingLabel, + sharingContent, + ), + flags = dragAndDropFlags, + ) + }, + ), ) Spacer(modifier = Modifier.weight(1f)) BookmarkButton(isBookmarked, onToggleBookmark) diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt index 468550878..75283942d 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt @@ -70,7 +70,6 @@ internal fun InterestsScreen( when (uiState) { InterestsUiState.Loading -> NiaLoadingWheel( - modifier = modifier, contentDesc = stringResource(id = R.string.feature_interests_loading), ) @@ -81,7 +80,6 @@ internal fun InterestsScreen( onFollowButtonClick = followTopic, selectedTopicId = uiState.selectedTopicId, highlightSelectedTopic = highlightSelectedTopic, - modifier = modifier, ) is InterestsUiState.Empty -> InterestsEmptyScreen() diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt index 83058c12e..715d61578 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt @@ -75,6 +75,7 @@ fun TopicsTabContent( onClick = { onTopicClick(topicId) }, onFollowButtonClick = { onFollowButtonClick(topicId, it) }, isSelected = isSelected, + modifier = Modifier.fillMaxWidth(), ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4e996177a..f0e2a2b9b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,8 @@ androidTools = "31.7.3" androidxActivity = "1.9.3" androidxAppCompat = "1.7.0" androidxBrowser = "1.8.0" -androidxComposeBom = "2024.11.00" +androidxComposeBom = "2024.12.01" +androidxComposeMaterial3Adaptive = "1.1.0-alpha08" androidxComposeRuntimeTracing = "1.7.5" androidxCore = "1.13.1" androidxCoreSplashscreen = "1.0.1" @@ -67,15 +68,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" } 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" }