How to create a QR code reader on Android with Android Studio

- Andrés Cruz

En español
How to create a QR code reader on Android with Android Studio

Download

In this post we will see how to create a QR code reader on Android in our applications; To get a clearer idea of how a QR reader works, there are currently several QR readers on Android that you can find on Google Play; to name a few:

Developing a QR reader in our Android application with the Play Service library

As you well know, Google offers a significant amount of functionality that translates into several APIs developed by Google itself that it makes available to us developers through its libraries as well as Play Services as well as supports among many more; To do the most common tasks, such as creating an application to read a QR code, it is not necessary to use a third-party library or package; the one provided by Google that does all the work for us is enough for us.

Creating a custom QR reader (created by ourselves using Android Studio as a software development environment or IDE) is quite simple with Android and we only have to include an external library which corresponds to Google Play services: com.google.android .gms:play-services-vision, but before that let's see what it is, how a QR code works and how we can use it in our Android applications and webs:

What is a QR code?

Before going into the topic of how to develop a QR reader using a native library provided by the people of Google, it is important to know what a QR code reader is and how its structure works, which is what we will explain below.

A QR code is nothing more than a system that allows information to be stored in a kind of next-generation barcode

A QR code is a type of barcode but in two dimensions in which information is encoded in a box like the following

Código qr

QR codes are really useful to manage inventories, share data and information in an easy and very professional way; QR codes are currently a fundamental element in any small or large application due to their versatility when using them.

What is a QR Code used for? and how we can exploit them in our Android apps

As previously mentioned, QR codes are widely used due to their versatility and great utility; They are used in all kinds of products, such as food, up to devices, automobiles, magazines and many other types of advertising, marketing, etc... and it is because with an Android app we can scan any type of QR code.

...

Although our case of interest is to show the process of creating an Android application to use it as a scanner in an Android app...

...Where we can process medicines and indicate to the client if the medicine is useful for them, having their profile previously loaded and without the need for the client to provide any information or data manually.

How to generate a QR code?

On the Internet you will find an immense number of ways to generate QR codes; We can even create QR codes with PHP, Java, etc. libraries; and there are websites that are responsible for generating QR codes such as the following Generate your free QR code or a personalized one using a programming language as we saw previously.

QR Scanner: How to crack a QR code?

With the help of a mobile we can recover this information just by pointing the camera towards the QR code and that is the idea and what we will do in the next section of this post where we will detail how to create a QR reader with Android.

Developing our own QR code reader on Android

Finally we come to the area of interest in what is to create our QR code reader on Android; first we need to add the necessary dependency in our build.gradle.

Adding the dependency in the build.gradle file in Android Studio

We open our build.gradle file and add the following dependency:

implementation 'com.google.android.gms:play-services:15.0.2'

The previous dependency gives us access not only to the vision API but also to the entire platform that Google Play offers us, as in our case of interest it is only reading a QR code, we can use the following dependency in our build.gradle:

implementation 'com.google.android.gms:play-services-vision:15.0.2'

You should keep in mind that the versions change from one day to the next; You can find the latest version in the official documentation at the following link:

Set Up Google Play Services

Para que el paquete actualice automáticamente podríamos hacer algo así implementation 'com.google.android.gms:play-services-vision:15.+'

Configuring the rest of the application for our QR reader

The manifest file to handle permissions

In our manifest we place the use of the camera:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.desarrollolibre.qr.qr">

    <uses-permission android:name="android.permission.CAMERA" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

The view of our QR code reader

With our dependency already configured we can now go to our activity and/or fragment and create the objects, events and configurations necessary to have our own QR reader; but first we must configure the layout of the activity or fragment:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="center_horizontal"
        android:padding="8dp">

        <SurfaceView
            android:id="@+id/camera_view"
            android:layout_width="640px"
            android:layout_height="480px"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true" />
    </LinearLayout>
</LinearLayout>

Setting up the code reader and camera in our activity

And in our activity or fragment we must create an object of the BarcodeDetector type. This class allows us to recognize barcodes and QR codes -which is our case of interest- through the device's camera and return an object of the SparseArray type with the decoded data. from the analyzed QR in addition to all this, we create the source of the camera and the associated resolution:

        // creo el detector qr
        barcodeDetector =
                new BarcodeDetector.Builder(getContext())
                        .setBarcodeFormats(Barcode.QR_CODE)
                        .build();

        // creo la camara fuente
        cameraSource = new CameraSource
                .Builder(getContext(), barcodeDetector)
                .setRequestedPreviewSize(640, 480)
                .build();

The CameraSource object allows obtaining frames from the device's camera and later analyzing it.

We specify the SurfaceView object of our layout; This object is responsible for mirroring or reflecting what we are seeing through the device's camera on our surface of our layout:

cameraView = (SurfaceView) v.findViewById(R.id.camera_view);

Now we specify the listeners that allow us to control the life cycle of the camera:

        // listener de ciclo de vida de la camara
        cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {

                // verifico si el usuario dio los permisos para la camara
                if (ContextCompat.checkSelfPermission(getContext(), android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                    try {
                        cameraSource.start(cameraView.getHolder());
                    } catch (IOException ie) {
                        Log.e("CAMERA SOURCE", ie.getMessage());
                    }
                } else {
                    Toast.makeText(getContext(), getResources().getString(R.string.error_camara), Toast.LENGTH_SHORT).show();
                }
            }

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

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

An important point of the above code is to start the camera once our surface is drawn with cameraSource.start(cameraView.getHolder());.

Finally, the event is prepared that will return the result provided by the detection and processing of the QR; when there is a response, barcodes.size() will return a value greater than zero:

 // preparo el detector de QR
        barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
            @Override
            public void release() {
            }


            @Override
            public void receiveDetections(Detector.Detections<Barcode> detections) {
                final SparseArray<Barcode> barcodes = detections.getDetectedItems();

                if (barcodes.size() != 0) {
barcodes.valueAt(0).displayValue.toString();
                 // hacer algo
            }

barcodeDetector.release();
        });

With the barcodes.valueAt(0).displayValue.toString(); we obtain the data returned by the QR that can be a text, integer or a URL depending on how we configure our QR code.

Permissions on Android 6 and above

Starting with Android 6, Google created a new permission scheme which is requested from users when they need to use a function of said permission(s)...

...so it is not enough to just include the dependency in our AndroidManifest, we must also request the permission itself from the java code of our Android application.

So, when building our surface we put the following code:

// listener de ciclo de vida de la camara
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
   @Override
   public void surfaceCreated(SurfaceHolder holder) {

       // verifico si el usuario dio los permisos para la camara
       if (ActivityCompat.checkSelfPermission(QRActiviy.this, Manifest.permission.CAMERA)
               != PackageManager.PERMISSION_GRANTED) {

           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               // verificamos la version de Android que sea al menos la M para mostrar
               // el dialog de la solicitud de la camara
               if (shouldShowRequestPermissionRationale(
                       Manifest.permission.CAMERA)) ;
               requestPermissions(new String[]{Manifest.permission.CAMERA},
                       MY_PERMISSIONS_REQUEST_CAMERA);
           }
           return;
       } else {
           try {
               cameraSource.start(cameraView.getHolder());
           } catch (IOException ie) {
               Log.e("CAMERA SOURCE", ie.getMessage());
           }
       }
   }

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

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

The code is relatively simple, we verify if the permission was granted with checkSelfPermission and with shouldShowRequestPermissionRationale we show the dialog to request the permission.

Finally the code is as follows:

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.webkit.URLUtil;

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 CameraSource cameraSource;
    private SurfaceView cameraView;
    private final int MY_PERMISSIONS_REQUEST_CAMERA = 1;
    private String token = "";
    private String tokenanterior = "";

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

        cameraView = (SurfaceView) findViewById(R.id.camera_view);
        initQR();
    }

    public void initQR() {

        // creo el detector qr
        BarcodeDetector barcodeDetector =
                new BarcodeDetector.Builder(this)
                        .setBarcodeFormats(Barcode.ALL_FORMATS)
                        .build();

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

        // listener de ciclo de vida de la camara
        cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {

                // verifico si el usuario dio los permisos para la camara
                if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)
                        != PackageManager.PERMISSION_GRANTED) {

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        // verificamos la version de ANdroid que sea al menos la M para mostrar
                        // el dialog de la solicitud de la camara
                        if (shouldShowRequestPermissionRationale(
                                Manifest.permission.CAMERA)) ;
                        requestPermissions(new String[]{Manifest.permission.CAMERA},
                                MY_PERMISSIONS_REQUEST_CAMERA);
                    }
                    return;
                } else {
                    try {
                        cameraSource.start(cameraView.getHolder());
                    } catch (IOException ie) {
                        Log.e("CAMERA SOURCE", ie.getMessage());
                    }
                }
            }

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

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

        // preparo el detector de QR
        barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
            @Override
            public void release() {
            }


            @Override
            public void receiveDetections(Detector.Detections<Barcode> detections) {
                final SparseArray<Barcode> barcodes = detections.getDetectedItems();

                if (barcodes.size() > 0) {

                    // obtenemos el token
                    token = barcodes.valueAt(0).displayValue.toString();

                    // verificamos que el token anterior no se igual al actual
                    // esto es util para evitar multiples llamadas empleando el mismo token
                    if (!token.equals(tokenanterior)) {

                        // guardamos el ultimo token proceado
                        tokenanterior = token;
                        Log.i("token", token);

                        if (URLUtil.isValidUrl(token)) {
                            // si es una URL valida abre el navegador
                            Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(token));
                            startActivity(browserIntent);
                        } else {
                            // comparte en otras apps
                            Intent shareIntent = new Intent();
                            shareIntent.setAction(Intent.ACTION_SEND);
                            shareIntent.putExtra(Intent.EXTRA_TEXT, token);
                            shareIntent.setType("text/plain");
                            startActivity(shareIntent);
                        }

                        new Thread(new Runnable() {
                            public void run() {
                                try {
                                    synchronized (this) {
                                        wait(5000);
                                        // limpiamos el token
                                        tokenanterior = "";
                                    }
                                } catch (InterruptedException e) {
                                    // TODO Auto-generated catch block
                                    Log.e("Error", "Waiting didnt work!!");
                                    e.printStackTrace();
                                }
                            }
                        }).start();

                    }
                }
            }
        });

    }

}

QR code reader for Kotlin

As you well know if you are a reader of this blog, we have already been taking the first steps with Kotlin for some time and since it could not be missing, below we show all the previous implementation but instead of Java it would be for Kotlin all our code would be as follows:

 private var cameraSource: CameraSource? = NULL
    private var cameraView: SurfaceView? = NULL
    private val MY_PERMISSIONS_REQUEST_CAMERA = 1
    private var token = ""
    private var tokenanterior = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        cameraView = findViewById<View>(R.id.camera_view)
        initQR()
    }

    fun initQR() {

        // creo el detector qr
        val barcodeDetector = BarcodeDetector.Builder(this)
                .setBarcodeFormats(Barcode.ALL_FORMATS)
                .build()

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

        // listener de ciclo de vida de la camara
        cameraView!!.holder.addCallback(object : SurfaceHolder.Callback {
            override fun surfaceCreated(holder: SurfaceHolder) {

                // verifico si el usuario dio los permisos para la camara
                if (ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        // verificamos la version de ANdroid que sea al menos la M para mostrar
                        // el dialog de la solicitud de la camara
                        if (shouldShowRequestPermissionRationale(
                                        Manifest.permission.CAMERA))
                        ;
                        requestPermissions(arrayOf(Manifest.permission.CAMERA),
                                MY_PERMISSIONS_REQUEST_CAMERA)
                    }
                    return
                } else {
                    try {
                        cameraSource!!.start(cameraView!!.holder)
                    } catch (ie: IOException) {
                        Log.e("CAMERA SOURCE", ie.message)
                    }

                }
            }

            override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}

            override fun surfaceDestroyed(holder: SurfaceHolder) {
                cameraSource!!.stop()
            }
        })

        // preparo el detector de QR
        barcodeDetector.setProcessor(object : Detector.Processor<Barcode>() {
            fun release() {}


            fun receiveDetections(detections: Detector.Detections<Barcode>) {
                val barcodes = detections.getDetectedItems()

                if (barcodes.size() > 0) {

                    // obtenemos el token
                    token = barcodes.valueAt(0).displayValue.toString()

                    // verificamos que el token anterior no se igual al actual
                    // esto es util para evitar multiples llamadas empleando el mismo token
                    if (token != tokenanterior) {

                        // guardamos el ultimo token proceado
                        tokenanterior = token
                        Log.i("token", token)

                        if (URLUtil.isValidUrl(token)) {
                            // si es una URL valida abre el navegador
                            val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(token))
                            startActivity(browserIntent)
                        } else {
                            // comparte en otras apps
                            val shareIntent = Intent()
                            shareIntent.action = Intent.ACTION_SEND
                            shareIntent.putExtra(Intent.EXTRA_TEXT, token)
                            shareIntent.type = "text/plain"
                            startActivity(shareIntent)
                        }

                        Thread(object : Runnable {
                            override fun run() {
                                try {
                                    synchronized(this) {
                                        wait(5000)
                                        // limpiamos el token
                                        tokenanterior = ""
                                    }
                                } catch (e: InterruptedException) {
                                    // TODO Auto-generated catch block
                                    Log.e("Error", "Waiting didnt work!!")
                                    e.printStackTrace()
                                }

                            }
                        }).start()

                    }
                }
            }
        })

    }

In the previous code you can notice that when detecting a reading of the QR code by the mobile, a series of steps is carried out that goes from obtaining it through barcodes.valueAt(0).displayValue.toString();, validation ( and release with the Thread) to avoid reading the same QR consecutively (the QR reader always keeps running regardless of whether it has processed a QR or not) and then using URLUtil.isValidUrl(token) we check if the obtained token is a URL, and in that case there will be a default browser on our phone, otherwise we simply show the dialog to share content through the different applications we have on our phone.

When running the application, we will get a screen like the following:

app corriendo con lector qr

It should be remembered that once we get the code we can do whatever we want, generally if we are creating a custom application (See how to create QR codes in CodeIgniter), it is likely that we want to send it to our application to validate it or simply show it to the user through a dialog or open a browser as we specified above.

You can use the following QR that returns the URL of this Blog:

desarrollolibre qr

An important point is that you can use the same functionality to read barcodes or barcodes on Android, the same core of the app is used and in this way you can create a fairly powerful app that serves as a QR code reader or reader using the implementation of the device library and camera.

Download

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.