Jetpack compose Barcode/QR code scanner
Jetpack compose Barcode QR code scanner using Google’s ML Kit and CameraX. In this article, we’ll discover how to build a jetpack compose Barcode scanner using Google the ML Kit and Jetpack CameraX.
This is the first version of ML Kit as a standalone SDK that is independent of Firebase. The SDK includes all of the on-device APIs previously available through Firebase’s ML Kit for Firebase SDK. See ML Kit Release Notes.
A stand-alone library to run ML on your device that you can use in conjunction with without Firebase. Jetpack compose Barcode QR code scanner
What’s jetpack CameraX?
Table of Contents
CameraX is a Jetpack support library that was designed to make the development of camera apps simpler. It provides a consistent and easy-to-use API surface that works across most Android devices, with backward compatibility to Android 5.0 (API level 21). While it leverages the capabilities of camera2, it uses a simpler, use the case-based approach that is lifecycle-aware.
What’s Google ML Kit?
ML Kit brings Google’s machine learning capabilities to mobile developers with an easy-to-use and powerful package. Create iOS and Android apps more interesting personal, efficient, and useful with tools that are designed for use on mobile devices.
ML Kit’s Barcode Scanner API ( Barcode QR code scanner )
Through ML Kit’s barcode scanning API, you are able to scan data encoded with the most commonly used barcode formats. Barcode scanning is performed within the device, and doesn’t require a connection.
Jetpack compose Barcode QR code scanner Example
Let’s Start your Android Studio and create a new android project with -> Select the jetpack Compose Activity.
After the finish, the steps and wait to build your project after successfully building add the below dependency in your Gradle Build.
Adding jetpack composes Camerax dependency.
def camerax_version = "1.0.2" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" implementation "androidx.camera:camera-view:1.0.0-alpha29"
Adding BarCode Scanner MLKit Dependency.
//Barcode implementation 'com.google.mlkit:barcode-scanning:17.0.0'
Adding Camera Permission dependency
//Camera Permission implementation "com.google.accompanist:accompanist-permissions:0.19.0"
All dependency:- for Jetpack compose Barcode QR code scanner
dependencies { implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.0' implementation 'com.google.android.material:material:1.4.0' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' implementation 'androidx.activity:activity-compose:1.4.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" def camerax_version = "1.0.2" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" implementation "androidx.camera:camera-view:1.0.0-alpha29" //Barcode implementation 'com.google.mlkit:barcode-scanning:17.0.0' //Camera Permission implementation "com.google.accompanist:accompanist-permissions:0.19.0" }
Add Permission in Manifest file :
<uses-feature android:name="android.hardware.camera" /> <uses-permission android:name="android.permission.CAMERA" />
Step 2: Create a Kotlin class With the Name BarCodeAnalyser.
you can use below source code for the Analyser bar code to get the output for the barcode.
import android.annotation.SuppressLint import android.util.Log import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageProxy import com.google.mlkit.vision.barcode.Barcode import com.google.mlkit.vision.barcode.BarcodeScannerOptions import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.common.InputImage import java.util.concurrent.TimeUnit @SuppressLint("UnsafeOptInUsageError") class BarCodeAnalyser( private val onBarcodeDetected: (barcodes: List<Barcode>) -> Unit, ): ImageAnalysis.Analyzer { private var lastAnalyzedTimeStamp = 0L override fun analyze(image: ImageProxy) { val currentTimestamp = System.currentTimeMillis() if (currentTimestamp - lastAnalyzedTimeStamp >= TimeUnit.SECONDS.toMillis(1)) { image.image?.let { imageToAnalyze -> val options = BarcodeScannerOptions.Builder() .setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS) .build() val barcodeScanner = BarcodeScanning.getClient(options) val imageToProcess = InputImage.fromMediaImage(imageToAnalyze, image.imageInfo.rotationDegrees) barcodeScanner.process(imageToProcess) .addOnSuccessListener { barcodes -> if (barcodes.isNotEmpty()) { onBarcodeDetected(barcodes) } else { Log.d("TAG", "analyze: No barcode Scanned") } } .addOnFailureListener { exception -> Log.d("TAG", "BarcodeAnalyser: Something went wrong $exception") } .addOnCompleteListener { image.close() } } lastAnalyzedTimeStamp = currentTimestamp } else { image.close() } } }
Step 3 Update your MainActivity.kt class.
import android.Manifest import android.os.Bundle import android.util.Log import android.view.ViewGroup import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.camera.core.CameraSelector import androidx.camera.core.ImageAnalysis import androidx.camera.core.Preview import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.view.PreviewView import androidx.compose.foundation.layout.* import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.core.content.ContextCompat import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberPermissionState import com.google.common.util.concurrent.ListenableFuture import com.jetpack.barcodescanner.ui.theme.BarcodeScannerTheme import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @ExperimentalPermissionsApi class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { BarcodeScannerTheme { Surface(color = MaterialTheme.colors.background) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Bottom ) { Spacer(modifier = Modifier.height(10.dp)) val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA) Button( onClick = { cameraPermissionState.launchPermissionRequest() } ) { Text(text = "Camera Permission") } Spacer(modifier = Modifier.height(10.dp)) CameraPreview() } } } } } } @Composable fun CameraPreview() { val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current var preview by remember { mutableStateOf<Preview?>(null) } val barCodeVal = remember { mutableStateOf("") } AndroidView( factory = { AndroidViewContext -> PreviewView(AndroidViewContext).apply { this.scaleType = PreviewView.ScaleType.FILL_CENTER layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, ) implementationMode = PreviewView.ImplementationMode.COMPATIBLE } }, modifier = Modifier.fillMaxSize().padding(50.dp), 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({ preview = Preview.Builder().build().also { it.setSurfaceProvider(previewView.surfaceProvider) } val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() val barcodeAnalyser = BarCodeAnalyser { barcodes -> barcodes.forEach { barcode -> barcode.rawValue?.let { barcodeValue -> barCodeVal.value = barcodeValue Toast.makeText(context, barcodeValue, Toast.LENGTH_SHORT).show() } } } 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 (e: Exception) { Log.d("TAG", "CameraPreview: ${e.localizedMessage}") } }, ContextCompat.getMainExecutor(context)) } ) }
After adding the above code to your main activity run your project and check the Output. For testing you can scan any QR code output will show a Toast here. Jetpack compose Barcode/QR code scanner.
Read More Tutorial
- Codeplayon Jetpack Compose Tutorial
- Codeplayon Android Tutorial
- Codeplayon Flutter Tutorial
- Codeplayon on Github