LGstudio
LGstudio

Reputation: 117

Android Compose PreviewView Bitmap is null, even if the preview is working

I am working on a specialized industrial device running Andoid 7.1 I want to make screenshots of the camera preview (to jpg) into the cache storage, when an event happens on the device (that event it is triggering a LaunchedEffect block).

My current code looks like this:

@Composable
fun CameraScreen() {

    val coroutineScope = rememberCoroutineScope()
    val lifecycle = LocalLifecycleOwner.current
    val ctx = LocalContext.current
    val vm = viewModel<MyViewModel>()

    var cameraProvider: ProcessCameraProvider
    var cameraPreviewView = PreviewView(ctx).apply {
        this.scaleType = scaleType
        layoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
        implementationMode = PreviewView.ImplementationMode.COMPATIBLE
    }

    val imageCapture by remember {
        mutableStateOf(
            ImageCapture.Builder()
                .setJpegQuality(80)
                .setResolutionSelector(
                    ResolutionSelector.Builder()
                        .setResolutionStrategy(
                            ResolutionStrategy(
                                Size(640, 480),
                                ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER
                            )
                        )
                        .setAspectRatioStrategy(AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY)
                        .build()
                )
                .setCaptureMode(CAPTURE_MODE_MINIMIZE_LATENCY)
                .build()
        )
    }

    LaunchedEffect(vm.eventHappened) {
        if (vm.eventHappened) {
            val bitmap = cameraPreviewView.bitmap  // !!! HERE BITMAP IS ALWAYS NULL
            if (bitmap != null) {
                saveBitmapImageToFile(vm.photoFile, bitmap)
            }
        }
    }

    Box(
        modifier = Modifier.fillMaxSize()
    ) {

        AndroidView(
            modifier = Modifier.width(pixelToDp(640)).height(pixelToDp(480)),
            factory = { context ->
                val previewUseCase = Preview.Builder()
                    .build()
                    .also {
                        it.surfaceProvider = cameraPreviewView.surfaceProvider
                    }

                coroutineScope.launch {
                    val cameraProviderFuture = ProcessCameraProvider.getInstance(ctx)
                    cameraProviderFuture.addListener({
                        cameraProvider = cameraProviderFuture.get()
                        val cameraSelector = CameraSelector.Builder()
                            .apply {
                               requireLensFacing(CameraSelector.LENS_FACING_BACK)
                            }.build()
                        cameraProvider.unbindAll()
                        cameraProvider.bindToLifecycle(lifecycle, cameraSelector, previewUseCase, imageCapture)
                    }, ContextCompat.getMainExecutor(ctx))
                }
                cameraPreviewView
            }
        )
    }
}


The camera preview works with no problems, but when the vm.eventHappened is triggered, the bitmap returned from the cameraPreviewView is null.

I have also tried calling cameraPreviewView.drawToBitmap() but that crashes with exception saying View needs to be laid out before calling drawToBitmap()

Thanks for all the help.

Upvotes: 0

Views: 42

Answers (2)

LGstudio
LGstudio

Reputation: 117

The only working solution was to use ImageAnalysis instead of ImageCapture

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(640, 480)) 
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .build()

imageAnalysis.setAnalyzer(cameraExecutor, ImageAnalysis.Analyzer { imageProxy ->
     if (vm.takePhoto) {
         vm.lastPhoto = imageProxy.toBitmap()
         vm.takePhoto = false
     }
     imageProxy.close()
})

cameraProvider.bindToLifecycle(lifecycle, cameraSelector, previewUseCase, imageAnalysis)

Upvotes: 0

kolgan35
kolgan35

Reputation: 1

You need to wrap the cameraProviderView in a remember function like this:

val cameraPreviewView by remember { 
        mutableStateOf(
            PreviewView(ctx).apply {
                this.scaleType = scaleType
                layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                )
                implementationMode = PreviewView.ImplementationMode.COMPATIBLE
            }
        )
    }

Upvotes: 0

Related Questions