diff --git a/feature/record/build.gradle.kts b/feature/record/build.gradle.kts index b3056c2c..71788033 100644 --- a/feature/record/build.gradle.kts +++ b/feature/record/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { libs.androidx.activity.compose, libs.androidx.camera.camera2, libs.androidx.camera.lifecycle, - libs.androidx.camera.view, + libs.androidx.camera.compose, libs.compose.keyboard.state, libs.logger, diff --git a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/content/OcrCameraContent.kt b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/content/OcrCameraContent.kt index e1daf915..b17b6c25 100644 --- a/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/content/OcrCameraContent.kt +++ b/feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/content/OcrCameraContent.kt @@ -4,15 +4,17 @@ import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.provider.Settings -import android.view.ViewGroup -import android.widget.LinearLayout import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts +import androidx.camera.compose.CameraXViewfinder +import androidx.camera.core.CameraSelector import androidx.camera.core.ImageCapture import androidx.camera.core.ImageCaptureException -import androidx.camera.view.LifecycleCameraController -import androidx.camera.view.PreviewView +import androidx.camera.core.Preview +import androidx.camera.core.SurfaceRequest +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.lifecycle.awaitInstance import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -28,8 +30,10 @@ 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.produceState 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 @@ -38,7 +42,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView import androidx.core.content.ContextCompat import androidx.core.net.toUri import androidx.lifecycle.Lifecycle @@ -115,17 +118,28 @@ internal fun OcrCameraContent( } /** - * Camera Controller + * Camera Setup (ProcessCameraProvider + Preview + ImageCapture) */ - val cameraController = remember { LifecycleCameraController(context) } - - DisposableEffect(isGranted, lifecycleOwner, cameraController) { - if (isGranted) { - cameraController.bindToLifecycle(lifecycleOwner) + var surfaceRequest by remember { mutableStateOf(null) } + val preview = remember { + Preview.Builder().build().also { + it.setSurfaceProvider { request -> + surfaceRequest = request + } } + } + val imageCapture = remember { ImageCapture.Builder().build() } - onDispose { - cameraController.unbind() + LaunchedEffect(isGranted) { + if (!isGranted) return@LaunchedEffect + ProcessCameraProvider.awaitInstance(context).apply { + unbindAll() + bindToLifecycle( + lifecycleOwner, + CameraSelector.DEFAULT_BACK_CAMERA, + preview, + imageCapture, + ) } } @@ -203,21 +217,12 @@ internal fun OcrCameraContent( .height(200.dp) .align(Alignment.Center), ) { - AndroidView( - modifier = Modifier.fillMaxSize(), - factory = { context -> - PreviewView(context).apply { - layoutParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - ) - clipToOutline = true - implementationMode = PreviewView.ImplementationMode.COMPATIBLE - scaleType = PreviewView.ScaleType.FILL_CENTER - controller = cameraController - } - }, - ) + surfaceRequest?.let { request -> + CameraXViewfinder( + surfaceRequest = request, + modifier = Modifier.fillMaxSize(), + ) + } } CameraFrame(modifier = Modifier.align(Alignment.Center)) } @@ -248,7 +253,7 @@ internal fun OcrCameraContent( val photoFile = File.createTempFile("ocr_", ".jpg", context.cacheDir) val output = ImageCapture.OutputFileOptions.Builder(photoFile).build() - cameraController.takePicture( + imageCapture.takePicture( output, executor, object : ImageCapture.OnImageSavedCallback { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab0bb25e..54e92f30 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -95,7 +95,7 @@ androidx-datastore-preferences = { group = "androidx.datastore", name = "datasto androidx-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "androidx-camera" } androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "androidx-camera" } androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "androidx-camera" } -androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "androidx-camera" } +androidx-camera-compose = { group = "androidx.camera", name = "camera-compose", version.ref = "androidx-camera" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidx-compose-bom" } androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }