io.tradle.RNBlinkIDModule.java Source code

Java tutorial

Introduction

Here is the source code for io.tradle.RNBlinkIDModule.java

Source

package io.tradle;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Base64;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.microblink.activity.ScanCard;
import com.microblink.hardware.camera.CameraType;
import com.microblink.metadata.MetadataSettings;
import com.microblink.recognizers.BaseRecognitionResult;
import com.microblink.recognizers.IResultHolder;
import com.microblink.recognizers.RecognitionResults;
import com.microblink.recognizers.blinkbarcode.usdl.USDLRecognizerSettings;
import com.microblink.recognizers.blinkbarcode.usdl.USDLScanResult;
import com.microblink.recognizers.blinkid.eudl.EUDLCountry;
import com.microblink.recognizers.blinkid.eudl.EUDLRecognitionResult;
import com.microblink.recognizers.blinkid.eudl.EUDLRecognizerSettings;
import com.microblink.recognizers.blinkid.mrtd.MRTDRecognitionResult;
import com.microblink.recognizers.blinkid.mrtd.MRTDRecognizerSettings;
import com.microblink.recognizers.settings.RecognitionSettings;
import com.microblink.recognizers.settings.RecognizerSettings;
import com.microblink.recognizers.settings.RecognizerSettingsUtils;
import com.microblink.util.RecognizerCompatibility;
import com.microblink.util.RecognizerCompatibilityStatus;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class RNBlinkIDModule extends ReactContextBaseJavaModule implements LifecycleEventListener {

    private final ReactApplicationContext reactContext;
    private final String notSupportedBecause;
    private String licenseKey;
    private static final String E_USER_CANCELED = "RNMBUserCanceledError";
    private static final String E_FAILED_INVALID = "RNMBInvalidResultError";
    private static final String E_FAILED_EMPTY = "RNMBEmptyResultError";
    private static final String E_FAILED_NO_AUTOFOCUS = "RNMBNoAutofocusError";
    private static final String E_DEVELOPER_ERROR = "RNMBDeveloperError";
    private static final String JPEG_DATA_URI_PREFIX = "data:image/jpeg;base64,";
    private static final String TYPE_EUDL = "eudl";
    private static final String TYPE_MRTD = "mrtd";
    private static final String TYPE_USDL = "usdl";
    private static final String KEY_NOT_SUPPORTED_BECAUSE = "notSupportedBecause";
    private static final String KEY_PREFIX_SUPPORTS = "supports";
    private static final int SCAN_REQUEST_CODE = 5792151;
    private static final Map<EUDLCountry, String> eudlCountryToString;
    private static final Map<String, EUDLCountry> countryToEUDLCountry;
    private final Map<String, Object> constants = new HashMap<>();
    static {
        eudlCountryToString = new HashMap<>();
        countryToEUDLCountry = new HashMap<>();
        eudlCountryToString.put(EUDLCountry.EUDL_COUNTRY_UK, "UK");
        eudlCountryToString.put(EUDLCountry.EUDL_COUNTRY_GERMANY, "DE");
        eudlCountryToString.put(EUDLCountry.EUDL_COUNTRY_AUSTRIA, "AT");
        countryToEUDLCountry.put("UK", EUDLCountry.EUDL_COUNTRY_UK);
        countryToEUDLCountry.put("DE", EUDLCountry.EUDL_COUNTRY_GERMANY);
        countryToEUDLCountry.put("AT", EUDLCountry.EUDL_COUNTRY_AUSTRIA);
    }

    // scan session variables
    private ReadableMap opts;
    private Promise scanPromise;

    private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
        @Override
        public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
            if (requestCode != SCAN_REQUEST_CODE)
                return;
            if (resultCode == ScanCard.RESULT_CANCELED) {
                onCanceled();
                return;
            }

            if (resultCode != ScanCard.RESULT_OK || data == null) {
                resetForNextScan();
                return;
            }

            // perform processing of the data here

            // for example, obtain parcelable recognition result
            Bundle extras = data.getExtras();
            RecognitionResults scanResult = data.getParcelableExtra(ScanCard.EXTRAS_RECOGNITION_RESULTS);

            Map<String, Bitmap> images = RNBlinkIDImageManager.get();

            // get array of recognition results
            BaseRecognitionResult[] resultArray = scanResult.getRecognitionResults();
            WritableMap allResults = Arguments.createMap();
            boolean yay = false;
            boolean hasEmpty = false;
            boolean hasInvalid = false;
            for (BaseRecognitionResult baseResult : resultArray) {
                if (!baseResult.isValid()) {
                    hasInvalid = true;
                    continue;
                }

                if (baseResult.isEmpty()) {
                    hasEmpty = true;
                    continue;
                }

                String type = null;
                WritableMap personal = Arguments.createMap();
                WritableMap address = Arguments.createMap();
                WritableMap document = Arguments.createMap();
                WritableMap resultsMap = Arguments.createMap();
                if (baseResult instanceof EUDLRecognitionResult) {
                    type = TYPE_EUDL;
                    EUDLRecognitionResult result = (EUDLRecognitionResult) baseResult;
                    IResultHolder resultHolder = result.getResultHolder();
                    personal.putString("firstName", result.getFirstName());
                    personal.putString("lastName", result.getLastName());
                    personal.putString("placeOfBirth", result.getPlaceOfBirth());
                    //          personal.putDouble("dateOfBirth", result.getDateOfBirth().getTime());
                    personal.putString("birthData", result.getResultHolder().getString("ownerBirthData"));

                    //          document.putDouble("dateOfExpiry", result.getDocumentExpiryDate().getTime());
                    document.putString("dateOfExpiry", resultHolder.getString("documentExpiryDate"));
                    //          document.putDouble("dateOfIssue", result.getDocumentIssueDate().getTime());
                    document.putString("dateOfIssue", resultHolder.getString("documentIssueDate"));
                    document.putString("documentNumber", result.getDriverNumber());
                    document.putString("issuer", result.getDocumentIssuingAuthority());
                    EUDLCountry eudlCountry = result.getCountry();
                    if (eudlCountry != EUDLCountry.EUDL_COUNTRY_AUTO) {
                        document.putString("country", eudlCountryToString.get(eudlCountry));
                    }

                    address.putString("full", result.getAddress());
                } else if (baseResult instanceof USDLScanResult) {
                    USDLScanResult result = (USDLScanResult) baseResult;
                    if (result.isUncertain()) {
                        hasInvalid = true;
                        continue;
                    }

                    type = TYPE_USDL;
                    personal.putString("firstName", result.getField(USDLScanResult.kCustomerFirstName));
                    personal.putString("lastName", result.getField(USDLScanResult.kCustomerFamilyName));
                    personal.putString("fullName", result.getField(USDLScanResult.kCustomerFullName));
                    personal.putString("dateOfBirth", result.getField(USDLScanResult.kDateOfBirth));
                    String sex = result.getField(USDLScanResult.kSex).equalsIgnoreCase("1") ? "M" : "F";
                    personal.putString("kPPSex", sex);
                    personal.putString("eyeColor", result.getField(USDLScanResult.kEyeColor));
                    personal.putString("heightCm", result.getField(USDLScanResult.kHeight));

                    address.putString("full", result.getField(USDLScanResult.kFullAddress));
                    address.putString("street", result.getField(USDLScanResult.kAddressStreet));
                    address.putString("city", result.getField(USDLScanResult.kAddressCity));
                    address.putString("state", result.getField(USDLScanResult.kAddressJurisdictionCode));
                    address.putString("postalCode", result.getField(USDLScanResult.kAddressJurisdictionCode));

                    document.putString("dateOfIssue", result.getField(USDLScanResult.kDocumentIssueDate));
                    document.putString("dateOfExpiry", result.getField(USDLScanResult.kDocumentExpirationDate));
                    document.putString("issueIdentificationNumber",
                            result.getField(USDLScanResult.kIssuerIdentificationNumber));
                    document.putString("jurisdictionVersionNumber",
                            result.getField(USDLScanResult.kJurisdictionVersionNumber));
                    document.putString("jurisdictionVehicleClass",
                            result.getField(USDLScanResult.kJurisdictionVehicleClass));
                    document.putString("jurisdictionRestrictionCodes",
                            result.getField(USDLScanResult.kJurisdictionRestrictionCodes));
                    document.putString("jurisdictionEndorsementCodes",
                            result.getField(USDLScanResult.kJurisdictionEndorsementCodes));
                    document.putString("documentNumber", result.getField(USDLScanResult.kCustomerIdNumber));
                    // deprecated
                    document.putString("customerIdNumber", result.getField(USDLScanResult.kCustomerIdNumber));
                } else if (baseResult instanceof MRTDRecognitionResult) {
                    type = TYPE_MRTD;
                    MRTDRecognitionResult result = (MRTDRecognitionResult) baseResult;
                    if (result.isMRZParsed()) {
                        personal.putString("lastName", result.getPrimaryId());
                        personal.putString("firstName", result.getSecondaryId());
                        personal.putString("nationality", result.getNationality());
                        //            personal.putDouble("dateOfBirth", result.getDateOfBirth().getTime());
                        //            personal.putString("dateOfBirthStr", result.getRawDateOfBirth());
                        personal.putString("sex", result.getSex());

                        document.putString("issuer", result.getIssuer());
                        document.putString("documentNumber", result.getDocumentNumber());
                        document.putString("documentCode", result.getDocumentCode());
                        //            document.putDouble("dateOfExpiry", result.getDateOfExpiry().getTime());
                        //            document.putString("dateOfExpiryStr", result.getRawDateOfExpiry());
                        document.putString("opt1", result.getOpt1());
                        document.putString("opt2", result.getOpt2());
                        document.putString("mrzText", result.getMRZText());
                    } else {
                        // attempt to parse OCR result by yourself
                        // or ask user to try again
                        resultsMap.putString("ocr", result.getOcrResult().toString());
                    }
                }

                if (type == null)
                    continue;

                // only one result for now
                yay = true;
                resultsMap.putMap("personal", personal);
                resultsMap.putMap("address", address);
                resultsMap.putMap("document", document);
                allResults.putMap(type, resultsMap);
                Bitmap bitmap = images.get(type.toUpperCase());
                if (bitmap == null && type.equalsIgnoreCase(TYPE_USDL)) {
                    bitmap = images.get("Success");
                }

                if (bitmap != null) {
                    allResults.putMap("image", serializeImage(bitmap));
                }
            }

            if (yay) {
                scanPromise.resolve(allResults);
            } else if (hasInvalid) {
                // not all relevant data was scanned, ask user
                // to try again
                onInvalidResultError();
            } else if (hasEmpty) {
                // not all relevant data was scanned, ask user
                // to try again
                onEmptyError();
            } else {
                onDeveloperError();
                //        scanPromise.reject(E_DEVELOPER_ERROR, "This should not happen, please report to react-native-blinkid developers");
            }

            resetForNextScan();
        }
    };

    public RNBlinkIDModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
        reactContext.addActivityEventListener(mActivityEventListener);
        RecognizerCompatibilityStatus supportStatus = RecognizerCompatibility
                .getRecognizerCompatibilityStatus(reactContext);
        if (supportStatus != RecognizerCompatibilityStatus.RECOGNIZER_SUPPORTED) {
            this.notSupportedBecause = supportStatus.name();
            return;
        }

        this.notSupportedBecause = null;
        if (RecognizerCompatibility.cameraHasAutofocus(CameraType.CAMERA_BACKFACE, reactContext)) {
            supports(TYPE_EUDL, true);
            supports(TYPE_USDL, true);
            supports(TYPE_MRTD, true);
            return;
        }

        EUDLRecognizerSettings eudl = new EUDLRecognizerSettings(EUDLCountry.EUDL_COUNTRY_AUTO);
        USDLRecognizerSettings usdl = new USDLRecognizerSettings();
        MRTDRecognizerSettings mrtd = new MRTDRecognizerSettings();
        RecognizerSettings[] settings = new RecognizerSettings[] { eudl, usdl, mrtd };
        RecognizerSettings[] supported = RecognizerSettingsUtils.filterOutRecognizersThatRequireAutofocus(settings);
        for (RecognizerSettings setting : supported) {
            if (setting == eudl) {
                supports(TYPE_EUDL, true);
            } else if (setting == usdl) {
                supports(TYPE_USDL, true);
            } else if (setting == mrtd) {
                supports(TYPE_MRTD, true);
            }
        }
    }

    private void supports(String type, boolean does) {
        constants.put(KEY_PREFIX_SUPPORTS + type.toUpperCase(), does);
    }

    @Override
    public void onHostDestroy() {
        resetForNextScan();
    }

    @Override
    public void onHostPause() {
    }

    @Override
    public void onHostResume() {
    }

    @Override
    public String getName() {
        return "RNBlinkID";
    }

    @Override
    public Map<String, Object> getConstants() {
        if (this.notSupportedBecause != null) {
            constants.put(KEY_NOT_SUPPORTED_BECAUSE, this.notSupportedBecause);
        }

        return constants;
    }

    @ReactMethod
    public void setLicenseKey(String licenseKey, final Promise promise) {
        this.licenseKey = licenseKey;
        promise.resolve(null);
    }

    //  @ReactMethod
    //  public void dismiss(final Promise promise) {
    //    resetForNextScan();
    //    promise.resolve(null);
    //  }

    @ReactMethod
    public void scan(ReadableMap opts, final Promise promise) {
        String licenseKey = getString(opts, "licenseKey");
        if (licenseKey == null) {
            licenseKey = this.licenseKey;
        }

        resetForNextScan();
        this.scanPromise = promise;
        this.opts = opts;

        Activity currentActivity = getCurrentActivity();
        Intent intent = new Intent(currentActivity, ScanCard.class);
        intent.putExtra(ScanCard.EXTRAS_LICENSE_KEY, licenseKey);
        intent.putExtra(ScanCard.EXTRAS_CAMERA_TYPE, (Parcelable) CameraType.CAMERA_BACKFACE);

        RecognitionSettings settings = new RecognitionSettings();
        RecognizerSettings[] recognizerSettings = getRecognitionSettings(opts);
        if (!RecognizerCompatibility.cameraHasAutofocus(CameraType.CAMERA_BACKFACE, reactContext)) {
            int length = recognizerSettings.length;
            recognizerSettings = RecognizerSettingsUtils
                    .filterOutRecognizersThatRequireAutofocus(recognizerSettings);
            if (recognizerSettings.length != length) {
                reject(E_FAILED_NO_AUTOFOCUS);
                return;
            }
        }

        settings.setRecognizerSettingsArray(recognizerSettings);
        if (opts.hasKey("timeout")) {
            settings.setNumMsBeforeTimeout(opts.getInt("timeout"));
        }

        intent.putExtra(ScanCard.EXTRAS_RECOGNITION_SETTINGS, settings);
        // pass implementation of image listener that will obtain document images
        intent.putExtra(ScanCard.EXTRAS_IMAGE_LISTENER, new MyImageListener());
        // pass image metadata settings that specifies which images will be obtained
        intent.putExtra(ScanCard.EXTRAS_IMAGE_METADATA_SETTINGS, getImageMetadataSettings(opts));

        // Starting Activity
        currentActivity.startActivityForResult(intent, SCAN_REQUEST_CODE);
    }

    private void onCanceled() {
        reject(E_USER_CANCELED);
        resetForNextScan();
    }

    private void onInvalidResultError() {
        reject(E_FAILED_INVALID);
        resetForNextScan();
    }

    private void onEmptyError() {
        reject(E_FAILED_EMPTY);
        resetForNextScan();
    }

    private void onDeveloperError() {
        reject(E_DEVELOPER_ERROR);
        resetForNextScan();
    }

    private void reject(String message) {
        if (scanPromise != null) {
            scanPromise.reject(null, message);
        }
    }

    public void resetForNextScan() {
        scanPromise = null;
        opts = null;
        RNBlinkIDImageManager.clear();
    }

    private String getString(ReadableMap map, String key) {
        return map.hasKey(key) ? map.getString(key) : null;
    }

    private boolean getBoolean(ReadableMap map, String key) {
        return map.hasKey(key) ? map.getBoolean(key) : false;
    }

    private MetadataSettings.ImageMetadataSettings getImageMetadataSettings(ReadableMap opts) {
        MetadataSettings.ImageMetadataSettings ims = new MetadataSettings.ImageMetadataSettings();
        String imagePath = getString(opts, "imagePath");
        boolean outputBase64 = getBoolean(opts, "base64");
        boolean needImage = imagePath != null || outputBase64;
        if (needImage) {
            // enable returning of dewarped images, if they are available
            ims.setDewarpedImageEnabled(true);
            // enable returning of image that was used to obtain valid scanning result
            if (opts.hasKey(TYPE_USDL)) {
                ims.setSuccessfulScanFrameEnabled(true);
            }
        }

        return ims;
    }

    private RecognizerSettings[] getRecognitionSettings(ReadableMap opts) {
        // TODO: interpret actual settings
        ArrayList<RecognizerSettings> settings = new ArrayList<>();

        if (opts.hasKey(TYPE_USDL)) {
            USDLRecognizerSettings sett = new USDLRecognizerSettings();
            sett.setUncertainScanning(false);
            // disable scanning of barcodes that do not have quiet zone
            // as defined by the standard
            sett.setNullQuietZoneAllowed(false);
            settings.add(sett);
        }

        if (opts.hasKey(TYPE_EUDL)) {
            ReadableMap eudlOpts = opts.getMap(TYPE_EUDL);
            EUDLCountry country = null;
            String countryStr = getString(opts, "issuer");
            if (countryStr != null) {
                countryStr = countryStr.toUpperCase();
                country = countryToEUDLCountry.get(countryStr);
            }

            if (country == null)
                country = EUDLCountry.EUDL_COUNTRY_AUTO;

            EUDLRecognizerSettings sett = new EUDLRecognizerSettings(country);
            sett.setShowFullDocument(getBoolean(eudlOpts, "showFullDocument"));
            settings.add(sett);
        }

        if (opts.hasKey(TYPE_MRTD)) {
            ReadableMap mrtdOpts = opts.getMap(TYPE_MRTD);
            MRTDRecognizerSettings sett = new MRTDRecognizerSettings();
            sett.setShowFullDocument(getBoolean(mrtdOpts, "showFullDocument"));
            settings.add(sett);
        }

        return settings.toArray(new RecognizerSettings[settings.size()]);
    }

    private WritableMap serializeImage(Bitmap bitmap) {
        WritableMap image = Arguments.createMap();
        int quality = 100;
        if (opts.hasKey("quality")) {
            quality = (int) (opts.getDouble("quality") * 100);
        }

        ByteArrayOutputStream bytes = getBytes(bitmap, quality);
        String imagePath = getString(opts, "imagePath");
        if (imagePath != null) {
            try {
                OutputStream outputStream = new FileOutputStream(imagePath);
                bytes.writeTo(outputStream);
            } catch (IOException i) {
                image.putString("error", i.getLocalizedMessage());
            }
        }

        if (getBoolean(opts, "base64")) {
            String base64 = toBase64(bytes);
            image.putString("base64", base64);
            // original width/height, ignoring compression
            image.putInt("width", bitmap.getWidth());
            image.putInt("height", bitmap.getHeight());
        }

        return image;
    }

    private static ByteArrayOutputStream getBytes(final Bitmap bitmap, final int quality) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream);
        return byteArrayOutputStream;
    }

    private static String toBase64(final ByteArrayOutputStream byteArrayOutputStream) {
        byte[] byteArray = byteArrayOutputStream.toByteArray();
        return JPEG_DATA_URI_PREFIX + Base64.encodeToString(byteArray, Base64.NO_WRAP);
    }
}