Learn how to create a very simple application that reads QR Codes using the Google's Mobile Vision API.

How to create a QR Code reader in Android using the QReader library

Surely there are a lot of Java libraries that allow you to scan QR Codes, however not all of them are compatible with Android or at least they aren't so easy to implement. QReader is one of those libraries that allows you to implement easily a QRCode scanner in your android application without headaches.

In this article, we will show you how to install and use the QReader library to read QR Codes in your Android application.

1. Add and install QReader as a dependency on gradle

As first step, to get started with the QReader library, you will need to install it. This can be done easily through the gradle management. If you are using Gradle > 4.0 then add the dependency with:

dependencies {
    implementation 'com.github.nisrulz:qreader:2.1.1'
}

If you are using Gradle < 4.0 then add it like this:

dependencies {
    compile 'com.github.nisrulz:qreader:2.1.1'
}

Till the date of this article, the latest available version might be higher, so be sure to check the latest version available on the releases page of the repository. After adding the dependency synchronize the project so the library will be installed. For more information about this library, please visit the official repository at Github here.

2. Register helper permission class

Create a new java class, namely RuntimePermissionUtil.java in your project. This file will contain the following code:

// \app\src\main\java\com\yourcompany\yourapp\RuntimePermissionUtil.java
package com.yourcompany.yourapp;

/*
 * Copyright (C) 2016 Nishant Srivastava
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;

public class RuntimePermissionUtil {

    private RuntimePermissionUtil() {

    }

    public static void onRequestPermissionsResult(int[] grantResults,
                                                  RPResultListener rpResultListener) {
        if (grantResults.length > 0) {
            for (int grantResult : grantResults) {
                if (grantResult == PackageManager.PERMISSION_GRANTED) {
                    rpResultListener.onPermissionGranted();
                } else {
                    rpResultListener.onPermissionDenied();
                }
            }
        }
    }

    public static void requestPermission(final Activity activity, final String[] permissions,
                                         final int REQUEST_CODE) {
        // No explanation needed, we can request the permission.
        ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE);
    }

    public static void requestPermission(final Activity activity, final String permission,
                                         final int REQUEST_CODE) {
        // No explanation needed, we can request the permission.
        ActivityCompat.requestPermissions(activity, new String[] { permission }, REQUEST_CODE);
    }

    public static boolean checkPermissonGranted(Context context, String permission) {
        return (
            ActivityCompat.checkSelfPermission(context, permission)
            ==
            PackageManager.PERMISSION_GRANTED
        );
    }
}

This class will be used later in our implementation code to handle the camera permissions dynamically just as the new Android platforms need. You will need to create an interface file as well with the following content:

// \app\src\main\java\com\yourcompany\yourapp\RPResultListener.java
package com.yourcompany.yourapp;

interface RPResultListener {
    void onPermissionGranted();

    void onPermissionDenied();
}

Following this step, you will have 2 extra java files that we'll use in the implementation of the next steps.

3. Create layout

The layout needs to be pretty independent according to your needs, however we will use a very basic approach that will contain 2 buttons, the one that handles the status of the reader (enabled/disabled) and the button that restarts the current activity. We will use a linear layout with the mentioned elements and we'll place them in the main activity. As our app only contains this, you may create another layout for your activity in case that you want to create it in another one:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical"
        >
        <TextView
            android:id="@+id/code_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp"
            android:textColor="@android:color/black"
            android:textSize="20sp"
        />
        <Button
            android:id="@+id/btn_start_stop"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Start/Stop reader"
        />

        <Button
            android:id="@+id/btn_restart_activity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="Restart activity"
        />

        <SurfaceView
            android:id="@+id/camera_view"
            android:layout_width="match_parent"
            android:layout_height="400dp"
            android:layout_marginTop="20dp"
            android:layout_above="@+id/info"
        />
    </LinearLayout>

</android.support.constraint.ConstraintLayout>

This layout will have the following preview:

Android Layout Preview

4. Implementation on activity

Finally, we need to write the code that will handle the logic in your activity. As we mentioned previously, we are working on a empty project that will have a single activity, so we will handle the code on the main activity of our application, but you can implement the code on another activity if you wish. As first we will need to include the namespaces of the classes that we will use. Create as well the class-level variables that will contain for example the reader instance, the surface view where the camera image will be displayed.

The logic is simply based on requesting the camera permission as first, if its granted, you should start the reader with the method setupQREader that initializes a basic instance of the QReader. When the reader detects and interprets a QRCode, the content will be displayed in the information label of our layout:

// \app\src\main\java\com\yourcompany\yourapp\MainActivity.java
package com.yourcompany.yourapp;

import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import android.widget.Button;
import android.view.View;
import android.view.SurfaceView;
import github.nisrulz.qreader.QRDataListener;
import github.nisrulz.qreader.QREader;

public class MainActivity extends AppCompatActivity {

    // User Interface
    private TextView text;

    // QREader
    private SurfaceView mySurfaceView;
    private QREader qrEader;
    private boolean hasCameraPermission = false;
    private static final String cameraPerm = Manifest.permission.CAMERA;

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

        // 1. When the app starts, request Camera permission
        hasCameraPermission = RuntimePermissionUtil.checkPermissonGranted(this, cameraPerm);

        text = findViewById(R.id.code_info);


        // 2. Handle action button text. With this button you will start/stop the reader
        final Button stateBtn = findViewById(R.id.btn_start_stop);
        // change of reader state in dynamic
        stateBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (qrEader.isCameraRunning()) {
                    stateBtn.setText("Start QREader");
                    qrEader.stop();
                } else {
                    stateBtn.setText("Stop QREader");
                    qrEader.start();
                }
            }
        });

        stateBtn.setVisibility(View.VISIBLE);

        Button restartButton = findViewById(R.id.btn_restart_activity);
        restartButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                restartActivity();
            }
        });

        // Setup SurfaceView
        // -----------------
        mySurfaceView = findViewById(R.id.camera_view);

        if (hasCameraPermission) {
            // Setup QREader
            setupQREader();
        } else {
            RuntimePermissionUtil.requestPermission(
                MainActivity.this,
                cameraPerm,
                100
            );
        }
    }

    void restartActivity() {
        startActivity(new Intent(MainActivity.this, MainActivity.class));
        finish();
    }

    void setupQREader() {
        // Init QREader
        // ------------
        qrEader = new QREader.Builder(this, mySurfaceView, new QRDataListener() {
            @Override
            public void onDetected(final String data) {
                Log.d("QREader", "Value : " + data);
                text.post(new Runnable() {
                    @Override
                    public void run() {
                        text.setText(data);
                    }
                });
            }
        }).facing(QREader.BACK_CAM)
            .enableAutofocus(true)
            .height(mySurfaceView.getHeight())
            .width(mySurfaceView.getWidth())
            .build();
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (hasCameraPermission) {

            // Cleanup in onPause()
            // --------------------
            qrEader.releaseAndCleanup();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (hasCameraPermission) {

            // Init and Start with SurfaceView
            // -------------------------------
            qrEader.initAndStart(mySurfaceView);
        }
    }

    // Method that handles the result of the permission request made at the beginning of the application
    @Override
    public void onRequestPermissionsResult(
        int requestCode,
        @NonNull final String[] permissions,
        @NonNull final int[] grantResults
    ) {
        if (requestCode == 100) {
            RuntimePermissionUtil.onRequestPermissionsResult(grantResults, new RPResultListener() {
                @Override
                public void onPermissionGranted() {
                    if ( RuntimePermissionUtil.checkPermissonGranted(MainActivity.this, cameraPerm)) {
                        restartActivity();
                    }
                }

                @Override
                public void onPermissionDenied() {
                    // Do nothing
                }
            });
        }
    }
}

Happy coding !


Senior Software Engineer at Software Medico. Interested in programming since he was 14 years old, Carlos is a self-taught programmer and founder and author of most of the articles at Our Code World.

Sponsors