diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d9b3c2616..32d12673f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -23,8 +23,6 @@ updates: - "org.jetbrains.kotlin:kotlin-stdlib" - "org.jetbrains.kotlin:kotlin-gradle-plugin" - "org.jetbrains.compose.compiler" - - "com.google.dagger.hilt.android" - - "com.google.devtools.ksp" # Request that all AGP dependent upgrades are grouped together agp: patterns: diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index fab2e6241..88244527e 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -15,12 +15,13 @@ kotlin = "2.3.10" # https://github.com/jeremymailen/kotlinter-gradle/blob/master/README.md#compatibility kotlinter = "5.0.1" -activity = "1.12.2" +activity = "1.13.0" +adaptive-navigation3 = "1.3.0-alpha09" appcompat = "1.7.1" coil = "2.7.0" -composeBom = "2025.12.01" +composeBom = "2026.03.00" ktlint-compose = "0.5.6" -coreKtx = "1.17.0" +coreKtx = "1.18.0" detekt = "1.23.8" dokka = "2.1.0" inject = "1" @@ -55,6 +56,7 @@ androidx-compose-material3-windowSize = { group = "androidx.compose.material3", androidx-compose-uiTest = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-lint-gradle = { module = "androidx.lint:lint-gradle", version.ref = "lint-gradle" } +androidx-nav-adaptive = { module = "androidx.compose.material3.adaptive:adaptive-navigation3", version.ref = "adaptive-navigation3" } androidx-nav-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav" } androidx-nav-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav" } androidx-ui = { group = "androidx.compose.ui", name = "ui" } diff --git a/android/sample/build.gradle.kts b/android/sample/build.gradle.kts index 2292436e8..f51cfe3e8 100644 --- a/android/sample/build.gradle.kts +++ b/android/sample/build.gradle.kts @@ -53,4 +53,5 @@ dependencies { implementation(libs.androidx.nav.runtime) implementation(libs.androidx.nav.ui) + implementation(libs.androidx.nav.adaptive) } \ No newline at end of file diff --git a/android/sample/detekt-baseline.xml b/android/sample/detekt-baseline.xml index 7246ba85f..7264c96c6 100644 --- a/android/sample/detekt-baseline.xml +++ b/android/sample/detekt-baseline.xml @@ -3,7 +3,7 @@ CognitiveComplexMethod:ChipsPreview.kt$@OptIn(ExperimentalLayoutApi::class) @Composable internal fun ChipsPreview(onBackPress: () -> Unit, modifier: Modifier = Modifier) - LongMethod:MainActivity.kt$MainActivity$override fun onCreate(savedInstanceState: Bundle?) + LongMethod:MainActivity.kt$MainActivity$@OptIn(ExperimentalMaterial3AdaptiveApi::class) override fun onCreate(savedInstanceState: Bundle?) UnusedPrivateMember:ButtonPreview.kt$@PreviewLightDark @Composable private fun Preview() UnusedPrivateMember:ChipsPreview.kt$@PreviewLightDark @Composable private fun Preview() UnusedPrivateMember:PromoStickerPreview.kt$@PreviewLightDark @Composable private fun Preview() diff --git a/android/sample/src/main/kotlin/com/gu/source/Home.kt b/android/sample/src/main/kotlin/com/gu/source/Home.kt index dba8919ae..08cdc096e 100644 --- a/android/sample/src/main/kotlin/com/gu/source/Home.kt +++ b/android/sample/src/main/kotlin/com/gu/source/Home.kt @@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -32,7 +34,7 @@ internal fun Home(modifier: Modifier = Modifier, navigate: (Destination) -> Unit topBar = { SampleTopAppBar { Text( - text = "Welcome to Source.", + text = "Source", style = Source.Typography.HeadlineMedium20, color = AppColour( light = Source.Palette.Brand400, @@ -49,7 +51,8 @@ internal fun Home(modifier: Modifier = Modifier, navigate: (Destination) -> Unit Column( modifier = Modifier .fillMaxSize() - .padding(paddingValues + PaddingValues(16.dp)), + .padding(paddingValues + PaddingValues(16.dp)) + .verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(8.dp), ) { Text( diff --git a/android/sample/src/main/kotlin/com/gu/source/MainActivity.kt b/android/sample/src/main/kotlin/com/gu/source/MainActivity.kt index b30196aac..14b8a32e4 100644 --- a/android/sample/src/main/kotlin/com/gu/source/MainActivity.kt +++ b/android/sample/src/main/kotlin/com/gu/source/MainActivity.kt @@ -2,13 +2,16 @@ package com.gu.source import android.os.Bundle import androidx.activity.ComponentActivity +import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.animation.core.spring -import androidx.compose.animation.fadeIn -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally -import androidx.compose.animation.togetherWith +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo +import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective +import androidx.compose.material3.adaptive.navigation3.ListDetailSceneStrategy +import androidx.compose.material3.adaptive.navigation3.rememberListDetailSceneStrategy +import androidx.compose.runtime.remember +import androidx.compose.ui.unit.dp import androidx.navigation3.runtime.entryProvider import androidx.navigation3.ui.NavDisplay import com.gu.source.daynight.AppColourMode @@ -25,6 +28,7 @@ import com.gu.source.previews.RatingPreview import com.gu.source.previews.TextButtonPreview internal class MainActivity : ComponentActivity() { + @OptIn(ExperimentalMaterial3AdaptiveApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -32,62 +36,94 @@ internal class MainActivity : ComponentActivity() { setContent { val navigator = rememberNavigator(Destination.Home) + val windowInfo = currentWindowAdaptiveInfo() + val isHomeOnTop = navigator.backstack.lastOrNull() is Destination.Home + + val directive = remember(windowInfo, isHomeOnTop) { + val isCompact = !windowInfo.windowSizeClass + .isWidthAtLeastBreakpoint(widthDpBreakpoint = 600) + calculatePaneScaffoldDirective(windowInfo) + .copy( + horizontalPartitionSpacerSize = 0.dp, + maxHorizontalPartitions = if (isCompact || isHomeOnTop) 1 else 2, + ) + } + AppColourMode { modifier -> NavDisplay( backStack = navigator.backstack, onBack = { navigator.popBackStack() }, modifier = modifier, + sceneStrategy = rememberListDetailSceneStrategy( + shouldHandleSinglePaneLayout = true, + directive = directive, + ), entryProvider = entryProvider { - entry(Destination.Home) { - Home { navigator.navigate(it) } + entry( + metadata = ListDetailSceneStrategy.listPane(), + ) { + Home { + if (!isHomeOnTop) { + navigator.popBackStack() + } + navigator.navigate(it) + } } - entry(Destination.PalettePreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } PalettePreview(navigator::popBackStack) } - entry(Destination.PagerProgressBarPreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } ImagePagerWithProgressIndicator(navigator::popBackStack) } - entry(Destination.ButtonsPreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } ButtonPreview(navigator::popBackStack) } - entry(Destination.TextButtonPreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } TextButtonPreview(navigator::popBackStack) } - entry(Destination.IconsPreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } IconsPreview(navigator::popBackStack) } - entry(Destination.AlertBannerPreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } AlertBannerPreview(navigator::popBackStack) } - entry(Destination.ChipsPreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } ChipsPreview(navigator::popBackStack) } - entry(Destination.BadgesPreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } PromoStickerPreview(navigator::popBackStack) } - entry(Destination.StarRatingsPreview) { + entry( + metadata = ListDetailSceneStrategy.detailPane(), + ) { + BackHandler { navigator.popBackStack() } RatingPreview(navigator::popBackStack) } }, - transitionSpec = { - // Slide in from right when navigating forward - slideInHorizontally(initialOffsetX = { it }) togetherWith - slideOutHorizontally(targetOffsetX = { -it }) - }, - popTransitionSpec = { - // Slide in from left when navigating back - fadeIn( - spring(dampingRatio = 1.0f, stiffness = 1600.0f), - ) togetherWith - slideOutHorizontally(targetOffsetX = { it }) - }, - predictivePopTransitionSpec = { - // Slide in from left when navigating back - fadeIn( - spring(dampingRatio = 1.0f, stiffness = 1600.0f), - ) togetherWith - slideOutHorizontally(targetOffsetX = { it }) - }, ) } } diff --git a/android/sample/src/main/kotlin/com/gu/source/previews/PromoStickerPreview.kt b/android/sample/src/main/kotlin/com/gu/source/previews/PromoStickerPreview.kt index 23411ffa0..c78111d98 100644 --- a/android/sample/src/main/kotlin/com/gu/source/previews/PromoStickerPreview.kt +++ b/android/sample/src/main/kotlin/com/gu/source/previews/PromoStickerPreview.kt @@ -21,7 +21,7 @@ import com.gu.source.foundation.typography.TextSansBold17 internal fun PromoStickerPreview(onBackPress: () -> Unit, modifier: Modifier = Modifier) { PreviewScaffold("Promo sticker", onBackPress, modifier) { LazyVerticalGrid( - columns = GridCells.Fixed(getGridCount()), + columns = GridCells.Fixed(2), modifier = it, verticalArrangement = Arrangement.Center, ) {