Skip to content

Commit

Permalink
chore: merge branch 'hotfix/1.8.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
ghasemdev committed Sep 22, 2023
2 parents eb7c43b + 4c75189 commit 2119dab
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ import com.parsuomash.affogato.pdfviewer.internal.loadPdf
import com.parsuomash.affogato.pdfviewer.state.HorizontalPdfReaderState
import com.parsuomash.affogato.pdfviewer.zoomable.Zoomable
import com.parsuomash.affogato.pdfviewer.zoomable.ZoomableDefaults
import com.parsuomash.affogato.pdfviewer.zoomable.ZoomableState
import com.parsuomash.affogato.pdfviewer.zoomable.rememberZoomableState

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HorizontalPDFReader(
state: HorizontalPdfReaderState,
zoomableState: ZoomableState = rememberZoomableState(
minScale = ZoomableDefaults.DefaultScale
),
modifier: Modifier
) {
BoxWithConstraints(
Expand All @@ -49,43 +53,40 @@ fun HorizontalPDFReader(
}

state.pdfRender?.let { pdf ->
val zoomableState = rememberZoomableState(
minScale = ZoomableDefaults.DefaultScale
)

Zoomable(
state = zoomableState,
enabled = state.isZoomEnable
) {
HorizontalPager(
modifier = Modifier.fillMaxSize(),
state = state.pagerState,
userScrollEnabled = state.scale == 1f
) { page ->
val pageContent = pdf.pageLists[page].stateFlow.collectAsState().value
DisposableEffect(key1 = Unit) {
pdf.pageLists[page].load()
onDispose {
pdf.pageLists[page].recycle()
}
}
when (pageContent) {
is PageContentInt.PageContent -> {
PdfImage(
bitmap = { pageContent.bitmap.asImageBitmap() },
contentDescription = pageContent.contentDescription
)
zoomableState = zoomableState,
enabled = state.isZoomEnable,
content = {
HorizontalPager(
modifier = Modifier.fillMaxSize(),
state = state.pagerState,
userScrollEnabled = state.scale == 1f
) { page ->
val pageContent = pdf.pageLists[page].stateFlow.collectAsState().value
DisposableEffect(key1 = Unit) {
pdf.pageLists[page].load()
onDispose {
pdf.pageLists[page].recycle()
}
}
when (pageContent) {
is PageContentInt.PageContent -> {
PdfImage(
bitmap = { pageContent.bitmap.asImageBitmap() },
contentDescription = pageContent.contentDescription
)
}

is PageContentInt.BlankPage -> {
BlackPage(
width = pageContent.width,
height = pageContent.height
)
is PageContentInt.BlankPage -> {
BlackPage(
width = pageContent.width,
height = pageContent.height
)
}
}
}
}
}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ import com.parsuomash.affogato.pdfviewer.internal.loadPdf
import com.parsuomash.affogato.pdfviewer.state.VerticalPdfReaderState
import com.parsuomash.affogato.pdfviewer.zoomable.Zoomable
import com.parsuomash.affogato.pdfviewer.zoomable.ZoomableDefaults
import com.parsuomash.affogato.pdfviewer.zoomable.ZoomableState
import com.parsuomash.affogato.pdfviewer.zoomable.rememberZoomableState

@Composable
fun VerticalPDFReader(
state: VerticalPdfReaderState,
zoomableState: ZoomableState = rememberZoomableState(
minScale = ZoomableDefaults.DefaultScale
),
modifier: Modifier
) {
BoxWithConstraints(
Expand All @@ -48,12 +52,8 @@ fun VerticalPDFReader(
}

state.pdfRender?.let { pdf ->
val zoomableState = rememberZoomableState(
minScale = ZoomableDefaults.DefaultScale
)

Zoomable(
state = zoomableState,
zoomableState = zoomableState,
enabled = state.isZoomEnable
) {
LazyColumn(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.parsuomash.affogato.pdfviewer.zoomable

import androidx.compose.foundation.gestures.animateZoomBy
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.awaitTouchSlopOrCancellation
Expand All @@ -11,10 +10,7 @@ import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
Expand All @@ -35,47 +31,28 @@ import kotlinx.coroutines.launch
* A zoomable layout that supports zooming in and out, dragging, double tap and dismiss gesture.
*
* @param modifier The modifier to apply to this layout.
* @param state The state object to be used to control or observe the state.
* @param zoomableState The state object to be used to control or observe the state.
* @param enabled Controls the enabled state. When false, all gestures will be ignored.
* @param dismissGestureEnabled Whether to enable dismiss gesture detection.
* @param onDismiss Will be called when dismiss gesture is detected. Should return a boolean
* indicating whether the dismiss request is handled.
* @param content a block, which describes the content.
*/
@Composable
internal fun Zoomable(
modifier: Modifier = Modifier,
state: ZoomableState = rememberZoomableState(),
zoomableState: ZoomableState = rememberZoomableState(),
enabled: Boolean = true,
dismissGestureEnabled: Boolean = false,
onDismiss: () -> Boolean = { false },
content: @Composable () -> Unit
) {
val dismissGestureEnabledState = rememberUpdatedState(dismissGestureEnabled)
val scope = rememberCoroutineScope()
val coroutineScope = rememberCoroutineScope()
val transformableState = rememberTransformableState { zoomChange, panChange, _ ->
if (state.dismissDragAbsoluteOffsetY == 0f) {
scope.launch {
state.onZoomChange(zoomChange)
state.onDrag(panChange)
}
}
}
LaunchedEffect(transformableState.isTransformInProgress) {
if (!transformableState.isTransformInProgress && state.scale < ZoomableDefaults.DefaultScale) {
scope.launch {
val zoomFactor = ZoomableDefaults.DefaultScale / state.scale
transformableState.animateZoomBy(zoomFactor)
}
coroutineScope.launch {
zoomableState.onDrag(panChange)
zoomableState.onZoomChange(zoomChange)
}
}
val gesturesModifier = if (!enabled) Modifier else Modifier
.pointerInput(state) {
detectTapAndDragGestures(
state = state,
dismissGestureEnabled = dismissGestureEnabledState,
onDismiss = onDismiss
)
.pointerInput(zoomableState) {
detectTapAndDragGestures(zoomableState)
}
.transformable(state = transformableState)

Expand All @@ -87,20 +64,19 @@ internal fun Zoomable(
val height = constraints.maxHeight
val placeable = measurable.measure(
Constraints(
maxWidth = (width * state.scale).roundToInt(),
maxHeight = (height * state.scale).roundToInt()
maxWidth = (width * zoomableState.scale).roundToInt(),
maxHeight = (height * zoomableState.scale).roundToInt()
)
)
state.size = IntSize(width, height)
state.childSize = Size(
placeable.width / state.scale,
placeable.height / state.scale
zoomableState.size = IntSize(width, height)
zoomableState.childSize = Size(
placeable.width / zoomableState.scale,
placeable.height / zoomableState.scale
)
layout(width, height) {
placeable.placeWithLayer(
state.translationX.roundToInt() - state.boundOffset.x,
state.translationY.roundToInt() - state.boundOffset.y
+ state.dismissDragOffsetY.roundToInt()
zoomableState.translationX.roundToInt() - zoomableState.boundOffset.x,
zoomableState.translationY.roundToInt() - zoomableState.boundOffset.y
)
}
}
Expand All @@ -110,9 +86,7 @@ internal fun Zoomable(
}

internal suspend fun PointerInputScope.detectTapAndDragGestures(
state: ZoomableState,
dismissGestureEnabled: State<Boolean>,
onDismiss: () -> Boolean
state: ZoomableState
) = coroutineScope {
launch {
detectTapGestures(
Expand All @@ -136,7 +110,6 @@ internal suspend fun PointerInputScope.detectTapAndDragGestures(
launch {
detectDragGestures(
state = state,
dismissGestureEnabled = dismissGestureEnabled,
startDragImmediately = { state.isDragInProgress },
onDragStart = {
state.onDragStart()
Expand All @@ -148,27 +121,17 @@ internal suspend fun PointerInputScope.detectTapAndDragGestures(
state.onDrag(dragAmount)
state.addPosition(change.uptimeMillis, change.position)
}
} else {
state.onDismissDrag(dragAmount.y)
}
},
onDragCancel = {
if (state.isZooming) {
state.resetTracking()
} else {
launch {
state.onDismissDragEnd()
}
}
},
onDragEnd = {
launch {
if (state.isZooming) {
state.onDragEnd()
} else {
if (!(state.shouldDismiss && onDismiss())) {
state.onDismissDragEnd()
}
}
}
}
Expand All @@ -178,7 +141,6 @@ internal suspend fun PointerInputScope.detectTapAndDragGestures(

private suspend fun PointerInputScope.detectDragGestures(
state: ZoomableState,
dismissGestureEnabled: State<Boolean>,
startDragImmediately: () -> Boolean,
onDragStart: (PointerInputChange) -> Unit = {},
onDragEnd: () -> Unit = {},
Expand All @@ -188,7 +150,7 @@ private suspend fun PointerInputScope.detectDragGestures(
awaitEachGesture {
// We have to always call this, or we'll get a crash if we do nothing.
val down = awaitFirstDown(requireUnconsumed = false)
if (state.isZooming || dismissGestureEnabled.value) {
if (state.isZooming) {
var overSlop = Offset.Zero
val drag = if (state.isZooming) {
if (startDragImmediately()) down else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.roundToInt
import kotlin.math.sin
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
Expand All @@ -42,7 +39,7 @@ import kotlinx.coroutines.launch
* @param initialTranslationY The initial value for [ZoomableState.translationY].
*/
@Composable
internal fun rememberZoomableState(
fun rememberZoomableState(
@FloatRange(from = 0.0) minScale: Float = ZoomableDefaults.MinScale,
@FloatRange(from = 0.0) maxScale: Float = ZoomableDefaults.MaxScale,
@FloatRange(from = 0.0) doubleTapScale: Float = ZoomableDefaults.DoubleTapScale,
Expand All @@ -68,7 +65,7 @@ internal fun rememberZoomableState(
* @see rememberZoomableState
*/
@Stable
internal class ZoomableState(
class ZoomableState(
@FloatRange(from = 0.0) initialScale: Float = ZoomableDefaults.MinScale,
@FloatRange(from = 0.0) initialTranslationX: Float = 0f,
@FloatRange(from = 0.0) initialTranslationY: Float = 0f
Expand Down Expand Up @@ -112,21 +109,6 @@ internal class ZoomableState(
internal var boundOffset by mutableStateOf(IntOffset.Zero)
private set

internal var dismissDragAbsoluteOffsetY by mutableFloatStateOf(0f)
private set

internal val dismissDragOffsetY: Float
get() {
val maxOffset = childSize.height
return if (maxOffset == 0f) 0f else {
val progress = (dismissDragAbsoluteOffsetY / maxOffset).coerceIn(-1f, 1f)
maxOffset / DismissDragResistanceFactor * sin(progress * PI.toFloat() / 2)
}
}

internal val shouldDismiss: Boolean
get() = abs(dismissDragAbsoluteOffsetY) > size.height * DismissDragThreshold

internal var size = IntSize.Zero
set(value) {
if (field != value) {
Expand Down Expand Up @@ -289,19 +271,6 @@ internal class ZoomableState(
velocityTracker.resetTracking()
}

internal fun onDismissDrag(dragAmountY: Float) {
dismissDragAbsoluteOffsetY += dragAmountY
}

internal suspend fun onDismissDragEnd() {
animate(
initialValue = dismissDragAbsoluteOffsetY,
targetValue = 0f
) { value, _ ->
dismissDragAbsoluteOffsetY = value
}
}

override fun toString(): String =
"ZoomableState(translateX=%.1f,translateY=%.1f,scale=%.2f)".format(
translationX, translationY, scale
Expand Down Expand Up @@ -342,15 +311,15 @@ internal object ZoomableDefaults {
/**
* The default value for [ZoomableState.minScale].
*/
const val MinScale = 1 / 4f
const val MinScale = 1 / 5f

/**
* The default value for [ZoomableState.maxScale].
*/
const val MaxScale = 4f
const val MaxScale = 5f

/**
* The default value for [ZoomableState.doubleTapScale].
*/
const val DoubleTapScale = 2f
const val DoubleTapScale = 2.5f
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
accompanist = "0.32.0"
affogato = "1.8.2"
affogato = "1.8.3"
agp = "8.1.1"
case-format = "0.2.0"
compose = "1.5.1"
Expand Down
1 change: 1 addition & 0 deletions sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ android {
getByName("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
signingConfig = signingConfigs.getByName("debug")
}
}
compileOptions {
Expand Down

0 comments on commit 2119dab

Please sign in to comment.