Android Example - Programmatically Scan QR Code and Bar Code - Truiton
Skip to content

Android Example – Programmatically Scan QR Code and Bar Code

Android Scan QR Code Example

Often when building Android apps, we encounter situations where it is required to scan a bar code or QR code. Now as difficult as it sounds, the complex code it has if you plan to write your own scanning algorithm. But this is the place where new and improved Mobile Vision from Google steps in. This API was initially introduced in August 2015, but now it as been further improved and re-introduced including features like OCR and object tracking along with the existing features of scanning qr code and barcodes. Therefore here lets understand the usage of Google’s Mobile Vision with an example where we scan a QR code on Android.

Programmatically scanning a QR Code or Bar Code on Android has always been a topic of great discussion. There are many ways through which this can be done:

  • Using a web based API solution, where image is uploaded to a server and it returns back the results.
  • Use a hybrid solution where web based app accesses your camera and scans Bar Code or QR Code.
  • Write your own image scanning algorithm.
  • Use the Google Play Services integrated Mobile Vision.

In my opinion using the Android Mobile Vision for scanning QR codes is the most favorable option in comparison to other options because it offers offline scanning of images, resulting a fast scan and an immediate result. This being said its also the easiest option available. As it just requires a few lines of code to make it work. Besides that; when using this library, you don’t need to import any other android scanning library as this API also takes care of OCR and facial scanning. To read more about it please visit my article on Android Mobile Vision API (coming soon).

Introducing the Official Android QR and Barcode Scanning Library : Mobile Vision

There are many third party libraries available for parsing of barcodes and QR codes, some of them are even endorsed by Google I believe. But nowdays reading of QR codes and barcodes on Android has become so common that, it demanded an official solution. Hence the Mobile Vision APIs were introduced with QR code and barcode scanning capabilities for Android. While most of the opensource libraries scan only one QR code or barcode at a time. These Mobile Vision APIs can scan multiple QR codes at the same time. Also if you want, you can directly integrate them with android’s camera and scan for barcodes and QR codes on the fly while streaming on a surface.

As of now Mobile Vision APIs can scan following types of Bar codes and QR codes on Android:

 

Setting up Android QR Code Scan Library : Mobile Vision

As I have mentioned before, setting up Mobile Vision API is very easy, all of its downloads are managed internally by Google Play services. Although to properly integrate it and make it functional before the actual usage, it is advised to reference the used features of Mobile Vision API in the Manifest of your app. This way the dependencies are downloaded before you can actually use them i.e. at the time of installation. Also please don’t forget to follow the steps for Google Play Services Integration. But in our case we only need to compile com.google.android.gms:play-services-vision, therefore make sure your build.gradle (app) has following dependencies:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:26.1.0'
    compile 'com.google.android.gms:play-services-vision:11.4.0'
}

And build.gradle(global) should have this:

allprojects {
    repositories {
        jcenter()
        maven {
            url "https://maven.google.com"
        }
    }
}

Also as mentioned to make Mobile Vision APIs functional for an Android Scan QR Code Example before installation, please include these lines in your Manifest inside the application tag:

<meta-data
            android:name="com.google.android.gms.vision.DEPENDENCIES"
            android:value="barcode"/>

Here the android:value tag can have following comma separated values:

  • barcode
  • face
  • ocr

Android Scan QR Code Example

One of the interesting features of Mobile Vision API is that it performs a scan very fast. Also its documentation states that you can perform multiple types of scans on the same frame. This means you can scan multiple barcodes, QR Codes and faces together. Be it a video, a picture, or a video stream. But covering all of this in a single article is not possible. Therefore to start off we will build a basic example where we would use native phone’s camera to take a picture and scan it for barcodes and qr codes, and building a basic Android Scan QR code Example.

To start off, have a look at the Manifest, where we define the camera and barcode dependency for this Android Scan QR Code example:

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.truiton.mobile.vision.qrcode"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-feature
        android:name="android.hardware.camera"
        android:required="true"/>
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <meta-data
            android:name="com.google.android.gms.vision.DEPENDENCIES"
            android:value="barcode"/>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>

</manifest>

Please Note: full source code is available at the end of the tutorial.

Next lets define a basic layout to show the QR code scan results.

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.truiton.mobile.vision.qrcode.MainActivity">

    <ImageView
        android:id="@+id/image"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:src="@mipmap/truiton"/>

    <TextView
        android:id="@+id/scan_header"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/image"
        android:layout_centerHorizontal="true"
        android:text="Scan Results:"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/scan_results"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/scan_header"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="20dp"
        android:text="Take Picture"/>

</RelativeLayout>

Now lets define the main activity where the Android QR/Bar code scanning takes place:

package com.truiton.mobile.vision.qrcode;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

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

import java.io.File;
import java.io.FileNotFoundException;

public class MainActivity extends AppCompatActivity {
    private static final String LOG_TAG = "Barcode Scanner API";
    private static final int PHOTO_REQUEST = 10;
    private TextView scanResults;
    private BarcodeDetector detector;
    private Uri imageUri;
    private static final int REQUEST_WRITE_PERMISSION = 20;
    private static final String SAVED_INSTANCE_URI = "uri";
    private static final String SAVED_INSTANCE_RESULT = "result";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        scanResults = (TextView) findViewById(R.id.scan_results);
        if (savedInstanceState != null) {
            imageUri = Uri.parse(savedInstanceState.getString(SAVED_INSTANCE_URI));
            scanResults.setText(savedInstanceState.getString(SAVED_INSTANCE_RESULT));
        }
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ActivityCompat.requestPermissions(MainActivity.this, new
                        String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION);
            }
        });

        detector = new BarcodeDetector.Builder(getApplicationContext())
                .setBarcodeFormats(Barcode.DATA_MATRIX | Barcode.QR_CODE)
                .build();
        if (!detector.isOperational()) {
            scanResults.setText("Could not set up the detector!");
            return;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_PERMISSION:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    takePicture();
                } else {
                    Toast.makeText(MainActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
                }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == PHOTO_REQUEST && resultCode == RESULT_OK) {
            launchMediaScanIntent();
            try {
                Bitmap bitmap = decodeBitmapUri(this, imageUri);
                if (detector.isOperational() && bitmap != null) {
                    Frame frame = new Frame.Builder().setBitmap(bitmap).build();
                    SparseArray<Barcode> barcodes = detector.detect(frame);
                    for (int index = 0; index < barcodes.size(); index++) {
                        Barcode code = barcodes.valueAt(index);
                        scanResults.setText(scanResults.getText() + code.displayValue + "\n");

                        //Required only if you need to extract the type of barcode
                        int type = barcodes.valueAt(index).valueFormat;
                        switch (type) {
                            case Barcode.CONTACT_INFO:
                                Log.i(LOG_TAG, code.contactInfo.title);
                                break;
                            case Barcode.EMAIL:
                                Log.i(LOG_TAG, code.email.address);
                                break;
                            case Barcode.ISBN:
                                Log.i(LOG_TAG, code.rawValue);
                                break;
                            case Barcode.PHONE:
                                Log.i(LOG_TAG, code.phone.number);
                                break;
                            case Barcode.PRODUCT:
                                Log.i(LOG_TAG, code.rawValue);
                                break;
                            case Barcode.SMS:
                                Log.i(LOG_TAG, code.sms.message);
                                break;
                            case Barcode.TEXT:
                                Log.i(LOG_TAG, code.rawValue);
                                break;
                            case Barcode.URL:
                                Log.i(LOG_TAG, "url: " + code.url.url);
                                break;
                            case Barcode.WIFI:
                                Log.i(LOG_TAG, code.wifi.ssid);
                                break;
                            case Barcode.GEO:
                                Log.i(LOG_TAG, code.geoPoint.lat + ":" + code.geoPoint.lng);
                                break;
                            case Barcode.CALENDAR_EVENT:
                                Log.i(LOG_TAG, code.calendarEvent.description);
                                break;
                            case Barcode.DRIVER_LICENSE:
                                Log.i(LOG_TAG, code.driverLicense.licenseNumber);
                                break;
                            default:
                                Log.i(LOG_TAG, code.rawValue);
                                break;
                        }
                    }
                    if (barcodes.size() == 0) {
                        scanResults.setText("Scan Failed: Found nothing to scan");
                    }
                } else {
                    scanResults.setText("Could not set up the detector!");
                }
            } catch (Exception e) {
                Toast.makeText(this, "Failed to load Image", Toast.LENGTH_SHORT)
                        .show();
                Log.e(LOG_TAG, e.toString());
            }
        }
    }

    private void takePicture() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File photo = new File(Environment.getExternalStorageDirectory(), "picture.jpg");
        imageUri = FileProvider.getUriForFile(MainActivity.this,
                BuildConfig.APPLICATION_ID + ".provider", photo);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, PHOTO_REQUEST);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        if (imageUri != null) {
            outState.putString(SAVED_INSTANCE_URI, imageUri.toString());
            outState.putString(SAVED_INSTANCE_RESULT, scanResults.getText().toString());
        }
        super.onSaveInstanceState(outState);
    }

    private void launchMediaScanIntent() {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        mediaScanIntent.setData(imageUri);
        this.sendBroadcast(mediaScanIntent);
    }

    private Bitmap decodeBitmapUri(Context ctx, Uri uri) throws FileNotFoundException {
        int targetW = 600;
        int targetH = 600;
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(ctx.getContentResolver().openInputStream(uri), null, bmOptions);
        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;

        int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;

        return BitmapFactory.decodeStream(ctx.getContentResolver()
                .openInputStream(uri), null, bmOptions);
    }
}

Please Note: Images to be scanned, should not be too big.

When it comes to scanning of QR code from a picture on an Android app using the Mobile Vision APIs, the process is quite simple. All we need to do is, pass on a Frame to the BarcodeDetector, this will return an array of type Barcode, which would contain the actual barcode value. But there are prerequisites to this implementation, like declaring all permissions and getting them on run time, which is shown in the android scan qr code example above. To view full source code for this example please refer to the link below:

Full Source Code

Also please have a look at the screens for this Android Scan QR code scan example:

By using these Mobile Vision APIs for barcodes and QR codes, we can also determine the type of barcode or QR code is being scanned. This is one of the most interesting parts about this API, it reads the data, as well as it understands what type of data it is. For e.g. when an event QR code is scanned, we can use Barcode.CalendarEvent class to extract out the exact data in required format. This can help us in reducing our code bases very efficiently.

When speaking of Android Barcode scanning libraries, Mobile Vision API is the latest addition in the list. Also it is one of the most stable and reliable options as its a library provided by Google, through play services. Which definitely gives it an added advantage over other libraries. That being said, would like to say, we have finally succeeded in offline scanning of barcodes and QR Codes. As most of the methods require uploading of images to a server, or including huge code bases or libraries into our project for offline scanning. But with Mobile Vision APIs nothing of this sort needs to be done, as all this is taken care by Google Play Services once they are downloaded. Hope this helped you. Connect with us on Twitter, Facebook and Google+ for more updates.

8 thoughts on “Android Example – Programmatically Scan QR Code and Bar Code”

  1. so I have followed your tutorial to add a barcode scanner to my app and have a issue I can not figure out. Could you please help?
    In the xml file..

    android:src=”@mipmap/truiton” is not found, so I set it to a image I did have in my project, I am able to take a picture but it never recognizes a barcode …..should not the ImageView src be a live preview of the camera? I do not know how to do that am I’m stuck…

    1. Hi, You can safely remove the whole ImageView, as the picture would be taken from your device’s camera.

      Also if it is not recognizing your barcode, then please ensure that your barcode falls under the specified list of supported barcode and QR code types.

      1. Hello. It doesn’t recognizing my barcode, I have tried multiple QR codes which should fall under the specified list. I am using play-service 10.2 now. Thank you.

  2. This actually dont work, i try this and the Ocr and it always say could not setup the detector, i try with 3 different phones and is always the same!! :/

Leave a Reply

Your email address will not be published. Required fields are marked *