com.phonegap.plugins.pdf417.Pdf417Scanner.java Source code

Java tutorial

Introduction

Here is the source code for com.phonegap.plugins.pdf417.Pdf417Scanner.java

Source

/**
 * PhoneGap is available under *either* the terms of the modified BSD license *or* the
 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
 *
 * Copyright (c) Matt Kane 2010
 * Copyright (c) 2011, IBM Corporation
 * Copyright (c) 2013, Maciej Nux Jaros
 */
package com.phonegap.plugins.pdf417;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.os.Parcelable;
import android.os.Bundle;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;

import com.microblink.activity.Pdf417ScanActivity;
import com.microblink.recognizers.barcode.BarcodeType;
import com.microblink.recognizers.barcode.bardecoder.BarDecoderRecognizerSettings;
import com.microblink.recognizers.barcode.bardecoder.BarDecoderScanResult;
import com.microblink.recognizers.barcode.pdf417.Pdf417RecognizerSettings;
import com.microblink.recognizers.barcode.pdf417.Pdf417ScanResult;
import com.microblink.recognizers.barcode.usdl.USDLRecognizerSettings;
import com.microblink.recognizers.barcode.usdl.USDLScanResult;
import com.microblink.recognizers.barcode.zxing.ZXingRecognizerSettings;
import com.microblink.recognizers.barcode.zxing.ZXingScanResult;
import com.microblink.recognizers.settings.GenericRecognizerSettings;
import com.microblink.recognizers.settings.RecognizerSettings;
import com.microblink.results.barcode.BarcodeDetailedData;
import com.microblink.view.recognition.RecognizerView;
import com.microblink.hardware.camera.CameraType;

public class Pdf417Scanner extends CordovaPlugin {

    private static final int REQUEST_CODE = 1337;

    private static final String SCAN = "scan";
    private static final String CANCELLED = "cancelled";

    private static final String RESULT_LIST = "resultList";
    private static final String RESULT_TYPE = "resultType";
    private static final String TYPE = "type";
    private static final String DATA = "data";
    private static final String FIELDS = "fields";
    private static final String RAW_DATA = "raw";

    private static final String LOG_TAG = "Pdf417Scanner";

    private CallbackContext callbackContext;

    /**
     * Constructor.
     */
    public Pdf417Scanner() {
    }

    /**
     * Executes the request.
     * 
     * This method is called from the WebView thread. To do a non-trivial amount
     * of work, use: cordova.getThreadPool().execute(runnable);
     * 
     * To run on the UI thread, use:
     * cordova.getActivity().runOnUiThread(runnable);
     * 
     * @param action
     *            The action to execute.
     * @param args
     *            The exec() arguments.
     * @param callbackContext
     *            The callback context used when calling back into JavaScript.
     * @return Whether the action was valid.
     * 
     * @sa 
     *     https://github.com/apache/cordova-android/blob/master/framework/src/org
     *     /apache/cordova/CordovaPlugin.java
     */
    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
        this.callbackContext = callbackContext;

        if (action.equals(SCAN)) {
            Set<String> types = new HashSet<String>();

            JSONArray typesArg = args.optJSONArray(0);
            for (int i = 0; i < typesArg.length(); ++i) {
                types.add(typesArg.optString(i));
            }

            // Default values
            Boolean customUI = false;
            Boolean beep = true, noDialog = null, uncertain = null, quietZone = null, inverseScanning = null,
                    frontFace = null;
            String license = null;

            if (!args.isNull(1)) {
                JSONObject options = args.optJSONObject(1);

                if (!options.isNull("beep")) {
                    beep = options.optBoolean("beep");
                }
                if (!options.isNull("noDialog")) {
                    noDialog = options.optBoolean("noDialog");
                }
                if (!options.isNull("uncertain")) {
                    uncertain = options.optBoolean("uncertain");
                }
                if (!options.isNull("quietZone")) {
                    quietZone = options.optBoolean("quietZone");
                }
                if (!options.isNull("inverseScanning")) {
                    inverseScanning = options.optBoolean("inverseScanning");
                }
                if (!options.isNull("frontFace")) {
                    frontFace = options.optBoolean("frontFace");
                }
            }

            if (!args.isNull(3)) {
                license = args.optString(3);
            }

            scan(types, beep, noDialog, uncertain, quietZone, inverseScanning, frontFace, license);
        } else {
            return false;
        }
        return true;
    }

    /**
     * Starts an intent from provided class to scan and decode a barcode.
     */
    public void scan(Set<String> types, Boolean beep, Boolean noDialog, Boolean uncertain, Boolean quietZone,
            Boolean inverseScanning, Boolean frontFace, String license) {

        Context context = this.cordova.getActivity().getApplicationContext();
        FakeR fakeR = new FakeR(this.cordova.getActivity());

        Intent intent = new Intent(context, Pdf417ScanActivity.class);

        // If you want sound to be played after the scanning process ends, 
        // put here the resource ID of your sound file. (optional)
        intent.putExtra(Pdf417ScanActivity.EXTRAS_BEEP_RESOURCE, fakeR.getId("raw", "beep_pdf417"));

        // set the license key (for commercial versions only) - obtain your key at
        // http://pdf417.mobi
        // after setting the correct license key
        if (license != null) {
            intent.putExtra(Pdf417ScanActivity.EXTRAS_LICENSE_KEY, license);
        }

        Pdf417RecognizerSettings pdf417RecognizerSettings = null;
        if (types.contains("PDF417")) {
            // Pdf417RecognizerSettings define the settings for scanning plain PDF417 barcodes.
            pdf417RecognizerSettings = new Pdf417RecognizerSettings();
            // Set this to true to scan barcodes which don't have quiet zone (white area) around it
            // Use only if necessary because it drastically slows down the recognition process
            if (quietZone != null) {
                pdf417RecognizerSettings.setNullQuietZoneAllowed(quietZone);
            }
            // Set this to true to scan even barcode not compliant with standards
            // For example, malformed PDF417 barcodes which were incorrectly encoded
            // Use only if necessary because it slows down the recognition process
            if (uncertain != null) {
                pdf417RecognizerSettings.setUncertainScanning(uncertain);
            }
        }

        // BarDecoderRecognizerSettings define settings for scanning 1D barcodes with algorithms
        // implemented by Microblink team.
        BarDecoderRecognizerSettings oneDimensionalRecognizerSettings = new BarDecoderRecognizerSettings();

        oneDimensionalRecognizerSettings.setScanCode128(types.contains("Code 128"));
        oneDimensionalRecognizerSettings.setScanCode39(types.contains("Code 39"));
        if (inverseScanning != null) {
            oneDimensionalRecognizerSettings.setInverseScanning(inverseScanning);
        }

        // USDLRecognizerSettings define settings for scanning US Driver's Licence barcodes
        // options available in that settings are similar to those in Pdf417RecognizerSettings
        // if license key does not allow scanning of US Driver's License, this settings will
        // be thrown out from settings array and error will be logged to logcat.
        USDLRecognizerSettings usdlRecognizerSettings = null;
        if (types.contains("USDL")) {
            usdlRecognizerSettings = new USDLRecognizerSettings();
            if (uncertain != null) {
                usdlRecognizerSettings.setUncertainScanning(uncertain);
            }
            // disable automatic scale detection
            //sett.setAutoScaleDetection(false);
            // disable scanning of barcodes that do not have quiet zone
            // as defined by the standard
            if (quietZone != null) {
                usdlRecognizerSettings.setNullQuietZoneAllowed(quietZone);
            }
        }

        // ZXingRecognizerSettings define settings for scanning barcodes with ZXing library
        // We use modified version of ZXing library to support scanning of barcodes for which
        // we still haven't implemented our own algorithms.
        ZXingRecognizerSettings zXingRecognizerSettings = new ZXingRecognizerSettings();
        // set this to true to enable scanning of QR codes
        zXingRecognizerSettings.setScanAztecCode(types.contains("Aztec"));
        zXingRecognizerSettings.setScanDataMatrixCode(types.contains("Data Matrix"));
        zXingRecognizerSettings.setScanEAN13Code(types.contains("EAN 13"));
        zXingRecognizerSettings.setScanEAN8Code(types.contains("EAN 8"));
        zXingRecognizerSettings.setScanITFCode(types.contains("ITF"));
        zXingRecognizerSettings.setScanQRCode(types.contains("QR Code"));
        zXingRecognizerSettings.setScanUPCACode(types.contains("UPCA"));
        zXingRecognizerSettings.setScanUPCECode(types.contains("UPCE"));
        if (inverseScanning != null) {
            zXingRecognizerSettings.setInverseScanning(inverseScanning);
        }
        // finally, when you have defined your scanning settings, you should put them into array
        // and send that array over intent to scan activity

        RecognizerSettings[] settArray = new RecognizerSettings[] { pdf417RecognizerSettings,
                oneDimensionalRecognizerSettings, zXingRecognizerSettings, usdlRecognizerSettings };
        // use Pdf417ScanActivity.EXTRAS_RECOGNIZER_SETTINGS_ARRAY to set array of recognizer settings
        intent.putExtra(Pdf417ScanActivity.EXTRAS_RECOGNIZER_SETTINGS_ARRAY, settArray);

        // additionally, there are generic settings that are used by all recognizers or the
        // whole recognition process
        GenericRecognizerSettings genericSettings = new GenericRecognizerSettings();
        // set this to true to enable returning of multiple scan results from single camera frame
        // default is false, which means that as soons as first barcode is found (no matter which type)
        // its contents will be returned.
        genericSettings.setAllowMultipleScanResultsOnSingleImage(true);
        intent.putExtra(Pdf417ScanActivity.EXTRAS_GENERIC_SETTINGS, genericSettings);

        // If you want sound to be played after the scanning process ends, 
        // put here the resource ID of your sound file. (optional)
        if (beep == true) {
            intent.putExtra(Pdf417ScanActivity.EXTRAS_BEEP_RESOURCE, fakeR.getId("raw", "beep_pdf417"));
        }

        // if you do not want the dialog to be shown when scanning completes, add following extra
        // to intent
        if (noDialog != null) {
            intent.putExtra(Pdf417ScanActivity.EXTRAS_SHOW_DIALOG_AFTER_SCAN, noDialog);
        }

        // front facing camera
        if (frontFace != null && frontFace == true) {
            intent.putExtra(Pdf417ScanActivity.EXTRAS_CAMERA_TYPE, (Parcelable) CameraType.CAMERA_FRONTFACE);
        }

        this.cordova.startActivityForResult((CordovaPlugin) this, intent, REQUEST_CODE);
    }

    /**
     * Called when the scanner intent completes.
     * 
     * @param requestCode
     *            The request code originally supplied to
     *            startActivityForResult(), allowing you to identify who this
     *            result came from.
     * @param resultCode
     *            The integer result code returned by the child activity through
     *            its setResult().
     * @param intent
     *            An Intent, which can return result data to the caller (various
     *            data can be attached to Intent "extras").
     */
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == REQUEST_CODE) {

            if (resultCode == Pdf417ScanActivity.RESULT_OK) {

                // First, obtain scan results array. If scan was successful, array will contain at least one element.
                // Multiple element may be in array if multiple scan results from single image were allowed in settings.

                Parcelable[] resultArray = data
                        .getParcelableArrayExtra(Pdf417ScanActivity.EXTRAS_RECOGNITION_RESULT_LIST);

                // Each recognition result corresponds to active recognizer. As stated earlier, there are 4 types of
                // recognizers available (PDF417, Bardecoder, ZXing and USDL), so there are 4 types of results
                // available.

                JSONArray resultsList = new JSONArray();

                for (Parcelable p : resultArray) {
                    try {
                        if (p instanceof Pdf417ScanResult) { // check if scan result is result of Pdf417 recognizer
                            resultsList.put(parsePdf417((Pdf417ScanResult) p));

                        } else if (p instanceof BarDecoderScanResult) { // check if scan result is result of BarDecoder recognizer                       
                            resultsList.put(parseBarDecoder((BarDecoderScanResult) p));

                        } else if (p instanceof ZXingScanResult) { // check if scan result is result of ZXing recognizer
                            resultsList.put(parseZxing((ZXingScanResult) p));

                        } else if (p instanceof USDLScanResult) { // check if scan result is result of US Driver's Licence recognizer
                            resultsList.put(parseUSDL((USDLScanResult) p));
                        }
                    } catch (Exception e) {
                        Log.e(LOG_TAG, "Error parsing " + p.getClass().getName());
                    }
                }

                try {
                    JSONObject root = new JSONObject();
                    root.put(RESULT_LIST, resultsList);
                    root.put(CANCELLED, false);
                    this.callbackContext.success(root);
                } catch (JSONException e) {
                    Log.e(LOG_TAG, "This should never happen");
                }

            } else if (resultCode == Pdf417ScanActivity.RESULT_CANCELED) {
                JSONObject obj = new JSONObject();
                try {
                    obj.put(CANCELLED, true);

                } catch (JSONException e) {
                    Log.e(LOG_TAG, "This should never happen");
                }
                this.callbackContext.success(obj);

            } else {
                this.callbackContext.error("Unexpected error");
            }
        }
    }

    private JSONObject parsePdf417(Pdf417ScanResult p) throws JSONException {
        // getStringData getter will return the string version of barcode contents
        String barcodeData = p.getStringData();
        // getRawData getter will return the raw data information object of barcode contents
        BarcodeDetailedData rawData = p.getRawData();
        // BarcodeDetailedData contains information about barcode's binary layout, if you
        // are only interested in raw bytes, you can obtain them with getAllData getter
        byte[] rawDataBuffer = rawData.getAllData();

        JSONObject result = new JSONObject();
        result.put(RESULT_TYPE, "Barcode result");
        result.put(TYPE, "PDF417");
        result.put(DATA, barcodeData);
        result.put(RAW_DATA, byteArrayToHex(rawDataBuffer));
        return result;
    }

    private JSONObject parseBarDecoder(BarDecoderScanResult p) throws JSONException {
        // with getBarcodeType you can obtain barcode type enum that tells you the type of decoded barcode
        BarcodeType type = p.getBarcodeType();
        // as with PDF417, getStringData will return the string contents of barcode
        String barcodeData = p.getStringData();

        JSONObject result = new JSONObject();
        result.put(RESULT_TYPE, "Barcode result");
        result.put(TYPE, type.name());
        result.put(DATA, barcodeData);
        return result;
    }

    private JSONObject parseZxing(ZXingScanResult p) throws JSONException {
        // with getBarcodeType you can obtain barcode type enum that tells you the type of decoded barcode
        BarcodeType type = p.getBarcodeType();
        // as with PDF417, getStringData will return the string contents of barcode
        String barcodeData = p.getStringData();

        JSONObject result = new JSONObject();
        result.put(RESULT_TYPE, "Barcode result");
        result.put(TYPE, type.name());
        result.put(DATA, barcodeData);
        return result;
    }

    private static final String RECOGNITIONDATA_TYPE = "PaymentDataType";

    private JSONObject parseUSDL(USDLScanResult p) throws JSONException {
        JSONObject fields = new JSONObject();

        Bundle bundle = p.getData();
        for (String key : bundle.keySet()) {
            // Originaly in RecognitionResultConstants.RECOGNITIONDATA_TYPE
            if (RECOGNITIONDATA_TYPE.equals(key)) {
                continue;
            }
            Object value = bundle.get(key);
            if (value instanceof String) {
                fields.put(key, (String) value);
            } else {
                Log.d(LOG_TAG, "Ignoring non string key '" + key + "'");
            }
        }

        JSONObject result = new JSONObject();
        result.put(RESULT_TYPE, "USDL result");
        result.put(FIELDS, fields);
        return result;
    }

    private String byteArrayToHex(byte[] data) {
        StringBuilder sb = new StringBuilder();
        for (byte b : data) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}