@@ -34,7 +34,10 @@ import androidx.compose.material3.Text
3434import androidx.compose.runtime.Composable
3535import androidx.compose.runtime.DisposableEffect
3636import androidx.compose.runtime.LaunchedEffect
37+ import androidx.compose.runtime.getValue
38+ import androidx.compose.runtime.mutableStateOf
3739import androidx.compose.runtime.remember
40+ import androidx.compose.runtime.setValue
3841import androidx.compose.ui.Alignment
3942import androidx.compose.ui.Modifier
4043import androidx.compose.ui.graphics.Color
@@ -89,11 +92,19 @@ private fun CameraPreview(
8992 val lifecycleOwner = LocalLifecycleOwner .current
9093 val permission = android.Manifest .permission.CAMERA
9194
92- // UI에서 항상 권한 최신 상태 확인
93- val isGranted = ContextCompat .checkSelfPermission(context, permission) == PackageManager .PERMISSION_GRANTED
94- val launcher = rememberLauncherForActivityResult(
95+ /* *
96+ * Camera Permission Request
97+ */
98+ var isGranted by remember {
99+ mutableStateOf(
100+ ContextCompat .checkSelfPermission(context, permission) == PackageManager .PERMISSION_GRANTED ,
101+ )
102+ }
103+ val permissionLauncher = rememberLauncherForActivityResult(
95104 contract = ActivityResultContracts .RequestPermission (),
96105 ) { granted ->
106+ isGranted = granted
107+
97108 if (! granted) {
98109 state.eventSink(OcrUiEvent .OnShowPermissionDialog )
99110 }
@@ -102,45 +113,20 @@ private fun CameraPreview(
102113 contract = ActivityResultContracts .StartActivityForResult (),
103114 ) { _ -> }
104115
105- val cameraController = remember { LifecycleCameraController (context) }
106- val imageAnalyzer = remember {
107- ImageAnalysis .Analyzer { imageProxy ->
108- state.eventSink(OcrUiEvent .OnFrameReceived (imageProxy))
109- }
110- }
111-
112- val systemUiController = rememberSystemUiController()
113-
114- DisposableEffect (systemUiController) {
115- systemUiController.setSystemBarsColor(
116- color = Color .Transparent ,
117- darkIcons = false ,
118- isNavigationBarContrastEnforced = false ,
119- )
120-
121- onDispose {
122- systemUiController.setSystemBarsColor(
123- color = Color .Transparent ,
124- darkIcons = true ,
125- isNavigationBarContrastEnforced = false ,
126- )
127- }
128- }
129-
130116 // 최초 진입 시 권한 요청
131117 LaunchedEffect (Unit ) {
132118 if (! isGranted) {
133119 state.eventSink(OcrUiEvent .OnHidePermissionDialog )
134- launcher .launch(permission)
120+ permissionLauncher .launch(permission)
135121 }
136122 }
137123
138- // 앱이 포그라운드로 북귀할 때 OS 권한 체크
124+ // 앱이 포그라운드로 북귀할 때 OS 권한 동기화
139125 DisposableEffect (Unit ) {
140126 val observer = LifecycleEventObserver { _, event ->
141127 if (event == Lifecycle .Event .ON_RESUME ) {
142- val currentGrant = ContextCompat .checkSelfPermission(context, permission) == PackageManager .PERMISSION_GRANTED
143- if (currentGrant ) {
128+ isGranted = ContextCompat .checkSelfPermission(context, permission) == PackageManager .PERMISSION_GRANTED
129+ if (isGranted ) {
144130 state.eventSink(OcrUiEvent .OnHidePermissionDialog )
145131 } else {
146132 state.eventSink(OcrUiEvent .OnShowPermissionDialog )
@@ -151,18 +137,52 @@ private fun CameraPreview(
151137 onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
152138 }
153139
154- DisposableEffect (lifecycleOwner, cameraController) {
155- cameraController.bindToLifecycle(lifecycleOwner)
156- cameraController.setImageAnalysisAnalyzer(
157- ContextCompat .getMainExecutor(context),
158- imageAnalyzer,
159- )
140+ /* *
141+ * Camera Controller & ImageAnalyzer
142+ */
143+ val cameraController = remember { LifecycleCameraController (context) }
144+ val imageAnalyzer = remember {
145+ ImageAnalysis .Analyzer { imageProxy ->
146+ state.eventSink(OcrUiEvent .OnFrameReceived (imageProxy))
147+ }
148+ }
149+
150+ DisposableEffect (isGranted, lifecycleOwner, cameraController) {
151+ if (isGranted) {
152+ cameraController.bindToLifecycle(lifecycleOwner)
153+ cameraController.setImageAnalysisAnalyzer(
154+ ContextCompat .getMainExecutor(context),
155+ imageAnalyzer,
156+ )
157+ }
158+
160159 onDispose {
161160 cameraController.unbind()
162161 cameraController.clearImageAnalysisAnalyzer()
163162 }
164163 }
165164
165+ /* *
166+ * SystemStatusBar Color
167+ */
168+ val systemUiController = rememberSystemUiController()
169+
170+ DisposableEffect (systemUiController) {
171+ systemUiController.setSystemBarsColor(
172+ color = Color .Transparent ,
173+ darkIcons = false ,
174+ isNavigationBarContrastEnforced = false ,
175+ )
176+
177+ onDispose {
178+ systemUiController.setSystemBarsColor(
179+ color = Color .Transparent ,
180+ darkIcons = true ,
181+ isNavigationBarContrastEnforced = false ,
182+ )
183+ }
184+ }
185+
166186 ReedScaffold (
167187 modifier = modifier.fillMaxSize(),
168188 containerColor = Neutral950 ,
0 commit comments