Barcode Scanner with CameraX and MLKit

Image for post
Image for post
Barcode and Android icons made by Freepik from www.flaticon.com

Intro

Image for post
Image for post

Let’s get crackin!

Overview

We are going to break down the app into three parts:

  1. Setting up CameraX, and its Preview use case
  2. Integrating MLKit into our project and preparing the barcode scanner
  3. Feeding the camera’s image into the scanner using CameraX Image Analysis use case

Simple as that.

1. CameraX

The Setup

implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha18"

Make sure the following plugin is right there at the top, too:

apply plugin: 'kotlin-android-extensions'

And we are going to be using Java 8, so let’s set our compile options as:

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

The View

The Code

So, let’s start the camera. Be sure to read the comments inside the code snippet if you want a more detailed understanding of the startCamera() method.

This will change a little later, but it’s enough for the camera to start streaming into the view.

So far, so good. Let’s leave this as it is for a moment and we’ll come back to it later.

2. MLKit

ML Kit is a mobile SDK that brings Google’s on-device machine learning expertise to Android and iOS apps.

…and…

ML Kit’s APIs all run on-device, allowing for real-time use cases where you want to process a live camera stream for example.

MLKit has a BUNCH of applications, all the way from Pose Detection to Digital Ink Recognition. Of course we are interested in the barcode scanning capabilities.

MLKit can also extract a whole lot more information from a barcode other than its raw value, for instance WiFi access point details or geographic coordinates.

For each barcode, you can get its bounding coordinates in the input image, as well as the raw data encoded by the barcode. Also, if the barcode scanner was able to determine the type of data encoded by the barcode, you can get an object containing parsed data.

We are not going to tap into any of that encoded information but you can read more about it here.

The Setup

implementation 'com.google.mlkit:barcode-scanning:16.0.3'

The Code

Couple of things to make a note of:

Although it’s enough to call BarcodeScanning.getClient() to get an instance of BarcodeScanner, we might want to do some previous configuration for say, only scanning QR codes. That could be done easily using the BarcodeScannerOptions builder which you can checkout in the documentation.

Also, you might see on other projects out there some “rotation compensation” method, or piece of code, which gets the device rotation and performs some calculation. This is not necessary in this case since the ImageAnalysis.Analyzer does it for us. You can see the rotation value as the second parameter to the InputImage.fromMediaImage method.

Finally, the barcodeListener we are going to pass to the analyzer is nothing more than an instance of this:

typealias BarcodeListener = (barcode: String) -> Unit

3. Connecting the Parts

In our Fragment, right after building our preview, we are going to build an instance of the ImageAnalysis use case. ImageAnalysis acquires images from the camera through an ImageReader and provides them to an ImageAnalysis.Analyzer (our BarcodeAnalyzer).

Here, the processingBarcode AtomicBoolean, is nothing more than a flag that helps us process one barcode at a time, preventing a flood of calls to the searchBracode method. This method is just an operation we want to do with the previously read barcode value. For the purpose of this project, on a successful read, the searchBarcode method will navigate us to a success screen after a 1-second delay.

The cameraExecutor is the executor in which the analyzer will be run:

cameraExecutor = Executors.newSingleThreadExecutor()

Now, we need to add this use case to the method call that binds the use cases to the lifecycleOwner…the piece of code inside the try catch block, remember? So this…

…becomes this.

That SHOULD cover everything we need…

Image for post
Image for post

Ok, ok, let’s give it a go and see what happens!

Image for post
Image for post

Yes! So, what you see there is a half-eaten chocolate bar wrapper, and the scanner reading the wrapper’s barcode properly!

Remember, we are not really doing anything with the code we are scanning. The progress bar just shows for one second and then we navigate to a “success” screen. This, is where you would perform the actual query, API call, or whatever it is you need to do.

Here are the links to the documentation and repo:

Github repo:
github.com/BarcodeScannerSample.git

CameraX Documentation:
https://developer.android.com/training/camerax

CameraX Codelab (love these things):
https://codelabs.developers.google.com/codelabs/camerax-getting-started/#0

MLKit Documentation:
https://developers.google.com/ml-kit/guides

I really hope you liked the article, that it was worth the read, and hopefully someday soon I’ll come across something worth sharing again.

Until next time!

Image for post
Image for post

Passionate Android developer with tons left to learn. https://www.linkedin.com/in/miguellasa/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store