Scan barcodes in Android using the ML Kit

How to implement a real-time barcode scanner in Kotlin with the help of Google APIs

Luca Pizzini
CodeX
6 min readSep 8, 2021

--

Photo by Albert Hu on Unsplash

One very common functionality requested in mobile applications is the ability to detect, scan and analyze barcodes.

There are a lot of open-source libraries on Github that suit the need.
Anyway, I think that using the official Google APIs is a better approach.
Official libraries are in general better maintained and optimized.

Google has developed a series of APIs dedicated to mobile developers which need to add Machine Learning capabilities to their application.

The ML Kit is compatible with both iOS and Android.
You can check all of its functionalities in the official documentation.

Required libraries

For this project, you will need to use two libraries:

  1. Barcode scanning API
    This library is part of the ML Kit Vision API.
    As its name suggests, its function is to read data from a variety of barcode formats.
    You can find more information on this API on the official documentation.
  2. CameraX
    This support library is part of the Android Jetpack suite.
    It provides an API to make camera-related functionalities easy to develop and implement.
    You can read more about the library in the official documentation.

Project setup

The first step to get started is to add the required dependencies and permissions to your project.

  1. Include the mavenCentral() repository into your project-level build.gradle file.
  2. Add the dependencies into your application-level build.gradle file:
// Barcode scanning API
implementation 'com.google.mlkit:barcode-scanning:17.0.0'
// CameraX library
def camerax_version = "1.0.1"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha28"

3. Add the required CAMERA permission into your AndroidManifest.xml file:

<uses-permission android:name="android.permission.CAMERA" />

Once you have completed these configuration steps and synchronized all the dependencies, you are ready to pass to the implementation phase.

Project implementation

Let’s implement a simple barcode scanner using a single Activity application.

1. Create a subclass of the ImageAnalysis.Analyzer class

This class will be responsible for processing all frames received from the camera.

The class must override the analyze(image: ImageProxy) method.
This method will receive every frame captured from the camera as an ImageProxy object and will allow you to process it.

To set up the processing environment you need to:

  1. Create an InputImage object from the received image via the fromMediaImage method
  2. Create a BarcodeScanner object that will process the InputImage.
    This object will receive a BarcodeScannerOptions parameter for its configuration.
    Once initialized, you will be able to process the received barcodes or handle the errors that occurred during the scanning phase.

You can optimize the scanning process by specifying accepted formats by the scanner using the setBarcodeFormats method.

It is important to call the image.close() method once the analysis is terminated.
This will free the analysis queue for other images to be processed.

Here is a full implementation of the class:

2. Create the BarcodeScanner activity

Now, is time to set up the main Activity of the application.

Create an XML layout file in your /res/layout folder, let’s call it activity_barcode_scanner.xml

As you can see, this layout contains only the PreviewView component.
This will be responsible for showing the camera preview on the screen.

Then, create the Activity associated with the layout, let’s call it BarcodeScannerActivity.kt

There are 3 basic features to implement in this class:

  1. Setting up a ExecutorService instance.
  2. Handling the request of the Android.manifest.CAMERA permission.
  3. Write the code responsible for the setup of the camera preview and bind it to the image analyzer created previously.

Let’s start by initializing theExecutorService.

This class provides methods to produce a Future, which will be responsible for tracking the tasks generated by the camera.
You can find more information on the official documentation.

We declare a lateinit var which will be initialized on the activity creation, and shut down once the activity is destroyed.

Then, is time for the code responsible for the permission request.

You need to implement 3 methods:

  1. A function that is responsible to prompt the user for permission, if this has not already been granted.
  2. A function that controls if the permission has been granted, and prompts the user if that is not the case.
  3. A function that analyzes the response of the user to the permission request.

You can see an implementation of the above functions in the code below.
Once the activity has been created you can start asking the user for permission by calling the checkCameraPermission.

After the permission has been correctly managed, let’s implement the camera setup code.

You will need to declare a single method.
This method will need to:

  1. Retrieve a ProcessCameraProvider instance.
    This object will bind the camera lifecycle to the application.
  2. Setup the Preview object, connected with the PreviewView declared in the layout
  3. Setup the QrCodeAnalyzer class.
    You should specify the ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST for optimization purposes.
    By doing that only one frame will be delivered for analysis each time, avoiding the process queue to grow in size.
  4. Select the preferred camera for the preview via the CameraSelector attributes
  5. Setup the camera session with the specified configurations using the bindToLifecycle method.

Here is a function that implements these steps.

You should be now able to scan barcodes with your application:

3. (Optional) Draw a graphic outline on the barcode

An optional feature that you can implement in your application is adding a bounding rectangle around the detected barcode.
Adding this feature will give friendly feedback to the user.

You can add a graphic outline by following 2 steps:

  1. Implement a subclass of the View class.
    This class will be responsible for drawing the rectangle on the screen.
  2. Edit your ImageAnalysis.Analyzer class to handle the addition of the View class when a barcode is detected.

Let’s start with the creation of the View subclass.
Create a new file, let’s call it BarcodeBoxView.

This class will receive a Context parameter to conform to the View constructor.

Also, you will use two global variables:

  1. A Paint object.
    This will be responsible for storing the style information of the bounding box.
  2. A RectF object.
    It will contain the coordinates of the rectangle that will be drawn on the screen.
    This variable will have a custom setter that will update its value.

The main function of the class is the onDraw(canvas: Canvas?) method.
It is responsible for displaying the desired figure on the screen every time the setRect method is called.

Here is a complete implementation of the class:

Once you have created this class you need to modify the QrCodeAnalyzer class to update the bounding box every time a new barcode is visualized.

To do that you need to add 3 variables to the class constructor:

  1. The BarcodeBoxView instance initialized in the BarcodeScannerActivity
  2. The width of the PreviewView
  3. The height of the PreviewView

The last two parameters will be used to adjust the scale of the bounding box relative to the size of the PreviewView.

After that, you need to implement the code responsible to scale the bounding box to adapt it at PreviewView‘s dimensions.

You can do that by following these steps:

  1. Declaring a horizontal and vertical scale factor, namely scaleX and scaleY.
  2. Declaring two functions, responsible for the adjustments of the parameters. Call this functions translateX and translateY.
  3. Declaring a function that will apply the adjustments to the Rect received from the barcode.boundingBox parameter.

After that initialization phase is completed, you can update the bounding box of the barcodes by using the setRect method of the BarcodeBoxView class.

Here is a complete implementation of the class discussed:

Finally, to make it work you’ll need to modify your QrCodeAnalyzer constructor in the startCamera method by adding the required parameters:

QrCodeAnalyzer(
this,
barcodeBoxView,
binding.previewView.width.toFloat(),
binding.previewView.height.toFloat()
)

Once you have completed these steps you should be able to see a bounding box displayed every time a barcode is scanned.

Wrapping up

That’s it for the implementation of the functionality.
You can find the complete example project at this GitHub repo.

I hope that this article will help you build amazing applications and made it clearer to you how to integrate ML Kit libraries using Android.

Happy coding!

--

--

Luca Pizzini
CodeX
Writer for

Software developer from Italy | Just a regular guy who loves to code and learn 💻📚 https://lucapizzini.com