mirror of
https://github.com/bbara04/Purefin.git
synced 2026-04-01 01:30:08 +02:00
fix: resolve gesture conflicts by unifying drag handlers
Replaced competing pointerInput modifiers with a single unified gesture handler that determines drag direction early. This eliminates conflicts between horizontal seeking and vertical brightness/volume gestures, making swipe interactions more reliable and predictable.
This commit is contained in:
@@ -5,10 +5,10 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
internal object HorizontalSeekGestureHelper {
|
internal object HorizontalSeekGestureHelper {
|
||||||
val START_THRESHOLD = 24.dp
|
val START_THRESHOLD = 12.dp
|
||||||
private const val COEFFICIENT = 1.3f
|
private const val COEFFICIENT = 3.1f
|
||||||
const val EXPONENT = 1.8f
|
const val EXPONENT = 1.7f
|
||||||
private const val MAX_DELTA_MS = 300_000L
|
private const val MAX_DELTA_MS = 12_000_000L
|
||||||
|
|
||||||
fun deltaMs(rawDelta: Float): Long {
|
fun deltaMs(rawDelta: Float): Long {
|
||||||
val magnitude = abs(rawDelta)
|
val magnitude = abs(rawDelta)
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
package hu.bbara.purefin.player.ui.components
|
package hu.bbara.purefin.player.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||||
import androidx.compose.foundation.gestures.detectDragGestures
|
|
||||||
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
|
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
|
import androidx.compose.foundation.gestures.drag
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.input.pointer.PointerInputChange
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.input.pointer.positionChange
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import hu.bbara.purefin.player.helper.HorizontalSeekGestureHelper
|
import hu.bbara.purefin.player.helper.HorizontalSeekGestureHelper
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerGesturesLayer(
|
fun PlayerGesturesLayer(
|
||||||
@@ -31,85 +32,96 @@ fun PlayerGesturesLayer(
|
|||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val horizontalThresholdPx = with(density) { HorizontalSeekGestureHelper.START_THRESHOLD.toPx() }
|
val horizontalThresholdPx = with(density) { HorizontalSeekGestureHelper.START_THRESHOLD.toPx() }
|
||||||
|
val directionThresholdPx = with(density) { 20.dp.toPx() } // Threshold to determine drag direction
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth(0.90f)
|
.fillMaxWidth(0.90f)
|
||||||
.fillMaxHeight(0.70f)
|
.fillMaxHeight(0.70f)
|
||||||
// .background(Color(2f, 2f, 2f, 0.3f))
|
|
||||||
.pointerInput(Unit) {
|
.pointerInput(Unit) {
|
||||||
detectTapGestures(
|
detectTapGestures(
|
||||||
onTap = { onTap() },
|
onTap = { onTap() },
|
||||||
onDoubleTap = { offset ->
|
onDoubleTap = { offset ->
|
||||||
// TODO extract it into an enum
|
|
||||||
val screenWidth = size.width
|
val screenWidth = size.width
|
||||||
val oneThird = screenWidth / 3
|
val oneThird = screenWidth / 3
|
||||||
val secondThird = oneThird * 2
|
val secondThird = oneThird * 2
|
||||||
if (offset.x < oneThird) {
|
when {
|
||||||
onDoubleTapLeft()
|
offset.x < oneThird -> onDoubleTapLeft()
|
||||||
} else if (offset.x >= oneThird && offset.x <= secondThird) {
|
offset.x <= secondThird -> onDoubleTapCenter()
|
||||||
onDoubleTapCenter()
|
else -> onDoubleTapRight()
|
||||||
} else {
|
|
||||||
onDoubleTapRight()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.pointerInput(Unit) {
|
.pointerInput(Unit) {
|
||||||
detectDragGestures { change, dragAmount ->
|
awaitEachGesture {
|
||||||
val horizontalThreshold = size.width / 2
|
val down = awaitFirstDown(requireUnconsumed = false)
|
||||||
if (change.position.x < horizontalThreshold) {
|
val startX = down.position.x
|
||||||
onVerticalDragLeft(dragAmount.y)
|
|
||||||
} else {
|
var accumulatedDrag = Offset.Zero
|
||||||
onVerticalDragRight(dragAmount.y)
|
var dragDirection: DragDirection? = null
|
||||||
|
var accumulatedHorizontalDrag = 0f
|
||||||
|
var isHorizontalDragActive = false
|
||||||
|
var lastPreviewDelta: Long? = null
|
||||||
|
|
||||||
|
val dragResult = drag(down.id) { change ->
|
||||||
|
val delta = change.positionChange()
|
||||||
|
accumulatedDrag += delta
|
||||||
|
|
||||||
|
// Determine direction if not yet determined
|
||||||
|
if (dragDirection == null && (abs(accumulatedDrag.x) > directionThresholdPx || abs(accumulatedDrag.y) > directionThresholdPx)) {
|
||||||
|
dragDirection = if (abs(accumulatedDrag.x) > abs(accumulatedDrag.y)) {
|
||||||
|
DragDirection.HORIZONTAL
|
||||||
|
} else {
|
||||||
|
DragDirection.VERTICAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle based on determined direction
|
||||||
|
when (dragDirection) {
|
||||||
|
DragDirection.HORIZONTAL -> {
|
||||||
|
accumulatedHorizontalDrag += delta.x
|
||||||
|
if (!isHorizontalDragActive && abs(accumulatedHorizontalDrag) >= horizontalThresholdPx) {
|
||||||
|
isHorizontalDragActive = true
|
||||||
|
}
|
||||||
|
if (isHorizontalDragActive) {
|
||||||
|
change.consume()
|
||||||
|
val deltaMs = HorizontalSeekGestureHelper.deltaMs(accumulatedHorizontalDrag)
|
||||||
|
if (deltaMs != 0L && deltaMs != lastPreviewDelta) {
|
||||||
|
lastPreviewDelta = deltaMs
|
||||||
|
onHorizontalDragPreview(deltaMs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DragDirection.VERTICAL -> {
|
||||||
|
val isLeftSide = startX < size.width / 2
|
||||||
|
if (isLeftSide) {
|
||||||
|
onVerticalDragLeft(delta.y)
|
||||||
|
} else {
|
||||||
|
onVerticalDragRight(delta.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null -> {
|
||||||
|
// Direction not determined yet, keep accumulating
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle drag end
|
||||||
|
if (dragDirection == DragDirection.HORIZONTAL && isHorizontalDragActive) {
|
||||||
|
val deltaMs = HorizontalSeekGestureHelper.deltaMs(accumulatedHorizontalDrag)
|
||||||
|
if (deltaMs != 0L) {
|
||||||
|
onHorizontalDrag(deltaMs)
|
||||||
|
onHorizontalDragPreview(deltaMs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onHorizontalDragPreview(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pointerInput(Unit) {
|
|
||||||
var accumulatedHorizontalDrag = 0f
|
|
||||||
var isHorizontalDragActive = false
|
|
||||||
var lastPreviewDelta: Long? = null
|
|
||||||
detectHorizontalDragGestures(
|
|
||||||
onDragStart = {
|
|
||||||
accumulatedHorizontalDrag = 0f
|
|
||||||
isHorizontalDragActive = false
|
|
||||||
lastPreviewDelta = null
|
|
||||||
onHorizontalDragPreview(null)
|
|
||||||
},
|
|
||||||
onHorizontalDrag = { change, dragAmount ->
|
|
||||||
accumulatedHorizontalDrag += dragAmount
|
|
||||||
if (!isHorizontalDragActive && kotlin.math.abs(accumulatedHorizontalDrag) >= horizontalThresholdPx) {
|
|
||||||
isHorizontalDragActive = true
|
|
||||||
}
|
|
||||||
if (isHorizontalDragActive) {
|
|
||||||
change.consume()
|
|
||||||
val deltaMs = HorizontalSeekGestureHelper.deltaMs(accumulatedHorizontalDrag)
|
|
||||||
if (deltaMs != 0L && deltaMs != lastPreviewDelta) {
|
|
||||||
lastPreviewDelta = deltaMs
|
|
||||||
onHorizontalDragPreview(deltaMs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDragEnd = {
|
|
||||||
if (isHorizontalDragActive) {
|
|
||||||
val deltaMs = HorizontalSeekGestureHelper.deltaMs(accumulatedHorizontalDrag)
|
|
||||||
if (deltaMs != 0L) {
|
|
||||||
onHorizontalDrag(deltaMs)
|
|
||||||
onHorizontalDragPreview(deltaMs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
accumulatedHorizontalDrag = 0f
|
|
||||||
isHorizontalDragActive = false
|
|
||||||
lastPreviewDelta = null
|
|
||||||
onHorizontalDragPreview(null)
|
|
||||||
},
|
|
||||||
onDragCancel = {
|
|
||||||
accumulatedHorizontalDrag = 0f
|
|
||||||
isHorizontalDragActive = false
|
|
||||||
lastPreviewDelta = null
|
|
||||||
onHorizontalDragPreview(null)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum class DragDirection {
|
||||||
|
HORIZONTAL,
|
||||||
|
VERTICAL
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user