Reading Barcode/QR code using mobile Vision API in Android

Reading Barcode/QR code using mobile Vision API in Android

    Reading barcode, QR code is a popular topic in mobile application development. The fact that barcodes and QR codes have become ubiquitous in recent years, it requires programmers to create applications for reading them by any smartphone with a decent camera.
    As you followed my blog, I also had 2 posts about this topic:
    Both of 2 posts above is guided about using ZXing library to create a barcode/QR code scanner. But now, 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 barcode, QR code in real time.
    In this tutorial, I am going to help you get started with it.

Project configurations

    After starting a new Android Studio project, adding Google Play Services dependency to dependencies scope of your app-level build.gradle:
compile 'com.google.android.gms:play-services:9.6.1'
    Add this meta-data to <application> tag in your AndroidManifest.xml:
<meta-data
            android:name="com.google.android.gms.vision.DEPENDENCIES"
            android:value="barcode" />

Reading barcode/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 qr_code.png:
    Firstly, you must decode your photo to a Bitmap by using BitmapFactory, this Bitmap is needed to Vison API as input:
Bitmap myQRCode = BitmapFactory.decodeStream(getAssets().open("qr_code.png"));
    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(PhotoActivity.this)
                            .setBarcodeFormats(Barcode.QR_CODE) // set QR code as the format type
                            .build();
    Create a Frame using the Bitmap you created:
Frame frame = 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 barcodes = barcodeDetector.detect(frame);
    Okey, after detecting some Barcode objects, you can get their values by call displayValue field:
                    // Check if at least one barcode was detected
                    if (barcodes.size() != 0) {
                        // Display the QR code's message
                        textView.setText("QR CODE Data: " + barcodes.valueAt(0).displayValue);
                        //Display QR code image to ImageView
                        imageView.setImageBitmap(myQRCode);
                    } else {
                        textView.setText("No QR Code found!");
                        textView.setTextColor(Color.RED);
                    }
    And this is full code for this activity:
PhotoActivity.java
package info.devexchanges.barcodescannermobilevisionapi;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;

public class PhotoActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photo);

        View btnPhotoScan = findViewById(R.id.photo_scan);
        final ImageView imageView = (ImageView) findViewById(R.id.image);
        final TextView textView = (TextView) findViewById(R.id.qr_code_content);
        btnPhotoScan.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View view) {
                try {
                    Bitmap myQRCode = BitmapFactory.decodeStream(getAssets().open("qr_code.png"));
                    BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(PhotoActivity.this)
                            .setBarcodeFormats(Barcode.QR_CODE)
                            .build();

                    Frame frame = new Frame.Builder().setBitmap(myQRCode).build();
                    SparseArray barcodes = barcodeDetector.detect(frame);

                    // Check if at least one barcode was detected
                    if (barcodes.size() != 0) {
                        // Display the QR code's message
                        textView.setText("QR CODE Data: " + barcodes.valueAt(0).displayValue);
                        //Display QR code image to ImageView
                        imageView.setImageBitmap(myQRCode);
                    } else {
                        textView.setText("No QR Code found!");
                        textView.setTextColor(Color.RED);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
    Running this Activity, you can get this output:

Reading a barcode 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.
    Firstly, you must request CAMERA permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
    Creating a layout file (xml) for this Activity. I will use a SurfaceView to display the preview frames captured by the camera and a TextView to display the content barcode value:
activity_main.xml
<?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:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true" />

    <TextView
        android:id="@+id/barcode_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:text="No Barcode"
        android:textColor="@android:color/white"
        android:textSize="20sp" />

</RelativeLayout>
    In the activity Java code, to "stream" the camera preview scene to the SurfaceView, we'll use an instance of CameraSource, initializing it with a BarcodeDetector object:
barcodeDetector = new BarcodeDetector.Builder(this)
                .setBarcodeFormats(Barcode.ALL_FORMATS)
                .build();

cameraSource = new CameraSource.Builder(this, barcodeDetector)
                .setRequestedPreviewSize(1600, 1024)
                .setAutoFocusEnabled(true) //you should add this feature
                .build();
    As noted in code, you should use setAutoFocusEnabled(true) when creating the CameraSource instance, your "camera preview" will be auto focused, not be blurry!
    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. Inside the surfaceCreated() method, call the start() method of the CameraSource to start drawing the preview frames:
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                try {
                    //noinspection MissingPermission
                    cameraSource.start(cameraView.getHolder());
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                cameraSource.stop();
            }
        });
The remaining work is displaying detected barcode content to TextView. We will use setProcessor() method of BarcodeDetector with the parameter is Detector.Processor:
barcodeDetector.setProcessor(new Detector.Processor() {
            @Override
            public void release() {
            }

            @Override
            public void receiveDetections(Detector.Detections detections) {
                final SparseArray barcodes = detections.getDetectedItems();
                if (barcodes.size() != 0) {
                    barcodeValue.post(new Runnable() {
                        @Override
                        public void run() {
                            //Update barcode value to TextView
                            barcodeValue.setText(barcodes.valueAt(0).displayValue);
                        }
                    });
                }
            }
        });
    Moreover, you should override onDestroy() method of your Activity to release the CameraSource to stop drawing the preview frame. Finally, this is full code:
MainActivity.java
package info.devexchanges.barcodescannermobilevisionapi;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;

import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;

import java.io.IOException;

public class MainActivity extends AppCompatActivity {
    private BarcodeDetector barcodeDetector;
    private CameraSource cameraSource;
    private SurfaceView cameraView;
    private TextView barcodeValue;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.activity_main);

        cameraView = (SurfaceView) findViewById(R.id.surface_view);
        barcodeValue = (TextView) findViewById(R.id.barcode_value);

        barcodeDetector = new BarcodeDetector.Builder(this)
                .setBarcodeFormats(Barcode.ALL_FORMATS)
                .build();

        cameraSource = new CameraSource.Builder(this, barcodeDetector)
                .setRequestedPreviewSize(1600, 1024)
                .setAutoFocusEnabled(true) //you should add this feature
                .build();

        cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                try {
                    //noinspection MissingPermission
                    cameraSource.start(cameraView.getHolder());
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                cameraSource.stop();
            }
        });

        barcodeDetector.setProcessor(new Detector.Processor() {
            @Override
            public void release() {
            }

            @Override
            public void receiveDetections(Detector.Detections detections) {
                final SparseArray barcodes = detections.getDetectedItems();
                if (barcodes.size() != 0) {
                    barcodeValue.post(new Runnable() {
                        @Override
                        public void run() {
                            //Update barcode value to TextView
                            barcodeValue.setText(barcodes.valueAt(0).displayValue);
                        }
                    });
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        cameraSource.release();
        barcodeDetector.release();
    }
}
    Running this activity and scanning a barcode, you may get result like this:

Conclusions

    In this tutorial, you learned how to use the mobile vision API to read barcode and QR codes from static images as well as from live camera streams. To learn more about the mobile vision API, I recommend visiting the API’s documentation.
    These are posts about Mobile Vision API on my blog:

Developing a "native" Barcode Scanner in Android

Developing a "native" Barcode Scanner in Android

    As you can read at my previous, I had presented a simple Barcode reader application. It's principle is user must download and install Zxing application from Google Play first, my app will call Zxing scanner screen as a sub-Activity and get the result (Barcode or QR code information) from it after scanning process finished!

    The fact that we always would like to developing an application which be able to "embedding" this scanner in it, not warning that our device has not installed Barcode Reader application. Fortunately, there is a library developed by Dushyanth Maguluru which based on ZXing and ZBar, which be able to Barcode Scanner views, help us to develop a simple application which can scan Barcode/QR code itself.

Adding Barcode reader library to Android Studio Project

    By reading it's source code on Github, we can find out that the author has combined ZXing and ZBar, each library to a dependency module in this project! In this tutorial, I will use Zxing, ZBar guide is absolutely similar!
    In order to use this library, the simplest way is adding it's dependency to your application level build.gradle:
dependencies {
    compile 'me.dm7.barcodescanner:zxing:1.9' //barcode reader dependency
    compile 'com.android.support:appcompat-v7:24.1.1'
}

Project main activity

    This main activity has 2 important works:
  • Start the scanning barcode activity as a sub-Activity by Intent.
  • Retrieve scanning results: barcode format and content by overriding onActivityResult().
    It's source code simple like this:
MainActivity.java
package info.devexchanges.barcodescanner;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private final static int REQUEST_SCANNER = 1;
    public final static String FORMAT = "format";
    public final static String CONTENT = "content";

    private TextView content;
    private TextView format;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        View btnScan = findViewById(R.id.btn_scan);
        format = (TextView) findViewById(R.id.format);
        content = (TextView) findViewById(R.id.content);

        setSupportActionBar(toolbar);

        btnScan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, ScannerActivity.class);
                startActivityForResult(intent, REQUEST_SCANNER);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == Activity.RESULT_OK) {
            content.setText(data.getStringExtra(CONTENT));
            format.setText(data.getStringExtra(FORMAT));
        }
    }
}
    And it's layout:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="info.devexchanges.barcodescanner.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_gravity="top"
        android:minHeight="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/btn_scan"
        android:text="Scan Barcode"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/format"
        android:gravity="center"
        android:padding="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/content"
        android:gravity="center"
        android:padding="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Scanning Barcode/QR code Activity

    As you can see at the MainActivity code above, when Button pressed, ScannerActivity will be launch as a sub-Activity.
    The Barcode scanner view is initialized by a ZXingScannerView object. In onCreate(), declaring it like this:
ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);

scannerView = new ZXingScannerView(this);
contentFrame.addView(scannerView);
    The most important work is retrieving scanning result, so your ScannerActivity must implements ResultHandler interface and override handleResult(Result result) method. Moreover, start scanning by invoke this code:
scannerView.startCamera();
    And if you want to stop this scanning, call closing camera method:
scannerView.stopCamera();
    This is full code for this activity:
ScannerActivity.java
package info.devexchanges.barcodescanner;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.ViewGroup;

import com.google.zxing.Result;

import me.dm7.barcodescanner.zxing.ZXingScannerView;

public class ScannerActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {

    private ZXingScannerView scannerView;

    @Override
    public void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_scanner);
        ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);

        scannerView = new ZXingScannerView(this);
        contentFrame.addView(scannerView);
    }

    @Override
    public void onResume() {
        super.onResume();
        scannerView.setResultHandler(this);
        scannerView.startCamera();
    }

    @Override
    public void onPause() {
        super.onPause();
        scannerView.stopCamera();
    }

    @Override
    public void handleResult(Result rawResult) {
        //Call back data to main activity
        Intent intent = new Intent();
        intent.putExtra(MainActivity.FORMAT, rawResult.getBarcodeFormat().toString());
        intent.putExtra(MainActivity.CONTENT, rawResult.getText());

        setResult(Activity.RESULT_OK, intent);
        finish();
    }
}
    And it's layout:
activity_scanner.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</FrameLayout>
    Running this application, we'll have this screen:

    Clicking button, we'll start scanning:
Scanning Barcode
    After scanning, we have this result:
    An example after scanning a QR code:

Conclusions

    Now, the simple "native" scanning Barcode/QR code app has been done! In this tutorial, we've run through the process of facilitating barcode/QR code scanning within Android app using the a third-party library based on ZXing. In your own apps, you might want to carry out further processing on the retrieved scan results, such as loading URLs or looking the data up in a third party data source. Moreover, by reading this library ZBar guide, you also can find out the way to use this module to your project - absolutely analogous!

Simple Scanning Barcode/QR code by Android Phone

Simple Scanning Barcode/QR code by Android Phone

    Every Android mobile device, has the ability to read QR codes as well as scanning barcodes by using it's own Camera. In this example, we are going to learn how the Android Barcode/Qr Code Scanner is implemented via the use of the ZXing library, which will help us to carry out barcode scanning within an application. We will call on the resources in this open apk library within our app, retrieving and processing the returned results.
    First, design a layout for activity:
activity_scan.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ScanActivity">

    <Button
        android:id="@+id/btn_scan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_margin="10dp"
        android:text="@string/scan" />

    <TextView
        android:id="@+id/format"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_scan"
        android:gravity="center"
        android:textColor="@android:color/holo_blue_dark"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/format"
        android:gravity="center"
        android:textColor="@android:color/holo_green_dark"
        android:textSize="20sp" />

</RelativeLayout>
    After click "Scan Barcode or QR code" button, our app will invoke Scan Activity of Barcode Scanner application (if you haven't install it, app will open GooglePlay to get it). Code for button event:
private View.OnClickListener onClickListener() {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Intent intent = new Intent("com.google.zxing.client.android.SCAN");
                    startActivityForResult(intent, 0);
                } catch (ActivityNotFoundException ex) {
                    ex.printStackTrace();

                    //if you haven't install barcodeScanner app, download it from Google Play
                    downloadScanBarcode();
                }

            }
        };
    }
    After complete scanning code, retrieving back data in onActivityResult() method:
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 0) {
            if (resultCode == RESULT_OK) {
                format.setText(data.getStringExtra("SCAN_RESULT_FORMAT"));
                content.setText(data.getStringExtra("SCAN_RESULT"));
            } else if (resultCode == RESULT_CANCELED) {
                format.setText("Press a button to start a scan.");
                content.setText("Scan cancelled.");
            }
        }
    }
    And this is method which open GooglePlay to download Barcode Scanner app:
    /**
     * Go to GooglePlay Store and down load "ScanBarCode" app
     */
    private void downloadScanBarcode() {
        Uri uri = Uri.parse("market://search?q=pname:" + "com.google.zxing.client.android");
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        try {
            startActivity(intent);
        } catch (ActivityNotFoundException ex) {
            ex.printStackTrace();
        }
    }
    Full ScanActivity.java code:
package com.blogspot.hongthaiit.barcode;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class ScanActivity extends Activity {

    private View btnScan;
    private TextView content;
    private TextView format;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan);

        btnScan = (View) findViewById(R.id.btn_scan);
        content = (TextView) findViewById(R.id.content);
        format = (TextView) findViewById(R.id.format);

        btnScan.setOnClickListener(onClickListener());
    }

    private View.OnClickListener onClickListener() {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Intent intent = new Intent("com.google.zxing.client.android.SCAN");
                    startActivityForResult(intent, 0);
                } catch (ActivityNotFoundException ex) {
                    ex.printStackTrace();

                    //if you haven't install barcodeScanner app, download it from Google Play
                    downloadScanBarcode();
                }

            }
        };
    }

    /**
     * Go to GooglePlay Store and down load "ScanBarCode" app
     */
    private void downloadScanBarcode() {
        Uri uri = Uri.parse("market://search?q=pname:" + "com.google.zxing.client.android");
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        try {
            startActivity(intent);
        } catch (ActivityNotFoundException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 0) {
            if (resultCode == RESULT_OK) {
                format.setText(data.getStringExtra("SCAN_RESULT_FORMAT"));
                content.setText(data.getStringExtra("SCAN_RESULT"));
            } else if (resultCode == RESULT_CANCELED) {
                format.setText("Press a button to start a scan.");
                content.setText("Scan cancelled.");
            }
        }
    }
}
    Some screenshots after running (click for full size):
pic name pic name pic name pic name

    References:


(sorry for having ads)