diff --git a/core/common/src/main/kotlin/com/ninecraft/booket/core/common/extensions/Modifier.kt b/core/common/src/main/kotlin/com/ninecraft/booket/core/common/extensions/Modifier.kt index 019befb7..4c5695cb 100644 --- a/core/common/src/main/kotlin/com/ninecraft/booket/core/common/extensions/Modifier.kt +++ b/core/common/src/main/kotlin/com/ninecraft/booket/core/common/extensions/Modifier.kt @@ -1,6 +1,8 @@ package com.ninecraft.booket.core.common.extensions import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.material3.ripple import androidx.compose.runtime.remember @@ -9,6 +11,8 @@ import androidx.compose.ui.composed import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.layer.GraphicsLayer import androidx.compose.ui.graphics.layer.drawLayer +import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.debugInspectorInfo import androidx.compose.ui.semantics.Role import com.ninecraft.booket.core.common.utils.MultipleEventsCutter @@ -54,3 +58,25 @@ fun Modifier.captureToGraphicsLayer(graphicsLayer: GraphicsLayer) = graphicsLayer.record { this@drawWithContent.drawContent() } drawLayer(graphicsLayer) } + +/** + * 부모 영역에서 동시 터치(두 손가락 이상)를 차단하는 Modifier + */ +fun Modifier.preventMultiTouch() = pointerInput(Unit) { + // awaitEachGesture: 한 번의 제스쳐 세션을 추상화 + awaitEachGesture { + val first = awaitFirstDown(requireUnconsumed = false) + + while (true) { + // 이벤트 전파 초기 단계(PointerEventPass.Initial)에서 하위 컴포저블로 이벤트가 내려가기 전에 가로채 소비한다 + val event = awaitPointerEvent(pass = PointerEventPass.Initial) + event.changes.forEach { change -> + if (change.id != first.id && change.pressed) { + change.consume() + } + } + // 첫 포인터가 pressed 상태일 동안만 유지한다 (up이거나 cancel되면 pressed=false로 루프 종료) + if (event.changes.none { it.id == first.id && it.pressed }) break + } + } +} diff --git a/core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/ReedScaffold.kt b/core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/ReedScaffold.kt index be8a59c2..92270ebb 100644 --- a/core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/ReedScaffold.kt +++ b/core/ui/src/main/kotlin/com/ninecraft/booket/core/ui/ReedScaffold.kt @@ -7,6 +7,7 @@ import androidx.compose.material3.ScaffoldDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import com.ninecraft.booket.core.common.extensions.preventMultiTouch import com.ninecraft.booket.core.designsystem.theme.White import tech.thdev.compose.extensions.keyboard.state.foundation.keyboardHide @@ -22,7 +23,9 @@ fun ReedScaffold( content: @Composable (PaddingValues) -> Unit, ) { Scaffold( - modifier = modifier.keyboardHide(), + modifier = modifier + .keyboardHide() + .preventMultiTouch(), topBar = topBar, bottomBar = bottomBar, snackbarHost = snackbarHost, diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt index 1525a760..4d00094f 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt @@ -38,6 +38,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @@ -120,6 +121,8 @@ class BookDetailPresenter @AssistedInject constructor( uiState = UiState.Success } + } catch (ce: CancellationException) { + throw ce } catch (e: Throwable) { uiState = UiState.Error(e) diff --git a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt index 4f676ad6..3ae920d7 100644 --- a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt +++ b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUi.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.ninecraft.booket.core.common.extensions.preventMultiTouch import com.ninecraft.booket.core.designsystem.DevicePreview import com.ninecraft.booket.core.designsystem.RecordStep import com.ninecraft.booket.core.designsystem.component.RecordProgressBar @@ -46,7 +47,9 @@ internal fun RecordRegisterUi( } Scaffold( - modifier = modifier.fillMaxSize(), + modifier = modifier + .fillMaxSize() + .preventMultiTouch(), containerColor = White, contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.ime), ) { innerPadding -> diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 12530d9b..484f649a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -79,8 +79,8 @@ firebase-crashlytics = "3.0.4" minSdk = "28" targetSdk = "35" compileSdk = "35" -versionName = "1.1.0" -versionCode = "4" +versionName = "1.1.1" +versionCode = "5" packageName = "com.ninecraft.booket" [libraries]