Trying to migrate barcode scanner to Jetpack compose and updating camera and ML Kit dependencies to latest version.
The current shows the camera view correctly, but it is not scanning the barcodes.
The ImageAnalysis
analyzer runs only once.
Code
@Composable
fun CameraPreview(
data: CameraPreviewData,
) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
AndroidView(
modifier = Modifier
.fillMaxSize(),
factory = { AndroidViewContext ->
PreviewView(AndroidViewContext).apply {
this.scaleType = PreviewView.ScaleType.FILL_CENTER
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
// Preview is incorrectly scaled in Compose on some devices without this
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
}
},
update = { previewView ->
val cameraSelector: CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> =
ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val preview: Preview = Preview.Builder()
.build()
.also {
// Attach the viewfinder's surface provider to preview use case
it.setSurfaceProvider(previewView.surfaceProvider)
}
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
val barcodeAnalyser = BarcodeAnalyser { barcodes ->
barcodes.forEach { barcode ->
barcode.rawValue?.let { barcodeValue ->
logError("Barcode value detected: ${barcodeValue}.")
// Other handling code
}
}
}
val imageAnalysis: ImageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
it.setAnalyzer(cameraExecutor, barcodeAnalyser)
}
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview,
imageAnalysis
)
} catch (exception: Exception) {
logError("Use case binding failed with exception : $exception")
}
}, ContextCompat.getMainExecutor(context))
},
)
}
BarcodeAnalyser
class BarcodeAnalyser(
private val onBarcodesDetected: (barcodes: List<Barcode>) -> Unit,
) : ImageAnalysis.Analyzer {
private var lastAnalyzedTimestamp = 0L
override fun analyze(
imageProxy: ImageProxy,
) {
logError("Inside analyze")
val currentTimestamp = System.currentTimeMillis()
if (currentTimestamp - lastAnalyzedTimestamp >= TimeUnit.SECONDS.toMillis(1)) {
imageProxy.image?.let { imageToAnalyze ->
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
.build()
val barcodeScanner = BarcodeScanning.getClient(options)
val imageToProcess =
InputImage.fromMediaImage(imageToAnalyze, imageProxy.imageInfo.rotationDegrees)
barcodeScanner.process(imageToProcess)
.addOnSuccessListener { barcodes ->
if (barcodes.isNotEmpty()) {
logError("Scanned: $barcodes")
onBarcodesDetected(barcodes)
imageProxy.close()
} else {
logError("No barcode scanned")
}
}
.addOnFailureListener { exception ->
logError("BarcodeAnalyser: Something went wrong with exception: $exception")
imageProxy.close()
}
}
lastAnalyzedTimestamp = currentTimestamp
}
}
}
References
Thanks to Adrian's comment.
It worked after the following changes.
In BarcodeAnalyser
imageProxy.close()
from addOnSuccessListener
and addOnFailureListener
. Added it to addOnCompleteListener
.imageProxy.close()
in else condition as well.class BarcodeAnalyser(
private val onBarcodesDetected: (barcodes: List<Barcode>) -> Unit,
) : ImageAnalysis.Analyzer {
private var lastAnalyzedTimestamp = 0L
override fun analyze(
imageProxy: ImageProxy,
) {
logError("Inside analyze")
val currentTimestamp = System.currentTimeMillis()
if (currentTimestamp - lastAnalyzedTimestamp >= TimeUnit.SECONDS.toMillis(1)) {
imageProxy.image?.let { imageToAnalyze ->
// ...Same code
barcodeScanner.process(imageToProcess)
.addOnSuccessListener { barcodes ->
if (barcodes.isNotEmpty()) {
logError("Scanned: $barcodes")
onBarcodesDetected(barcodes)
// imageProxy.close()
} else {
logError("No barcode scanned")
}
}
.addOnFailureListener { exception ->
logError("BarcodeAnalyser: Something went wrong with exception: $exception")
// imageProxy.close()
}
.addOnCompleteListener {
imageProxy.close()
}
}
lastAnalyzedTimestamp = currentTimestamp
} else {
imageProxy.close()
}
}
}