From 5684370e2598d103a271c27dcd936eb915df79d5 Mon Sep 17 00:00:00 2001 From: seoyoon Date: Sat, 9 Aug 2025 17:54:39 +0900 Subject: [PATCH] =?UTF-8?q?[BOOK-245]=20fix:=20=EC=8B=9C=EC=8A=A4=ED=85=9C?= =?UTF-8?q?=20=EA=B6=8C=ED=95=9C=20=ED=8C=9D=EC=97=85=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=97=88=EC=9A=A9=20=EC=8B=9C=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20?= =?UTF-8?q?=ED=94=84=EB=A6=AC=EB=B7=B0=20=EB=B3=B4=EC=9D=B4=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booket/feature/record/ocr/OcrUi.kt | 96 +++++++++++-------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt index fa2362ba..2f24a26e 100644 --- a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt +++ b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/OcrUi.kt @@ -34,7 +34,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -89,11 +92,19 @@ private fun CameraPreview( val lifecycleOwner = LocalLifecycleOwner.current val permission = android.Manifest.permission.CAMERA - // UI에서 항상 권한 최신 상태 확인 - val isGranted = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED - val launcher = rememberLauncherForActivityResult( + /** + * Camera Permission Request + */ + var isGranted by remember { + mutableStateOf( + ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED, + ) + } + val permissionLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestPermission(), ) { granted -> + isGranted = granted + if (!granted) { state.eventSink(OcrUiEvent.OnShowPermissionDialog) } @@ -102,45 +113,20 @@ private fun CameraPreview( contract = ActivityResultContracts.StartActivityForResult(), ) { _ -> } - val cameraController = remember { LifecycleCameraController(context) } - val imageAnalyzer = remember { - ImageAnalysis.Analyzer { imageProxy -> - state.eventSink(OcrUiEvent.OnFrameReceived(imageProxy)) - } - } - - val systemUiController = rememberSystemUiController() - - DisposableEffect(systemUiController) { - systemUiController.setSystemBarsColor( - color = Color.Transparent, - darkIcons = false, - isNavigationBarContrastEnforced = false, - ) - - onDispose { - systemUiController.setSystemBarsColor( - color = Color.Transparent, - darkIcons = true, - isNavigationBarContrastEnforced = false, - ) - } - } - // 최초 진입 시 권한 요청 LaunchedEffect(Unit) { if (!isGranted) { state.eventSink(OcrUiEvent.OnHidePermissionDialog) - launcher.launch(permission) + permissionLauncher.launch(permission) } } - // 앱이 포그라운드로 북귀할 때 OS 권한 체크 + // 앱이 포그라운드로 북귀할 때 OS 권한 동기화 DisposableEffect(Unit) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { - val currentGrant = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED - if (currentGrant) { + isGranted = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED + if (isGranted) { state.eventSink(OcrUiEvent.OnHidePermissionDialog) } else { state.eventSink(OcrUiEvent.OnShowPermissionDialog) @@ -151,18 +137,52 @@ private fun CameraPreview( onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } - DisposableEffect(lifecycleOwner, cameraController) { - cameraController.bindToLifecycle(lifecycleOwner) - cameraController.setImageAnalysisAnalyzer( - ContextCompat.getMainExecutor(context), - imageAnalyzer, - ) + /** + * Camera Controller & ImageAnalyzer + */ + val cameraController = remember { LifecycleCameraController(context) } + val imageAnalyzer = remember { + ImageAnalysis.Analyzer { imageProxy -> + state.eventSink(OcrUiEvent.OnFrameReceived(imageProxy)) + } + } + + DisposableEffect(isGranted, lifecycleOwner, cameraController) { + if (isGranted) { + cameraController.bindToLifecycle(lifecycleOwner) + cameraController.setImageAnalysisAnalyzer( + ContextCompat.getMainExecutor(context), + imageAnalyzer, + ) + } + onDispose { cameraController.unbind() cameraController.clearImageAnalysisAnalyzer() } } + /** + * SystemStatusBar Color + */ + val systemUiController = rememberSystemUiController() + + DisposableEffect(systemUiController) { + systemUiController.setSystemBarsColor( + color = Color.Transparent, + darkIcons = false, + isNavigationBarContrastEnforced = false, + ) + + onDispose { + systemUiController.setSystemBarsColor( + color = Color.Transparent, + darkIcons = true, + isNavigationBarContrastEnforced = false, + ) + } + } + ReedScaffold( modifier = modifier.fillMaxSize(), containerColor = Neutral950,