DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Diving into JNI: My Messy Adventures With C++ in Android
  • Cordova: Communicating Between JavaScript and Java
  • Is Java Still Relevant?
  • Using Barcodes in iText 7

Trending

  • RAG Is Not Enough: Advanced Retrieval Architectures Using Vertex AI Search on GCP
  • Introduction to Tactical DDD With Java: Steps to Build Semantic Code
  • Ingesting Fixed-Width Mainframe Files Into Delta Lake: The Details Nobody Writes Down
  • Rethinking Java CRUDs With Event Sourcing and CQRS Patterns
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. The Quickest Way to Create an Android QR Code Scanner

The Quickest Way to Create an Android QR Code Scanner

Learn the quickest way to create an Android QR code scanner. See a step-by-step implementation camera preview, as well as how to integrate QR code scanning SDK.

By 
Xiao Ling user avatar
Xiao Ling
·
Jan. 08, 22 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
5.1K Views

Join the DZone community and get the full member experience.

Join For Free

A QR code scanner application primarily consists of two parts: camera preview and QR code scanning. There are many Android QR code scanner apps downloadable in the Google Play Store. However, it is more interesting to create a QR code scanner by yourself than to use an existing one. This article aims to reveal the quickest way to create an Android QR code scanner. You will see how to step by step implement camera preview, as well as how to integrate QR code scanning SDK.

Prerequisites

The following Android libraries are required in order to implement the QR code scanner. You can feel free to replace them with your own libraries. 

  • Camera Preview SDK
  • CameraX

Since Android Camera2 API is extremely complicated for beginners, Google released CameraX to simplify camera app development. The codelab tutorial is a good starting point to learn CameraX. 

Installation

In AndroidManifest.xml, add the camera permission:

XML
 
<uses-feature android:name="android.hardware.camera.any" />

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

In app/build.gradle, add the dependency:        

Groovy
 
dependencies {

            ...

            def camerax_version = "1.0.1"

            implementation "androidx.camera:camera-camera2:$camerax_version"

            implementation "androidx.camera:camera-lifecycle:$camerax_version"

            implementation "androidx.camera:camera-view:1.0.0-alpha27"

        }

Dynamsoft Camera Enhancer 

Similar to CameraX, [Dynamsoft Camera Enhancer](https://www.dynamsoft.com/camera-enhancer/docs/introduction/) is also a wrapper for Android Camera2 API. In addition to basic camera capabilities, it features frame filtering for better image quality. We use Dynamsoft Camera Enhancer to make a comparison with CameraX.

Installation

In "settings.gradle", add the custom Maven repository:

Groovy
 
dependencyResolutionManagement {

            repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)

            repositories {

                ...

                maven{url "https://download.dynamsoft.com/maven/dce/aar"}

            }

        }

In app/build.gradle, add the dependency:

Groovy
 
dependencies {

            ...

            implementation 'com.dynamsoft:dynamsoftcameraenhancer:2.1.0@aar'

        }
  • QR Code Scanning SDK

Dynamsoft Barcode Reader: 

A barcode SDK that supports all mainstream linear barcode and 2D barcode formats.

Installation

In settings.gradle, add the custom Maven repository:       

Groovy
 
dependencyResolutionManagement {

            repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)

            repositories {

                ...

                maven{url "https://download.dynamsoft.com/maven/dbr/aar"}

            }

        }

In app/build.gradle, add the dependency:        

Groovy
 
dependencies {

            ...

            implementation 'com.dynamsoft:dynamsoftbarcodereader:8.9.0@aar'

        }

You also need a license key to activate the barcode SDK.

Creating Android Camera Preview Within 5 Minutes

Three Steps To Implement Camera Preview With CameraX

The official CameraX tutorial is written in Kotlin. Here we use Java.

  1. Create the UI layout which contains the CameraX preview view:
XML
 
<?xml version="1.0" encoding="utf-8"?>

    <androidx.constraintlayout.widget.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:layout_width="match_parent"

        android:layout_height="match_parent"

        tools:context=".CameraXActivity">

    

        <androidx.camera.view.PreviewView

            android:id="@+id/camerax_viewFinder"

            android:layout_width="match_parent"

            android:layout_height="match_parent" />

    

    </androidx.constraintlayout.widget.ConstraintLayout>

2.  Check and request camera permissions:

Java
 
  @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.camerax_main);

        previewView = findViewById(R.id.camerax_viewFinder);



        if (!CameraUtils.allPermissionsGranted(this)) {

            CameraUtils.getRuntimePermissions(this);

        } else {

            startCamera();

        }

    }



    private static String[] getRequiredPermissions(Context context) {

        try {

            PackageInfo info =

                    context.getPackageManager()

                            .getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);

            String[] ps = info.requestedPermissions;

            if (ps != null && ps.length > 0) {

                return ps;

            } else {

                return new String[0];

            }

        } catch (Exception e) {

            return new String[0];

        }

    }



    public static boolean allPermissionsGranted(Context context) {

        for (String permission : getRequiredPermissions(context)) {

            if (!isPermissionGranted(context, permission)) {

                return false;

            }

        }

        return true;

    }



    public static void getRuntimePermissions(Activity activity) {

        List<String> allNeededPermissions = new ArrayList<>();

        for (String permission : getRequiredPermissions(activity)) {

            if (!isPermissionGranted(activity, permission)) {

                allNeededPermissions.add(permission);

            }

        }



        if (!allNeededPermissions.isEmpty()) {

            ActivityCompat.requestPermissions(

                    activity, allNeededPermissions.toArray(new String[0]), PERMISSION_REQUESTS);

        }

    }



    private static boolean isPermissionGranted(Context context, String permission) {

        if (ContextCompat.checkSelfPermission(context, permission)

                == PackageManager.PERMISSION_GRANTED) {

            Log.i(TAG, "Permission granted: " + permission);

            return true;

        }

        Log.i(TAG, "Permission NOT granted: " + permission);

        return false;

    }

3.  Start the camera preview:

Java
 
 private void startCamera() {

        ListenableFuture<ProcessCameraProvider> cameraProviderFuture =

                ProcessCameraProvider.getInstance(getApplication());

        cameraProviderFuture.addListener(

                () -> {

                    try {

                        ProcessCameraProvider cameraProvider = cameraProviderFuture.get();



                        Preview.Builder builder = new Preview.Builder();

                        Preview previewUseCase = builder.build();

                        previewUseCase.setSurfaceProvider(previewView.getSurfaceProvider());

                        CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;

                        cameraProvider.unbindAll();

                        cameraProvider.bindToLifecycle(this, cameraSelector, previewUseCase);

                    } catch (ExecutionException | InterruptedException e) {

                        Log.e(TAG, "Unhandled exception", e);

                    }

                },

                ContextCompat.getMainExecutor(getApplication()));

    }

Two Steps To Implement Camera Preview With Dynamsoft Camera Enhancer

Using Dynamsoft Camera Enhancer, you can write less code than using CameraX to implement the same functionality.

  1. Create the UI layout which contains the DCE preview view:
XML
 
<?xml version="1.0" encoding="utf-8"?>

    <androidx.constraintlayout.widget.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:layout_width="match_parent"

        android:layout_height="match_parent"

        tools:context=".CameraXActivity">

    

        <com.dynamsoft.dce.DCECameraView

            android:id="@+id/dce_viewFinder"

            android:layout_width="match_parent"

            android:layout_height="match_parent" />

    

    </androidx.constraintlayout.widget.ConstraintLayout>

2.  Start the camera preview:

Java
 
@Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.dce_main);

        previewView = findViewById(R.id.dce_viewFinder);



        cameraEnhancer = new CameraEnhancer(this);

        cameraEnhancer.setCameraView(previewView);

        cameraEnhancer.addListener(this);

    }



    @Override

    protected void onResume() {

        super.onResume();

        try {

            cameraEnhancer.open();

        } catch (CameraEnhancerException e) {

            e.printStackTrace();

        }

    }



    @Override

    protected void onPause() {

        super.onPause();

        try {

            cameraEnhancer.close();

        } catch (CameraEnhancerException e) {

            e.printStackTrace();

        }

    }

All in One

We create an entry activity to launch the CameraX and Dynamsoft Camera Enhancer respectively:

Java
 
package com.example.qrcodescanner;



import android.content.Intent;

import android.os.Bundle;

import android.view.View;



import androidx.appcompat.app.AppCompatActivity;



public class EntryChoiceActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.entry_choice);



        findViewById(R.id.camerax_entry_point).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                Intent intent = new Intent(EntryChoiceActivity.this, CameraXActivity.class);

                startActivity(intent);

            }

        });



        findViewById(R.id.dce_entry_point).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                Intent intent = new Intent(EntryChoiceActivity.this, DceActivity.class);

                startActivity(intent);

            }

        });

    }

}

Turning Android Camera Into QR Code Scanner

To scan QR codes, we need to continuously get the camera preview frames and pass the frames to the QR code detector.

How To Set the Camera Frame Callback

When using CameraX, we can use the "ImageAnalysis" class to receive the camera frames:

Java
 
ImageAnalysis analysisUseCase = new ImageAnalysis.Builder().build();

analysisUseCase.setAnalyzer(cameraExecutor,

        imageProxy -> {

            // image processing

            // Must call close to keep receiving frames.

            imageProxy.close();

        });

cameraProvider.bindToLifecycle(this, cameraSelector, previewUseCase, analysisUseCase);

In contrast, Dynamsoft Camera Enhancer is much easier. The callback function looks like the one used in Android Camera1:

Java
 
public class DceActivity extends AppCompatActivity implements DCEFrameListener {

    @Override

    public void frameOutputCallback(DCEFrame dceFrame, long l) {

        // image processing

    }

}

The data types returned by their callback functions are different. Data type conversion is required for later use.

Decoding QR Code

With CameraX, we firstly convert "ByteBuffer" to "byte[]" and then call the "decodeBuffer()" method:

Java
 
analysisUseCase.setAnalyzer(cameraExecutor,

imageProxy -> {

    TextResult[] results = null;

    ByteBuffer buffer = imageProxy.getPlanes()[0].getBuffer();

    int nRowStride = imageProxy.getPlanes()[0].getRowStride();

    int nPixelStride = imageProxy.getPlanes()[0].getPixelStride();

    int length = buffer.remaining();

    byte[] bytes = new byte[length];

    buffer.get(bytes);

    try {

        results = reader.decodeBuffer(bytes, imageProxy.getWidth(), imageProxy.getHeight(), nRowStride * nPixelStride, EnumImagePixelFormat.IPF_NV21, "");

    } catch (BarcodeReaderException e) {

        e.printStackTrace();

    }



    // Must call close to keep receiving frames.

    imageProxy.close();

});

Whereas with Dynamsoft Camera Enhancer, we get Bitmap from the DCEFrame and then call the decodeBufferedImage() method:

Java
 
public void frameOutputCallback(DCEFrame dceFrame, long l) {

    TextResult[] results = null;

    try {

        results = reader.decodeBufferedImage(dceFrame.toBitmap(), "");

    } catch (BarcodeReaderException e) {

        e.printStackTrace();

    }

}

Android QR code scanner

Using Zoom and Torch To Boost the Frame Quality

The recognition accuracy is always affected by the input image quality. If the QR code is too small, we can zoom in on the camera to scale up the image. If the input image is too dark, we can turn on the torch to brighten the image. Both CameraX and Dynamsoft Camera Enhancer have completely supported the camera control.

Android Camera Zoom

We use the finger pinch gesture to trigger the zoom. Thus, the first step is to create the gesture detector and take over the "onTouchEvent()" method:

Java
 
public class ZoomController {

    public final static String TAG = "ZoomController";

    private float currentFactor = 1.0f;

    private float minZoomRatio = 1.0f, maxZoomRatio = 1.0f;

    private ZoomStatus zoomStatus;

    private ScaleGestureDetector scaleGestureDetector;

    private ScaleGestureDetector.OnScaleGestureListener scaleGestureListener = new ScaleGestureDetector.OnScaleGestureListener() {

        @Override

        public boolean onScale(ScaleGestureDetector detector) {

            Log.i(TAG, "onScale: " + detector.getScaleFactor());

            currentFactor = detector.getScaleFactor() * currentFactor;

            if (currentFactor < minZoomRatio) currentFactor = minZoomRatio;

            if (currentFactor > maxZoomRatio) currentFactor = maxZoomRatio;



            if (zoomStatus != null) {

                zoomStatus.onZoomChange(currentFactor);

            }



            return true;

        }



        @Override

        public boolean onScaleBegin(ScaleGestureDetector detector) {

            return true;

        }



        @Override

        public void onScaleEnd(ScaleGestureDetector detector) {

        }

    };



    public ZoomController(Activity activity) {

        scaleGestureDetector = new ScaleGestureDetector(activity, scaleGestureListener);

    }



    public interface ZoomStatus {

        void onZoomChange(float ratio);

    }



    public void addListener(ZoomStatus zoomStatus) {

        this.zoomStatus = zoomStatus;

    }



    public void initZoomRatio(float minZoomRatio, float maxZoomRatio) {

        this.minZoomRatio = minZoomRatio;

        this.maxZoomRatio = maxZoomRatio;

    }



    public boolean onTouchEvent(MotionEvent event) {

        return scaleGestureDetector.onTouchEvent(event);

    }

}



@Override

public boolean onTouchEvent(MotionEvent event) {

    zoomController.onTouchEvent(event);

    return super.onTouchEvent(event);

}

As the gesture is detected, we get the scale factor and take it as the zoom ratio.

Set Camera Zoom Ratio With CameraX

Java
 
if (camera != null) {

    camera.getCameraControl().setZoomRatio(ratio);

}

Set Camera Zoom Ratio With Dynamsoft Camera Enhancer

Java
 
try {

    cameraEnhancer.setZoom(ratio);

} catch (CameraEnhancerException e) {

    e.printStackTrace();

}

Android Camera Torch

To turn on the torch automatically, we monitor the light value returned by the light sensor.

Java
 
public class AutoTorchController implements SensorEventListener {

    public final static String TAG = "AutoTorchController";

    private SensorManager sensorManager;

    private TorchStatus torchStatus;



    public interface TorchStatus {

        void onTorchChange(boolean status);

    }



    public AutoTorchController(Activity activity) {

        sensorManager = (SensorManager)activity.getSystemService(SENSOR_SERVICE);

    }



    public void onStart() {

        Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

        if(lightSensor != null){

            sensorManager.registerListener(

                    this,

                    lightSensor,

                    SensorManager.SENSOR_DELAY_NORMAL);



        }

    }



    public void onStop() {

        sensorManager.unregisterListener(this);

    }



    @Override

    public void onSensorChanged(SensorEvent event) {

        if(event.sensor.getType() == Sensor.TYPE_LIGHT){

            if (event.values[0] < 20) {

                if (torchStatus != null) torchStatus.onTorchChange(true);

            }

            else {

                if (torchStatus != null) torchStatus.onTorchChange(false);

            }

        }

    }



    @Override

    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }



    public void addListener(TorchStatus torchStatus) {

        this.torchStatus = torchStatus;

    }

}

Toggle Camera Torch With CameraX

Java
 
if (camera != null) camera.getCameraControl().enableTorch(status);

Toggle Camera Torch With Dynamsoft Camera Enhancer

Java
 
if (status) {

    try {

        cameraEnhancer.turnOnTorch();

    } catch (CameraEnhancerException e) {

        e.printStackTrace();

    }

}

else {

    try {

        cameraEnhancer.turnOffTorch();

    } catch (CameraEnhancerException e) {

        e.printStackTrace();

    }

}

Source Code

https://github.com/yushulx/android-camera2-preview-qr-code-scanner

QR code Android (robot) Java (programming language)

Published at DZone with permission of Xiao Ling. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Diving into JNI: My Messy Adventures With C++ in Android
  • Cordova: Communicating Between JavaScript and Java
  • Is Java Still Relevant?
  • Using Barcodes in iText 7

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook