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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Android Device Matching With Socket Programming
  • Cordova: Communicating Between JavaScript and Java
  • Is Java Still Relevant?
  • Using Barcodes in iText 7

Trending

  • Go 1.24+ Native FIPS Support for Easier Compliance
  • Intro to RAG: Foundations of Retrieval Augmented Generation, Part 2
  • Implementing API Design First in .NET for Efficient Development, Testing, and CI/CD
  • Modern Test Automation With AI (LLM) and Playwright MCP
  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
4.8K 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

  • Android Device Matching With Socket Programming
  • Cordova: Communicating Between JavaScript and Java
  • Is Java Still Relevant?
  • Using Barcodes in iText 7

Partner Resources

×

Comments
Oops! Something Went Wrong

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

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

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 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!