diff --git a/app/build.gradle b/app/build.gradle index e568b689cb..55dd9bee17 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -382,8 +382,6 @@ dependencies { debugImplementation "androidx.compose.ui:ui-test-manifest:$composeVersion" implementation "com.google.accompanist:accompanist-themeadapter-appcompat:0.33.1-alpha" - implementation "com.google.accompanist:accompanist-pager:0.33.1-alpha" - implementation "com.google.accompanist:accompanist-pager-indicators:0.33.1-alpha" implementation "com.google.accompanist:accompanist-permissions:0.33.1-alpha" implementation "com.google.accompanist:accompanist-drawablepainter:0.33.1-alpha" diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt index 0b7b6d6b4c..76c3ba83f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt @@ -2,26 +2,44 @@ package org.thoughtcrime.securesms.ui import androidx.annotation.DrawableRes import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.pager.PagerState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Card +import androidx.compose.material.ContentAlpha import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.LocalContentColor import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import network.loki.messenger.R +import kotlin.math.absoluteValue +import kotlin.math.sign @OptIn(ExperimentalFoundationApi::class) @Composable @@ -34,7 +52,7 @@ fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) { .padding(8.dp) ) { Box(modifier = Modifier.padding(8.dp)) { - com.google.accompanist.pager.HorizontalPagerIndicator( + ClickableHorizontalPagerIndicator( pagerState = pagerState, pageCount = pagerState.pageCount, activeColor = Color.White, @@ -43,6 +61,123 @@ fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) { } } +internal interface PagerStateBridge { + val currentPage: Int + val currentPageOffset: Float +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun ClickableHorizontalPagerIndicator( + pagerState: PagerState, + pageCount: Int, + modifier: Modifier = Modifier, + pageIndexMapping: (Int) -> Int = { it }, + activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), + inactiveColor: Color = activeColor.copy(ContentAlpha.disabled), + indicatorWidth: Dp = 8.dp, + indicatorHeight: Dp = indicatorWidth, + spacing: Dp = indicatorWidth, + indicatorShape: Shape = CircleShape, +) { + val scope = rememberCoroutineScope() + + val stateBridge = remember(pagerState) { + object : PagerStateBridge { + override val currentPage: Int + get() = pagerState.currentPage + override val currentPageOffset: Float + get() = pagerState.currentPageOffsetFraction + } + } + + HorizontalPagerIndicator( + pagerState = stateBridge, + pageCount = pageCount, + modifier = modifier, + pageIndexMapping = pageIndexMapping, + activeColor = activeColor, + inactiveColor = inactiveColor, + indicatorHeight = indicatorHeight, + indicatorWidth = indicatorWidth, + spacing = spacing, + indicatorShape = indicatorShape, + ) { + scope.launch { + pagerState.animateScrollToPage(it) + } + } +} + +@Composable +private fun HorizontalPagerIndicator( + pagerState: PagerStateBridge, + pageCount: Int, + modifier: Modifier = Modifier, + pageIndexMapping: (Int) -> Int = { it }, + activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), + inactiveColor: Color = activeColor.copy(ContentAlpha.disabled), + indicatorWidth: Dp = 8.dp, + indicatorHeight: Dp = indicatorWidth, + spacing: Dp = indicatorWidth, + indicatorShape: Shape = CircleShape, + onIndicatorClick: (Int) -> Unit, +) { + + val indicatorWidthPx = LocalDensity.current.run { indicatorWidth.roundToPx() } + val spacingPx = LocalDensity.current.run { spacing.roundToPx() } + + Box( + modifier = modifier, + contentAlignment = Alignment.CenterStart + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(spacing), + verticalAlignment = Alignment.CenterVertically, + ) { + + repeat(pageCount) { + Box( + modifier = Modifier + .size(width = indicatorWidth, height = indicatorHeight) + .clip(indicatorShape) + .background(color = inactiveColor) + .clickable { onIndicatorClick(it) } //modified here + ) + } + } + + Box( + Modifier + .offset { + val position = pageIndexMapping(pagerState.currentPage) + val offset = pagerState.currentPageOffset + val next = pageIndexMapping(pagerState.currentPage + offset.sign.toInt()) + val scrollPosition = ((next - position) * offset.absoluteValue + position) + .coerceIn( + 0f, + (pageCount - 1) + .coerceAtLeast(0) + .toFloat() + ) + + IntOffset( + x = ((spacingPx + indicatorWidthPx) * scrollPosition).toInt(), + y = 0 + ) + } + .size(width = indicatorWidth, height = indicatorHeight) + .then( + if (pageCount > 0) Modifier.background( + color = activeColor, + shape = indicatorShape, + ) + else Modifier + ) + ) + } +} + @OptIn(ExperimentalFoundationApi::class) @Composable fun RowScope.CarouselPrevButton(pagerState: PagerState) {