Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt +12 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.graphics.drawable.Animatable import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import android.os.Build import android.os.Handler import android.os.Looper import android.view.View Loading Loading @@ -117,13 +118,17 @@ class DrawablePainter( return true } override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean = drawable.setLayoutDirection( override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean { if (Build.VERSION.SDK_INT >= 23) { return drawable.setLayoutDirection( when (layoutDirection) { LayoutDirection.Ltr -> View.LAYOUT_DIRECTION_LTR LayoutDirection.Rtl -> View.LAYOUT_DIRECTION_RTL } ) } return false } override val intrinsicSize: Size get() = drawableIntrinsicSize Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Pager.kt +14 −25 Original line number Diff line number Diff line Loading @@ -19,8 +19,6 @@ package com.android.settingslib.spa.framework.compose import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow Loading @@ -36,7 +34,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp Loading Loading @@ -123,7 +120,7 @@ fun VerticalPager( contentPadding: PaddingValues = PaddingValues(0.dp), horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, key: ((page: Int) -> Any)? = null, content: @Composable() (PagerScope.(page: Int) -> Unit), content: @Composable PagerScope.(page: Int) -> Unit, ) { Pager( count = count, Loading Loading @@ -175,24 +172,8 @@ internal fun Pager( .collect { state.updateCurrentPageBasedOnLazyListState() } } val density = LocalDensity.current val layoutDirection = LocalLayoutDirection.current LaunchedEffect(density, contentPadding, isVertical, layoutDirection, reverseLayout, state) { with(density) { // this should be exposed on LazyListLayoutInfo instead. b/200920410 state.afterContentPadding = if (isVertical) { if (!reverseLayout) { contentPadding.calculateBottomPadding() } else { contentPadding.calculateTopPadding() } } else { if (!reverseLayout) { contentPadding.calculateEndPadding(layoutDirection) } else { contentPadding.calculateStartPadding(layoutDirection) } }.roundToPx() } LaunchedEffect(density, state, itemSpacing) { with(density) { state.itemSpacing = itemSpacing.roundToPx() } } val pagerScope = remember(state) { PagerScopeImpl(state) } Loading @@ -203,6 +184,7 @@ internal fun Pager( ConsumeFlingNestedScrollConnection( consumeHorizontal = !isVertical, consumeVertical = isVertical, pagerState = state, ) } Loading Loading @@ -268,6 +250,7 @@ internal fun Pager( private class ConsumeFlingNestedScrollConnection( private val consumeHorizontal: Boolean, private val consumeVertical: Boolean, private val pagerState: PagerState, ) : NestedScrollConnection { override fun onPostScroll( consumed: Offset, Loading @@ -281,9 +264,15 @@ private class ConsumeFlingNestedScrollConnection( } override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { // We can consume all post fling velocity on the main-axis // so that it doesn't propagate up to the Pager return available.consume(consumeHorizontal, consumeVertical) return if (pagerState.currentPageOffset != 0f) { // The Pager is already scrolling. This means that a nested scroll child was // scrolled to end, and the Pager can use this fling Velocity.Zero } else { // A nested scroll child is still scrolling. We can consume all post fling // velocity on the main-axis so that it doesn't propagate up to the Pager available.consume(consumeHorizontal, consumeVertical) } } } Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PagerState.kt +12 −14 Original line number Diff line number Diff line Loading @@ -85,12 +85,14 @@ class PagerState( return layoutInfo.visibleItemsInfo.maxByOrNull { val start = maxOf(it.offset, 0) val end = minOf( it.offset + it.size, layoutInfo.viewportEndOffset - afterContentPadding) it.offset + it.size, layoutInfo.viewportEndOffset - layoutInfo.afterContentPadding ) end - start } } internal var afterContentPadding = 0 internal var itemSpacing by mutableStateOf(0) private val currentPageLayoutInfo: LazyListItemInfo? get() = lazyListState.layoutInfo.visibleItemsInfo.lastOrNull { Loading Loading @@ -135,9 +137,7 @@ class PagerState( */ val currentPageOffset: Float by derivedStateOf { currentPageLayoutInfo?.let { // We coerce since itemSpacing can make the offset > 1f. // We don't want to count spacing in the offset so cap it to 1f (-it.offset / it.size.toFloat()).coerceIn(-1f, 1f) (-it.offset / (it.size + itemSpacing).toFloat()).coerceIn(-0.5f, 0.5f) } ?: 0f } Loading Loading @@ -187,28 +187,26 @@ class PagerState( // offset from the size lazyListState.animateScrollToItem( index = page, scrollOffset = (target.size * pageOffset).roundToInt() scrollOffset = ((target.size + itemSpacing) * pageOffset).roundToInt() ) } else if (layoutInfo.visibleItemsInfo.isNotEmpty()) { // If we don't, we use the current page size as a guide val currentSize = layoutInfo.visibleItemsInfo.first().size val currentSize = layoutInfo.visibleItemsInfo.first().size + itemSpacing lazyListState.animateScrollToItem( index = page, scrollOffset = (currentSize * pageOffset).roundToInt() ) // The target should be visible now target = lazyListState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == page } target = layoutInfo.visibleItemsInfo.firstOrNull { it.index == page } if (target != null && target.size != currentSize) { if (target != null && target.size + itemSpacing != currentSize) { // If the size we used for calculating the offset differs from the actual // target page size, we need to scroll again. This doesn't look great, // but there's not much else we can do. lazyListState.animateScrollToItem( index = page, scrollOffset = (target.size * pageOffset).roundToInt() scrollOffset = ((target.size + itemSpacing) * pageOffset).roundToInt() ) } } Loading Loading @@ -248,7 +246,7 @@ class PagerState( if (pageOffset.absoluteValue > 0.0001f) { currentPageLayoutInfo?.let { scroll { scrollBy(it.size * pageOffset) scrollBy((it.size + itemSpacing) * pageOffset) } } } Loading Loading @@ -295,7 +293,7 @@ class PagerState( } private fun requireCurrentPageOffset(value: Float, name: String) { require(value in -1f..1f) { "$name must be >= 0 and <= 1" } require(value in -1f..1f) { "$name must be >= -1 and <= 1" } } companion object { Loading packages/SettingsLib/Spa/tests/build.gradle +7 −1 Original line number Diff line number Diff line Loading @@ -76,7 +76,13 @@ task coverageReport(type: JacocoReport, dependsOn: "connectedDebugAndroidTest") sourceDirectories.from = files("../spa/src") classDirectories.from = fileTree( dir: "../spa/build/tmp/kotlin-classes/debug", excludes: ["com/android/settingslib/spa/debug/**"], excludes: [ "com/android/settingslib/spa/debug/**", // Excludes files forked from Accompanist. "com/android/settingslib/spa/framework/compose/DrawablePainter*", "com/android/settingslib/spa/framework/compose/Pager*", ], ) executionData.from = fileTree(dir: "$buildDir/outputs/code_coverage/debugAndroidTest/connected") } Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt +12 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.graphics.drawable.Animatable import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import android.os.Build import android.os.Handler import android.os.Looper import android.view.View Loading Loading @@ -117,13 +118,17 @@ class DrawablePainter( return true } override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean = drawable.setLayoutDirection( override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean { if (Build.VERSION.SDK_INT >= 23) { return drawable.setLayoutDirection( when (layoutDirection) { LayoutDirection.Ltr -> View.LAYOUT_DIRECTION_LTR LayoutDirection.Rtl -> View.LAYOUT_DIRECTION_RTL } ) } return false } override val intrinsicSize: Size get() = drawableIntrinsicSize Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Pager.kt +14 −25 Original line number Diff line number Diff line Loading @@ -19,8 +19,6 @@ package com.android.settingslib.spa.framework.compose import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow Loading @@ -36,7 +34,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp Loading Loading @@ -123,7 +120,7 @@ fun VerticalPager( contentPadding: PaddingValues = PaddingValues(0.dp), horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, key: ((page: Int) -> Any)? = null, content: @Composable() (PagerScope.(page: Int) -> Unit), content: @Composable PagerScope.(page: Int) -> Unit, ) { Pager( count = count, Loading Loading @@ -175,24 +172,8 @@ internal fun Pager( .collect { state.updateCurrentPageBasedOnLazyListState() } } val density = LocalDensity.current val layoutDirection = LocalLayoutDirection.current LaunchedEffect(density, contentPadding, isVertical, layoutDirection, reverseLayout, state) { with(density) { // this should be exposed on LazyListLayoutInfo instead. b/200920410 state.afterContentPadding = if (isVertical) { if (!reverseLayout) { contentPadding.calculateBottomPadding() } else { contentPadding.calculateTopPadding() } } else { if (!reverseLayout) { contentPadding.calculateEndPadding(layoutDirection) } else { contentPadding.calculateStartPadding(layoutDirection) } }.roundToPx() } LaunchedEffect(density, state, itemSpacing) { with(density) { state.itemSpacing = itemSpacing.roundToPx() } } val pagerScope = remember(state) { PagerScopeImpl(state) } Loading @@ -203,6 +184,7 @@ internal fun Pager( ConsumeFlingNestedScrollConnection( consumeHorizontal = !isVertical, consumeVertical = isVertical, pagerState = state, ) } Loading Loading @@ -268,6 +250,7 @@ internal fun Pager( private class ConsumeFlingNestedScrollConnection( private val consumeHorizontal: Boolean, private val consumeVertical: Boolean, private val pagerState: PagerState, ) : NestedScrollConnection { override fun onPostScroll( consumed: Offset, Loading @@ -281,9 +264,15 @@ private class ConsumeFlingNestedScrollConnection( } override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { // We can consume all post fling velocity on the main-axis // so that it doesn't propagate up to the Pager return available.consume(consumeHorizontal, consumeVertical) return if (pagerState.currentPageOffset != 0f) { // The Pager is already scrolling. This means that a nested scroll child was // scrolled to end, and the Pager can use this fling Velocity.Zero } else { // A nested scroll child is still scrolling. We can consume all post fling // velocity on the main-axis so that it doesn't propagate up to the Pager available.consume(consumeHorizontal, consumeVertical) } } } Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PagerState.kt +12 −14 Original line number Diff line number Diff line Loading @@ -85,12 +85,14 @@ class PagerState( return layoutInfo.visibleItemsInfo.maxByOrNull { val start = maxOf(it.offset, 0) val end = minOf( it.offset + it.size, layoutInfo.viewportEndOffset - afterContentPadding) it.offset + it.size, layoutInfo.viewportEndOffset - layoutInfo.afterContentPadding ) end - start } } internal var afterContentPadding = 0 internal var itemSpacing by mutableStateOf(0) private val currentPageLayoutInfo: LazyListItemInfo? get() = lazyListState.layoutInfo.visibleItemsInfo.lastOrNull { Loading Loading @@ -135,9 +137,7 @@ class PagerState( */ val currentPageOffset: Float by derivedStateOf { currentPageLayoutInfo?.let { // We coerce since itemSpacing can make the offset > 1f. // We don't want to count spacing in the offset so cap it to 1f (-it.offset / it.size.toFloat()).coerceIn(-1f, 1f) (-it.offset / (it.size + itemSpacing).toFloat()).coerceIn(-0.5f, 0.5f) } ?: 0f } Loading Loading @@ -187,28 +187,26 @@ class PagerState( // offset from the size lazyListState.animateScrollToItem( index = page, scrollOffset = (target.size * pageOffset).roundToInt() scrollOffset = ((target.size + itemSpacing) * pageOffset).roundToInt() ) } else if (layoutInfo.visibleItemsInfo.isNotEmpty()) { // If we don't, we use the current page size as a guide val currentSize = layoutInfo.visibleItemsInfo.first().size val currentSize = layoutInfo.visibleItemsInfo.first().size + itemSpacing lazyListState.animateScrollToItem( index = page, scrollOffset = (currentSize * pageOffset).roundToInt() ) // The target should be visible now target = lazyListState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == page } target = layoutInfo.visibleItemsInfo.firstOrNull { it.index == page } if (target != null && target.size != currentSize) { if (target != null && target.size + itemSpacing != currentSize) { // If the size we used for calculating the offset differs from the actual // target page size, we need to scroll again. This doesn't look great, // but there's not much else we can do. lazyListState.animateScrollToItem( index = page, scrollOffset = (target.size * pageOffset).roundToInt() scrollOffset = ((target.size + itemSpacing) * pageOffset).roundToInt() ) } } Loading Loading @@ -248,7 +246,7 @@ class PagerState( if (pageOffset.absoluteValue > 0.0001f) { currentPageLayoutInfo?.let { scroll { scrollBy(it.size * pageOffset) scrollBy((it.size + itemSpacing) * pageOffset) } } } Loading Loading @@ -295,7 +293,7 @@ class PagerState( } private fun requireCurrentPageOffset(value: Float, name: String) { require(value in -1f..1f) { "$name must be >= 0 and <= 1" } require(value in -1f..1f) { "$name must be >= -1 and <= 1" } } companion object { Loading
packages/SettingsLib/Spa/tests/build.gradle +7 −1 Original line number Diff line number Diff line Loading @@ -76,7 +76,13 @@ task coverageReport(type: JacocoReport, dependsOn: "connectedDebugAndroidTest") sourceDirectories.from = files("../spa/src") classDirectories.from = fileTree( dir: "../spa/build/tmp/kotlin-classes/debug", excludes: ["com/android/settingslib/spa/debug/**"], excludes: [ "com/android/settingslib/spa/debug/**", // Excludes files forked from Accompanist. "com/android/settingslib/spa/framework/compose/DrawablePainter*", "com/android/settingslib/spa/framework/compose/Pager*", ], ) executionData.from = fileTree(dir: "$buildDir/outputs/code_coverage/debugAndroidTest/connected") }