Developing a "native" Barcode Scanner in Android

Developing a "native" Barcode Scanner in Android

    As you can read at my previous, I had presented a simple Barcode reader application. It's principle is user must download and install Zxing application from Google Play first, my app will call Zxing scanner screen as a sub-Activity and get the result (Barcode or QR code information) from it after scanning process finished!

    The fact that we always would like to developing an application which be able to "embedding" this scanner in it, not warning that our device has not installed Barcode Reader application. Fortunately, there is a library developed by Dushyanth Maguluru which based on ZXing and ZBar, which be able to Barcode Scanner views, help us to develop a simple application which can scan Barcode/QR code itself.

Adding Barcode reader library to Android Studio Project

    By reading it's source code on Github, we can find out that the author has combined ZXing and ZBar, each library to a dependency module in this project! In this tutorial, I will use Zxing, ZBar guide is absolutely similar!
    In order to use this library, the simplest way is adding it's dependency to your application level build.gradle:
dependencies {
    compile 'me.dm7.barcodescanner:zxing:1.9' //barcode reader dependency
    compile 'com.android.support:appcompat-v7:24.1.1'
}

Project main activity

    This main activity has 2 important works:
  • Start the scanning barcode activity as a sub-Activity by Intent.
  • Retrieve scanning results: barcode format and content by overriding onActivityResult().
    It's source code simple like this:
MainActivity.java
package info.devexchanges.barcodescanner;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private final static int REQUEST_SCANNER = 1;
    public final static String FORMAT = "format";
    public final static String CONTENT = "content";

    private TextView content;
    private TextView format;

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

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        View btnScan = findViewById(R.id.btn_scan);
        format = (TextView) findViewById(R.id.format);
        content = (TextView) findViewById(R.id.content);

        setSupportActionBar(toolbar);

        btnScan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, ScannerActivity.class);
                startActivityForResult(intent, REQUEST_SCANNER);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == Activity.RESULT_OK) {
            content.setText(data.getStringExtra(CONTENT));
            format.setText(data.getStringExtra(FORMAT));
        }
    }
}
    And it's layout:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="info.devexchanges.barcodescanner.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_gravity="top"
        android:minHeight="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/btn_scan"
        android:text="Scan Barcode"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/format"
        android:gravity="center"
        android:padding="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/content"
        android:gravity="center"
        android:padding="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Scanning Barcode/QR code Activity

    As you can see at the MainActivity code above, when Button pressed, ScannerActivity will be launch as a sub-Activity.
    The Barcode scanner view is initialized by a ZXingScannerView object. In onCreate(), declaring it like this:
ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);

scannerView = new ZXingScannerView(this);
contentFrame.addView(scannerView);
    The most important work is retrieving scanning result, so your ScannerActivity must implements ResultHandler interface and override handleResult(Result result) method. Moreover, start scanning by invoke this code:
scannerView.startCamera();
    And if you want to stop this scanning, call closing camera method:
scannerView.stopCamera();
    This is full code for this activity:
ScannerActivity.java
package info.devexchanges.barcodescanner;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.ViewGroup;

import com.google.zxing.Result;

import me.dm7.barcodescanner.zxing.ZXingScannerView;

public class ScannerActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {

    private ZXingScannerView scannerView;

    @Override
    public void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_scanner);
        ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);

        scannerView = new ZXingScannerView(this);
        contentFrame.addView(scannerView);
    }

    @Override
    public void onResume() {
        super.onResume();
        scannerView.setResultHandler(this);
        scannerView.startCamera();
    }

    @Override
    public void onPause() {
        super.onPause();
        scannerView.stopCamera();
    }

    @Override
    public void handleResult(Result rawResult) {
        //Call back data to main activity
        Intent intent = new Intent();
        intent.putExtra(MainActivity.FORMAT, rawResult.getBarcodeFormat().toString());
        intent.putExtra(MainActivity.CONTENT, rawResult.getText());

        setResult(Activity.RESULT_OK, intent);
        finish();
    }
}
    And it's layout:
activity_scanner.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</FrameLayout>
    Running this application, we'll have this screen:

    Clicking button, we'll start scanning:
Scanning Barcode
    After scanning, we have this result:
    An example after scanning a QR code:

Conclusions

    Now, the simple "native" scanning Barcode/QR code app has been done! In this tutorial, we've run through the process of facilitating barcode/QR code scanning within Android app using the a third-party library based on ZXing. In your own apps, you might want to carry out further processing on the retrieved scan results, such as loading URLs or looking the data up in a third party data source. Moreover, by reading this library ZBar guide, you also can find out the way to use this module to your project - absolutely analogous!

Android Basic Training Course: Working with Camera and Image Gallery

Android Basic Training Course: Working with Camera and Image Gallery

    Sometimes, in your Android application, you’ll want the user to choose an image from the gallery or device camera. With camera, we can take a photo directly at the moment and call back data (taken photo in Bitmap format) to our application, and with Gallery apps which available in our device (Gallery, Photos, ES File Explorer, etc), we can get any image storing on device memory in Uri format to our application. Both of 2 ways use Intent and sub-Activity.

Take photo from Camera

    Firstly, you must request camera using permission with your app first in AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA"/>
    In code, creating an Intent with action String is MediaStore.ACTION_IMAGE_CAPTURE, start a sub Activity like this:
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_PICK); //CAMERA_PICK is a final int variable!
    Handling data (in Bitmap format after taking a photo by overriding onActivityResult():
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == CAMERA_PICK && resultCode == RESULT_OK) {
            Bitmap photo = (Bitmap) data.getExtras().get("data");
            //set photo bitmap to ImageView
            imageView.setImageBitmap(photo);
        } 
    }
NOTE: RESULT_OK is code when we taking a photo from camera successfully.

Get Photo from Gallery

    In order to access to device memory to get file (in this case is photos), you must request reading data permission:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Absolutely similar with using Camera, you can open a dialog to choosing a Gallery app by Intent with ACTION_GET_CONTENT:
Intent intent = new Intent();
intent.setType("image/*"); //set type for files (image type)
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_FROM_GALLERY); //PICK_FROM_GALLERY is final int variable
And get data back in onActivityResult():
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == PICK_FROM_GALLERY && resultCode == RESULT_OK) {
            Uri selectedImage = data.getData();
            imageView.setImageURI(selectedImage);
        }
    }

Sample project full code

    Now, I will provide a simple project which allows users perform these 2 actions and get data to ImageView. The main Activity layout:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="info.devexchanges.pickimages.MainActivity">

    <ImageView
        android:id="@+id/image"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:scaleType="centerCrop"
        android:layout_centerHorizontal="true" />

    <Button
        android:id="@+id/btn_camera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:drawableLeft="@android:drawable/ic_menu_camera"
        android:text="Pick from Camera" />

    <Button
        android:id="@+id/btn_gallery"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_camera"
        android:drawableLeft="@android:drawable/ic_menu_gallery"
        android:text="Pick from Gallery" />
</RelativeLayout>
    And it's programmatically code:
MainActivity.java
package info.devexchanges.pickimages;

import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private View btnCamera;
    private View btnGallery;
    private ImageView imageView;
    private static final int CAMERA_PICK = 1;
    private static final int PICK_FROM_GALLERY = 2;

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

        btnCamera = findViewById(R.id.btn_camera);
        btnGallery = findViewById(R.id.btn_gallery);
        imageView = (ImageView) findViewById(R.id.image);

        btnCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(cameraIntent, CAMERA_PICK);
            }
        });

        btnGallery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setType("image/*"); //set type for files (image type)
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_FROM_GALLERY);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == CAMERA_PICK && resultCode == RESULT_OK) {
            Bitmap photo = (Bitmap) data.getExtras().get("data");
            //set photo bitmap to ImageView
            imageView.setImageBitmap(photo);
        } else if (requestCode == PICK_FROM_GALLERY && resultCode == RESULT_OK) {
            Uri selectedImage = data.getData();
            imageView.setImageURI(selectedImage);
        }
    }
}
    Running application, we have this output screen:
 
    After taking a photo from Camera successful, photo will be set to ImageView:

    When clicking at "Pick From Gallery" button, the "Gallery apps chooser" will be displayed:
    And when user choose a app and a photo then, it's also set to ImageView:

Final thoughts

    If you would like Google Play prevents the app to be installed on devices those doesn’t have camera, please adding <uses-feature android:name="android.hardware.camera" /> in your AndroidManifest.xml.
    Another common problem is working with Camera and Gallery in Fragment, your Fragment sometimes stop before onActivityResult() called. This bug happen in some devices have strict memory management (e.g: Samsung Galaxy Tab 4, Samsung Galaxy S3,...), and in order to resolved it, please uncheck "Do not keep Activities" option in Developer Options entry: