Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8239b139 authored by cketti's avatar cketti
Browse files

Add `CameraPreviewView`

parent 78c9b13f
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -14,6 +14,10 @@ dependencies {
    implementation(projects.core.ui.compose.designsystem)
    debugImplementation(projects.core.ui.compose.theme2.k9mail)

    implementation(libs.androidx.camera.core)
    implementation(libs.androidx.camera.camera2)
    implementation(libs.androidx.camera.lifecycle)
    implementation(libs.androidx.camera.view)
    implementation(libs.moshi)
    implementation(libs.timber)

+81 −0
Original line number Diff line number Diff line
package app.k9mail.feature.migration.qrcode.ui

import android.content.Context
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.foundation.background
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.LocalLifecycleOwner
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleLarge
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

@Composable
internal fun CameraPreviewView(
    modifier: Modifier = Modifier,
) {
    if (LocalInspectionMode.current) {
        TextTitleLarge(
            text = "Camera preview",
            textAlign = TextAlign.Center,
            modifier = modifier.background(Color.Cyan),
        )
        return
    }

    val lensFacing = CameraSelector.LENS_FACING_BACK

    val lifecycleOwner = LocalLifecycleOwner.current
    val context = LocalContext.current

    val previewView = remember {
        PreviewView(context).apply {
            scaleType = PreviewView.ScaleType.FIT_CENTER
        }
    }

    LaunchedEffect(lensFacing) {
        val cameraProvider = getProcessCameraProvider(context)

        val cameraxSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
        val preview = Preview.Builder().build()

        cameraProvider.unbindAll()
        cameraProvider.bindToLifecycle(lifecycleOwner, cameraxSelector, preview)

        preview.setSurfaceProvider(previewView.surfaceProvider)
    }

    AndroidView(
        factory = { previewView },
        modifier = modifier,
    )
}

/**
 * Suspending function to retrieve a [ProcessCameraProvider].
 */
private suspend fun getProcessCameraProvider(context: Context): ProcessCameraProvider {
    return suspendCoroutine { continuation ->
        val mainExecutor = ContextCompat.getMainExecutor(context)

        ProcessCameraProvider.getInstance(context).also { processCameraProvider ->
            val listener = Runnable {
                continuation.resume(processCameraProvider.get())
            }

            processCameraProvider.addListener(listener, mainExecutor)
        }
    }
}
+7 −5
Original line number Diff line number Diff line
package app.k9mail.feature.migration.qrcode.ui

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge

@Composable
internal fun QrCodeScannerView() {
    TextBodyLarge(
        text = "TODO: implement",
        modifier = Modifier.testTag("QrCodeScannerView"),
    Column(modifier = Modifier.testTag("QrCodeScannerView")) {
        CameraPreviewView(
            modifier = Modifier.fillMaxSize(),
        )
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ androidxActivity = "1.9.2"
androidxAnnotation = "1.8.2"
androidxAppCompat = "1.7.0"
androidxBiometric = "1.1.0"
androidxCamera = "1.3.1"
# https://developer.android.com/jetpack/compose/bom/bom-mapping
androidxComposeBom = "2024.09.00"
androidxConstraintLayout = "2.1.4"
@@ -122,6 +123,10 @@ androidx-activity-compose = { module = "androidx.activity:activity-compose", ver
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidxAnnotation" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidxAppCompat" }
androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "androidxBiometric" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidxCamera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidxCamera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidxCamera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidxCamera" }
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidxComposeBom" }
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }