Scan barcodes in Android using the ML Kit
How to implement a real-time barcode scanner in Kotlin with the help of Google APIs
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:
- 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. - 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.
- Include the
mavenCentral()
repository into your project-levelbuild.gradle
file. - 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:
- Create an
InputImage
object from the received image via thefromMediaImage
method - Create a
BarcodeScanner
object that will process theInputImage
.
This object will receive aBarcodeScannerOptions
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:
- Setting up a
ExecutorService
instance. - Handling the request of the
Android.manifest.CAMERA
permission. - 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:
- A function that is responsible to prompt the user for permission, if this has not already been granted.
- A function that controls if the permission has been granted, and prompts the user if that is not the case.
- 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:
- Retrieve a
ProcessCameraProvider
instance.
This object will bind the camera lifecycle to the application. - Setup the
Preview
object, connected with thePreviewView
declared in the layout - Setup the
QrCodeAnalyzer
class.
You should specify theImageAnalysis.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. - Select the preferred camera for the preview via the
CameraSelector
attributes - 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:
- Implement a subclass of the
View
class.
This class will be responsible for drawing the rectangle on the screen. - Edit your
ImageAnalysis.Analyzer
class to handle the addition of theView
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:
- A
Paint
object.
This will be responsible for storing the style information of the bounding box. - A
RectF
object.
It will contain the coordinates of the rectangle that will be drawn on the screen.
This variable will have a customsetter
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:
- The
BarcodeBoxView
instance initialized in theBarcodeScannerActivity
- The width of the
PreviewView
- 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:
- Declaring a horizontal and vertical scale factor, namely
scaleX
andscaleY
. - Declaring two functions, responsible for the adjustments of the parameters. Call this functions
translateX
andtranslateY
. - Declaring a function that will apply the adjustments to the
Rect
received from thebarcode.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!