Skip to content

Instantly share code, notes, and snippets.

@dgmltn
Created February 2, 2025 21:50
Show Gist options
  • Save dgmltn/c5ac4941185d7923a41284c8adaacc13 to your computer and use it in GitHub Desktop.
Save dgmltn/c5ac4941185d7923a41284c8adaacc13 to your computer and use it in GitHub Desktop.
MLKit + AndroidX Compose Camera QR Code Scanner
implementation(libs.bundles.camerax)
implementation(libs.kermit)
import android.os.Looper
import androidx.annotation.OptIn
import androidx.camera.compose.CameraXViewfinder
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageAnalysis.COORDINATE_SYSTEM_ORIGINAL
import androidx.camera.core.Preview
import androidx.camera.core.SurfaceRequest
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.lifecycle.awaitInstance
import androidx.camera.mlkit.vision.MlKitAnalyzer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat
import androidx.core.util.Consumer
import androidx.lifecycle.compose.LifecycleStartEffect
import androidx.lifecycle.compose.LocalLifecycleOwner
import co.touchlab.kermit.Logger
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import kotlinx.coroutines.launch
import java.util.concurrent.Executors
@OptIn(ExperimentalGetImage::class)
@Composable
fun CameraXandMlKitQrCodeScanner(
onScanned: (String) -> Unit,
modifier: Modifier = Modifier,
) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
var currentSurfaceRequest: SurfaceRequest? by remember { mutableStateOf(null) }
val lifecycleOwner = LocalLifecycleOwner.current
// CameraX Lifecycle-aware controller
val analysisExecutor = remember { Executors.newSingleThreadExecutor() }
val mainThreadExecutor = remember { ContextCompat.getMainExecutor(context) }
// Select back-facing camera
val cameraSelector = remember {
CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
}
val barcodeScanner = remember {
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
.enableAllPotentialBarcodes()
.build()
BarcodeScanning.getClient(options)
}
val mlKitConsumer = object: Consumer<MlKitAnalyzer.Result> {
override fun accept(result: MlKitAnalyzer.Result) {
check(Looper.myLooper() == Looper.getMainLooper()) { "must be called on the main thread" }
val barcodeResults: List<Barcode>? = result.getValue(barcodeScanner)
barcodeResults?.let {
it.mapNotNull { it.rawValue }.filter { it.isNotEmpty() }.forEach { barcode ->
onScanned(barcode)
Logger.i { "Found Barcode: $barcode" }
}
}
}
}
val previewUseCase = remember {
Preview.Builder()
.build()
.apply {
setSurfaceProvider {
currentSurfaceRequest = it
}
}
}
val qrCodeAnalyzerUseCase = remember {
ImageAnalysis
.Builder()
.build()
.apply {
setAnalyzer(
analysisExecutor,
MlKitAnalyzer(
listOf(barcodeScanner),
COORDINATE_SYSTEM_ORIGINAL,
mainThreadExecutor,
mlKitConsumer
)
)
}
}
LifecycleStartEffect(Unit) {
var cameraProvider: ProcessCameraProvider? = null
scope.launch {
cameraProvider = ProcessCameraProvider.awaitInstance(context)
Logger.d { "cameraProvider $cameraProvider" }
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, previewUseCase, qrCodeAnalyzerUseCase)
}
onStopOrDispose {
cameraProvider?.unbindAll()
}
}
currentSurfaceRequest?.let { surfaceRequest ->
CameraXViewfinder(
surfaceRequest = surfaceRequest,
modifier = modifier
.fillMaxSize()
)
}
}
[versions]
# https://github.com/touchlab/Kermit/releases
kermit = "2.0.2"
# https://developer.android.com/jetpack/androidx/releases/camera
# https://developer.android.com/reference/kotlin/androidx/camera/compose/package-summary
camerax = "1.5.0-alpha05"
[libraries]
kermit = { group = "co.touchlab", name = "kermit", version.ref = "kermit" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camerax" }
androidx-camera-mlkit-vision = { module = "androidx.camera:camera-mlkit-vision", version.ref = "camerax" }
androidx-camera-compose = { module = "androidx.camera:camera-compose", version.ref = "camerax" }
[bundles]
camerax = [ "androidx-camera-camera2", "androidx-camera-lifecycle", "androidx-camera-mlkit-vision", "androidx-camera-compose" ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment