Introduction
QR codes have become ubiquitous in recent years. I am sure you’ve seen one in a newspaper advertisement or on a billboard. In layman’s terms, QR codes, like all other barcodes, are images that are designed to be read by machines. Usually, they represent a small string, such as a shortened URL or a phone number. Here is a sample QR code that contains the URL of the Tuts+ homepage:
Unlike traditional barcodes, which need specialized hardware, QR codes can be read accurately by any smartphone with a decent camera.
The latest release of the Google Play services SDK includes the mobile vision API which, among other things, makes it very easy for Android developers to create apps capable of detecting and reading QR codes in real time. In this tutorial, I am going to help you get started with it.
Prerequisites
To follow this tutorial, you will need:
- the latest version of Android Studio
- an Android device with a camera
1. Installing the Google Play Services SDK
Before you use the mobile vision API in your app, you should add the Google Play services SDK 7.8 as a compile
dependency in your app module’s build.gradle.
compile 'com.google.android.gms:play-services:7.8.0'
When you press the Sync Now button, you will see an error that looks like this:
Click the Install repository and sync project link to install the SDK.
2. Editing the App Manifest
Add the following line to your app’s AndroidManifest.xml to automatically install the barcode detection libraries on the devices that try to run your app:
<meta-data android:name="com.google.android.gms.vision.DEPENDENCIES" android:value="barcode"/>
Additionally, as you will be using the device’s camera to capture the QR codes, you should request the android.permission.CAMERA
permission.
<uses-permission android:name="android.permission.CAMERA" />
3. Reading a QR Code From a Photo
Let’s now write some code that can read a QR code from a photo stored in your app’s assets folder. I’m going to name the photo myqrcode.jpg. If you don’t have any photos containing QR codes handy, you can get some from Flickr.
Step 1: Convert the Photo Into a Bitmap
Because the mobile vision API needs a Bitmap
as its input, you should first convert your photo into a Bitmap
. To do so, open the photo using the open
method of the AssetManager
class and pass the InputStream
returned to the decodeStream
method of BitmapFactory
. To keep it simple, do it inside the onCreate
method of your Activity
.
Bitmap myQRCode = BitmapFactory.decodeStream( getAssets().open("myqrcode.jpg") );
Step 2: Create a Barcode Detector
To detect QR codes(and other types of barcodes), you should use an instance of the BarcodeDetector
class. The following code shows you how to create one using BarcodeDetector.Builder
:
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(this) .setBarcodeFormats(Barcode.QR_CODE) .build();
Note that the detector will, by default, detect barcodes of all supported formats. I have used the setBarcodeFormats
method to explicitly specify that the detector should only detect QR codes.
Step 3: Read the QR Code
Use Frame.Builder
to create a Frame
using the Bitmap
you created earlier.
Frame myFrame = new Frame.Builder() .setBitmap(myQRCode) .build();
Call the detect
method of the BarcodeDetector
to generate a SparseArray
containing all the QR codes the BarcodeDetector
detected in your photo.
SparseArray<Barcode> barcodes = barcodeDetector.detect(myFrame);
Each item of the SparseArray
contains a Barcode
object. To fetch the raw contents of the QR code, you can use the Barcode
object’s rawValue
field. However, I suggest you use the easier to read displayValue
field instead. Here’s some code that prints the contents of the first QR code the API detected:
// Check if at least one barcode was detected if(barcodes.size() != 0) { // Print the QR code's message Log.d("My QR Code's Data", barcodes.valueAt(0).displayValue ); }
If you run your Activity
now, you should be able to see the message contained in your photo’s QR code.
4. Reading a QR Code Using the Camera
The mobile vision API also makes it very easy for you to detect and read barcodes using your device’s camera in real time. Let’s create a new Activity
that does just that.
Step 1: Define the Layout
Create a new layout XML file called activity_main.xml. The layout should have a SurfaceView
in order to display the preview frames captured by the camera. If you want, you can also add a TextView
to display the contents of the QR codes the API detects.
After using a RelativeLayout
to position both widgets, the layout XML file should look something like this:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <SurfaceView android:layout_width="640px" android:layout_height="480px" android:layout_centerVertical="true" android:layout_alignParentLeft="true" android:id="@+id/camera_view" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/code_info" android:layout_toRightOf="@+id/camera_view" android:textSize="20sp" android:layout_marginLeft="16dp" android:text="Nothing to read." android:layout_alignParentTop="true" /> </RelativeLayout>
Step 2: Create the Activity
Create a new Java class called MainActivity.java. Make it a subclass of Activity
and override its onCreate
method. Inside the onCreate
method, call setContentView
to apply the layout you created in the previous step. Next, use findViewById
to get references to the widgets defined in the layout.
setContentView(R.layout.activity_main); cameraView = (SurfaceView)findViewById(R.id.camera_view); barcodeInfo = (TextView)findViewById(R.id.code_info);
To fetch a stream of images from the device’s camera and display them in the SurfaceView
, create a new instance of the CameraSource
class using CameraSource.Builder
. Because the CameraSource
needs a BarcodeDetector
, create one using the BarcodeDetector.Builder
class. If you want, you can adjust the dimensions of the camera preview using the setRequestedPreviewSize
method.
barcodeDetector = new BarcodeDetector.Builder(this) .setBarcodeFormats(Barcode.QR_CODE) .build(); cameraSource = new CameraSource .Builder(this, barcodeDetector) .setRequestedPreviewSize(640, 480) .build();
Next, add a callback to the SurfaceHolder
of the SurfaceView
so that you know when you can start drawing the preview frames. The callback should implement the SurfaceHolder.Callback
interface.
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } });
Inside the surfaceCreated
method, call the start
method of the CameraSource
to start drawing the preview frames. Because the start
method expects you to handle an IOException
, you should call it from inside a try...catch
block.
try { cameraSource.start(cameraView.getHolder()); } catch (IOException ie) { Log.e("CAMERA SOURCE", ie.getMessage()); }
Similarly, inside the surfaceDestroyed
method, call the stop
method of the CameraSource
to stop drawing the preview frames.
cameraSource.stop();
Your Activity
is almost ready. However, you still need to tell the BarcodeDetector
what it should do when it detects a QR code. Create an instance of a class that implements the Detector.Processor
interface and pass it to the setProcessor
method of the BarcodeDetector
. Android Studio will automatically generate stubs for the methods of the interface.
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() { @Override public void release() { } @Override public void receiveDetections(Detector.Detections<Barcode> detections) { } });
Inside the receiveDetections
method, get the SparseArray
of Barcode
objects by calling the getDetectedItems
method of the Detector.Detections
class. You can now write the code to do something with the detected QR codes, because I have already shown you how to work with SpareArray
objects earlier in this tutorial.
Here’s how you can display the QR code’s displayValue
in the TextView
:
final SparseArray<Barcode> barcodes = detections.getDetectedItems(); if (barcodes.size() != 0) { barcodeInfo.post(new Runnable() { // Use the post method of the TextView public void run() { barcodeInfo.setText( // Update the TextView barcodes.valueAt(0).displayValue ); } }); }
Note that you should embed the call to the setText
method inside a call to the post
method of the TextView
, because receiveDetections
does not run on the UI thread. Failing to do so will lead to a runtime error.
You can now compile and run your app. Point your device’s camera to a QR code and you should be able to see the QR code’s contents immediately.
Conclusion
In this tutorial, you learned how to use the mobile vision API to read QR codes from static images as well as from live camera streams. Even though we only worked with QR codes in this tutorial, you can also use the API to read other popular barcode formats such as UPC-A and EAN-13.
To learn more about the mobile vision API, I recommend visiting the API’s documentation.