Learn how to Build a Barcode Scanner Android App with CameraX
Tags: android, barcode
Published: 2021-08-11 16:42:55

Barcodes are everywhere. Barcode technology is used in almost every industry these days. Barcode technology forms an integral part of retail, healthcare, logistics, agriculture, or finance. The evolution of technology has made mobile barcoding possible, and it is widely used in almost every field. And if you want to build an Android barcode scanner application, then you have reached the right place! This blog will take you through the process of building a barcode scanner Android with CameraX. 

## Barcode Scanner Android with CameraX 

Barcode reading and camera control are two critical parts of a barcode scanner. Today, we will use the Dynamsoft Barcode Reader SDK to make a [barcode scanner Android](https://www.dynamsoft.com/codepool/android-barcode-scanner-with-camerax.html) app with CameraX. Dynamsoft Barcode Reader SDK provides an easy-to-use Android library. Hence, the implementation of barcode reading functionality becomes straightforward. While Dynamsoft Barcode Scanner SDK simplifies adding barcode reading capability into an application, creating a good camera app is not easy. 

Android offers three sets of camera APIs: Camera, Camera2, and CameraX. Camera is good at recording videos and taking photos, but it does not provide good camera control. Camera2 comes with in-depth camera control, but its usage is complex. CameraX is a newer API. It is specially designed to make the development of camera apps easier. Preview, image analysis, and capture are the three primary use cases of CameraX. Now that you are acquainted with CameraX, let's discuss the step-by-step procedure to build a barcode scanner Android app. 

## Things You Should Have

Since we are going to use Dynamsoft Barcode Reader SDK to build this barcode scanner Android app, you'll do the following. 

* Dynamsoft Barcode Reader SDK Offline SDK: [Click here](https://www.dynamsoft.com/barcode-reader/downloads/) to download the free version of the Dynamsoft Barcode SDK trial. 
* 30-Day Free Trial License: Download the free trial license by [clicking here](https://www.dynamsoft.com/api-common/Regist/Regist?callback=https%3A%2F%2Fwww.dynamsoft.com%2Fcustomer%2Flicense%2FtrialLicense%2F%3Fproduct%3Ddbr). 

## How to Create a New Project? 

The following steps will help with creating a new project. 

**Step 1:** Create a new project by opening Android Studio. Now choose Empty Activity. Select Java as the language and set the minimum SDK to 21 as CameraX needs at least Android 5.0. 
**Step 2:** Follow this to add Dynamsoft Barcode Reader 
**Step 3:** This guide will help you to add CameraX. 

## Layout Design 

Below is the content of activity_main.xml that defines the layout of the Main Activity. A Button will be used to open the camera activity. 

```
<?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=".MainActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/container"
            android:textSize="20sp"
            android:text="This is a CameraX test app!"
            android:gravity="center">
        </TextView>
        <Button
            android:id="@+id/enableCamera"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Open Camera" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
```
To show barcode reading results and camera preview, you will have to create a new activity named CameraActivity. Below is the layout for the same> 

```
<?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=".CameraActivity">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.camera.view.PreviewView
            android:id="@+id/previewView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

        </androidx.camera.view.PreviewView>
    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/resultContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/resultView"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:layout_gravity="bottom"
            android:background="#80000000"
            android:gravity="center_horizontal|top"
            android:textColor="#9999ff"
            android:textSize="12sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
```
## How to use CameraX to Preview and Analyse Images? 

The following steps will help you to preview and analyze images using CameraX. 

1. Set event listener for the open camera button.

```
Button enableCamera = findViewById(R.id.enableCamera);
     Context ctx=this;
     enableCamera.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             if (hasCameraPermission()) {
                 Intent intent = new Intent(ctx, CameraActivity.class);
                 startActivity(intent);
             } else {
                 requestPermission();
                 Toast.makeText(ctx, "Please grant camera permission" , Toast.LENGTH_SHORT).show();
             }
         }
     });
```

You will require camera permission. The user will be asked for permission when the camera is required. The code is shown below. 

```
 //properties
 private static final String[] CAMERA_PERMISSION = new String[]{Manifest.permission.CAMERA};
 private static final int CAMERA_REQUEST_CODE = 10;
    
 //methods
 private boolean hasCameraPermission() {
     return ContextCompat.checkSelfPermission(
             this,
             Manifest.permission.CAMERA
     ) == PackageManager.PERMISSION_GRANTED;
 }

 private void requestPermission() {
     ActivityCompat.requestPermissions(
             this,
             CAMERA_PERMISSION,
             CAMERA_REQUEST_CODE
     );
 }
```
You will have to add the following code to AndroidManifest.xml:

```
<uses-permission android:name="android.permission.CAMERA" />
```
2. Init and enable preview and image analysis use cases

The Dynamsoft Barcode Reader SDK will read barcodes from the image buffer. 

```
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_camera);
    previewView = findViewById(R.id.previewView);
    resultView = findViewById(R.id.resultView);
    exec = Executors.newSingleThreadExecutor();
    try {
        dbr = new BarcodeReader();
    } catch (BarcodeReaderException e) {
        e.printStackTrace();
    }
    DMLTSConnectionParameters parameters = new DMLTSConnectionParameters();
    parameters.organizationID = "******";
    dbr.initLicenseFromLTS(parameters, new DBRLTSLicenseVerificationListener() {
        @Override
        public void LTSLicenseVerificationCallback(boolean isSuccess, Exception error) {
            if (!isSuccess) {
                error.printStackTrace();
            }
        }
    });
    cameraProviderFuture = ProcessCameraProvider.getInstance(this);
    cameraProviderFuture.addListener(new Runnable() {
        @Override
        public void run() {
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                bindPreviewAndImageAnalysis(cameraProvider);
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, ContextCompat.getMainExecutor(this));
}
    
@SuppressLint("UnsafeExperimentalUsageError")
private void bindPreviewAndImageAnalysis(@NonNull ProcessCameraProvider cameraProvider) {
    Size resolution = new Size(720, 1280);
    Display d = getDisplay();
    if (d.getRotation() != Surface.ROTATION_0) {
        resolution = new Size(1280, 720);
    }

    Preview.Builder previewBuilder = new Preview.Builder();
    previewBuilder.setTargetResolution(resolution);
    Preview preview = previewBuilder.build();

    ImageAnalysis.Builder imageAnalysisBuilder = new ImageAnalysis.Builder();

    imageAnalysisBuilder.setTargetResolution(resolution)
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST);

    ImageAnalysis imageAnalysis = imageAnalysisBuilder.build();

    imageAnalysis.setAnalyzer(exec, new ImageAnalysis.Analyzer() {
        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void analyze(@NonNull ImageProxy image) {
            int rotationDegrees = image.getImageInfo().getRotationDegrees();
            TextResult[] results = null;
            ByteBuffer buffer = image.getPlanes()[0].getBuffer();
            int nRowStride = image.getPlanes()[0].getRowStride();
            int nPixelStride = image.getPlanes()[0].getPixelStride();
            int length = buffer.remaining();
            byte[] bytes = new byte[length];
            buffer.get(bytes);
            ImageData imageData = new ImageData(bytes, image.getWidth(), image.getHeight(), nRowStride * nPixelStride);
            try {
                results = dbr.decodeBuffer(imageData.mBytes, imageData.mWidth, imageData.mHeight, imageData.mStride, EnumImagePixelFormat.IPF_NV21, "");
            } catch (BarcodeReaderException e) {
                e.printStackTrace();
            }
            StringBuilder sb = new StringBuilder();
            sb.append("Found ").append(results.length).append(" barcode(s):\n");
            for (int i = 0; i < results.length; i++) {
                sb.append(results[i].barcodeText);
                sb.append("\n");
            }
            Log.d("DBR", sb.toString());
            resultView.setText(sb.toString());
            image.close();
        }
    });

    CameraSelector cameraSelector = new CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
    preview.setSurfaceProvider(previewView.createSurfaceProvider());

    UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
            .addUseCase(preview)
            .addUseCase(imageAnalysis)
            .build();
    camera = cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, useCaseGroup);
}
    
private class ImageData {
    private int mWidth, mHeight, mStride;
    byte[] mBytes;

    ImageData(byte[] bytes, int nWidth, int nHeight, int nStride) {
        mBytes = bytes;
        mWidth = nWidth;
        mHeight = nHeight;
        mStride = nStride;
    }
}
```
### How to make the Barcode Scanner Android App with CameraX more Usable? 

If you want to add more features and functionalities to this app, you will have to add more CameraControl APIs. [This blog](https://www.dynamsoft.com/codepool/android-barcode-scanner-with-camerax.html) will take you through all the steps. [Click here](https://www.dynamsoft.com/codepool/android-barcode-scanner-with-camerax.html) to get started.