Hi everybody, I'm currently trying to implement a ...
# compose
c
Hi everybody, I'm currently trying to implement a Barcode Scanner for my compose app. I'm using CameraX and Google ML Kit for this use case. Scanning the Codes and getting the values behind it is already working. Now I'd like to mark the position of the currently scanned Barcode within the camera View. I' having trouble implementing this. The barcode has a property with a bounding Box and I think I'd just have to add this as a overlay to the Preview, but I don't know how to do it. Any Ideas?
Here's my current code:
Copy code
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun QrCodeScanner(model: Model){
    var code by remember { mutableStateOf("") }
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val cameraProviderFuture = remember {
        ProcessCameraProvider.getInstance(context)
    }
    val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA)

    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        if(cameraPermissionState.hasPermission){
            AndroidView(factory = {context ->
                val previewView = PreviewView(context)
                val preview = Preview.Builder().build()
                val selector = CameraSelector.Builder()
                    .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                    .build()
                preview.setSurfaceProvider(previewView.surfaceProvider)
                val imageAnalysis = ImageAnalysis.Builder()
                    .setTargetResolution(
                        Size(
                        previewView.width,
                        previewView.height
                    )
                    )
                    .setBackpressureStrategy(
                        ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                    .build()

                imageAnalysis.setAnalyzer(
                    ContextCompat.getMainExecutor(context),
                    QrCodeAnalyzer { result->
                        code = result
                    }
                )

                try {
                    cameraProviderFuture.get().unbindAll()
                    cameraProviderFuture.get().bindToLifecycle(
                        lifecycleOwner,
                        selector,
                        preview,
                        imageAnalysis
                    )
                } catch (e: Exception){
                    e.printStackTrace()
                }
                //previewView.addView(View())
                previewView
            },
                modifier = Modifier.fillMaxSize().weight(1f))
            Column(Modifier.height(112.dp).padding(bottom = 56.dp)) {
                Row(verticalAlignment = Alignment.CenterVertically,
                    modifier = Modifier.height(56.dp).padding(horizontal = 12.dp)){
                        ConstraintLayout(Modifier
                            .fillMaxSize()
                            .background(ColorsUtil.get(FormColors.BACKGROUND_COLOR_LIGHT))
                        ) {

                            val (codeText, button) = createRefs()
                            Text(
                                text = code,
                                textAlign = TextAlign.Center,
                                modifier = Modifier.constrainAs(codeText){
                                    top.linkTo(<http://parent.top|parent.top>)
                                    start.linkTo(parent.start)
                                    bottom.linkTo(parent.bottom)
                                }
                                    .fillMaxWidth())

                            Button(onClick = {
                                model.setValueAsText(code)
                                model.publishText()
                            }, content = {
                                Text("Send")
                            },
                                modifier = Modifier.constrainAs(button){
                                    top.linkTo(<http://parent.top|parent.top>)
                                    end.linkTo(parent.end)
                                    bottom.linkTo(parent.bottom)
                                })
                        }
                }
            }
        }else if(cameraPermissionState.shouldShowRationale){
            Text("permission is required to access the camera")
        }
        else if(!cameraPermissionState.shouldShowRationale && !cameraPermissionState.hasPermission){
            Text("camera permission was permanently denied, you can enable it in the app settings")
        }
    }
Copy code
class QrCodeAnalyzer(
    private val onQrCodeScanned: (String) -> Unit
): ImageAnalysis.Analyzer {

    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image

        if (mediaImage != null) {
            val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
            val scanner = BarcodeScanning.getClient()
            scanner.process(image)
                .addOnSuccessListener { barcodes ->

                    if(barcodes.size > 0) {
                        when(barcodes[0].valueType){
                            Barcode.TYPE_URL -> barcodes[0].rawValue?.let { onQrCodeScanned(it) }
                            Barcode.TYPE_ISBN -> barcodes[0].rawValue?.let { onQrCodeScanned(it) }
                            Barcode.TYPE_TEXT -> barcodes[0].rawValue?.let { onQrCodeScanned(it) }
                            Barcode.TYPE_URL -> barcodes[0].rawValue?.let { onQrCodeScanned(it) }
                            else -> barcodes[0].rawValue?.let { onQrCodeScanned("Type not supported") }
                        }
                        Log.d("position", barcodes[0].boundingBox.toString())
                    }
                    imageProxy.close()
                    scanner.close()
                }
                .addOnFailureListener {
                    it.printStackTrace()
                    imageProxy.close()
                    scanner.close()
                }
        }

    }
}