diff --git a/android/src/main/java/org/reactnative/barcodedetector/BarcodeFormatUtils.java b/android/src/main/java/org/reactnative/barcodedetector/BarcodeFormatUtils.java new file mode 100644 index 0000000..8c102e5 --- /dev/null +++ b/android/src/main/java/org/reactnative/barcodedetector/BarcodeFormatUtils.java @@ -0,0 +1,58 @@ +package org.reactnative.barcodedetector; + +import android.util.SparseArray; +import com.google.android.gms.vision.barcode.Barcode; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class BarcodeFormatUtils { + + public static final SparseArray FORMATS; + public static final Map REVERSE_FORMATS; + + private static final String UNKNOWN_FORMAT_STRING = "UNKNOWN_FORMAT"; + private static final int UNKNOWN_FORMAT_INT = -1; + + static { + // Initialize integer to string map + SparseArray map = new SparseArray<>(); + map.put(Barcode.CODE_128, "CODE_128"); + map.put(Barcode.CODE_39, "CODE_39"); + map.put(Barcode.CODE_93, "CODE_93"); + map.put(Barcode.CODABAR, "CODABAR"); + map.put(Barcode.DATA_MATRIX, "DATA_MATRIX"); + map.put(Barcode.EAN_13, "EAN_13"); + map.put(Barcode.EAN_8, "EAN_8"); + map.put(Barcode.ITF, "ITF"); + map.put(Barcode.QR_CODE, "QR_CODE"); + map.put(Barcode.UPC_A, "UPC_A"); + map.put(Barcode.UPC_E, "UPC_E"); + map.put(Barcode.PDF417, "PDF417"); + map.put(Barcode.AZTEC, "AZTEC"); + FORMATS = map; + + + // Initialize string to integer map + Map rmap = new HashMap<>(); + for (int i = 0; i < map.size(); i++) { + rmap.put(map.valueAt(i), map.keyAt(i)); + } + + rmap.put("ALL", 0); + REVERSE_FORMATS = Collections.unmodifiableMap(rmap); + } + + public static String get(int format) { + return FORMATS.get(format, UNKNOWN_FORMAT_STRING); + } + + public static int get(String format) { + if (REVERSE_FORMATS.containsKey(format)) { + return REVERSE_FORMATS.get(format); + } + + return UNKNOWN_FORMAT_INT; + } +} diff --git a/android/src/main/java/org/reactnative/barcodedetector/RNBarcodeDetector.java b/android/src/main/java/org/reactnative/barcodedetector/RNBarcodeDetector.java new file mode 100644 index 0000000..8156089 --- /dev/null +++ b/android/src/main/java/org/reactnative/barcodedetector/RNBarcodeDetector.java @@ -0,0 +1,75 @@ +package org.reactnative.barcodedetector; + +import android.content.Context; +import android.util.SparseArray; +import com.google.android.gms.vision.barcode.Barcode; +import com.google.android.gms.vision.barcode.BarcodeDetector; +import org.reactnative.camera.utils.ImageDimensions; +import org.reactnative.frame.RNFrame; + +public class RNBarcodeDetector { + + private BarcodeDetector mBarcodeDetector = null; + private ImageDimensions mPreviousDimensions; + private BarcodeDetector.Builder mBuilder; + + private int mBarcodeType = Barcode.ALL_FORMATS; + + public RNBarcodeDetector(Context context) { + mBuilder = new BarcodeDetector.Builder(context) + .setBarcodeFormats(mBarcodeType); + } + + // Public API + + public boolean isOperational() { + if (mBarcodeDetector == null) { + createBarcodeDetector(); + } + + return mBarcodeDetector.isOperational(); + } + + public SparseArray detect(RNFrame frame) { + // If the frame has different dimensions, create another barcode detector. + // Otherwise we will most likely get nasty "inconsistent image dimensions" error from detector + // and no barcode will be detected. + if (!frame.getDimensions().equals(mPreviousDimensions)) { + releaseBarcodeDetector(); + } + + if (mBarcodeDetector == null) { + createBarcodeDetector(); + mPreviousDimensions = frame.getDimensions(); + } + + return mBarcodeDetector.detect(frame.getFrame()); + } + + public void setBarcodeType(int barcodeType) { + if (barcodeType != mBarcodeType) { + release(); + mBuilder.setBarcodeFormats(barcodeType); + mBarcodeType = barcodeType; + } + } + + + public void release() { + releaseBarcodeDetector(); + mPreviousDimensions = null; + } + + // Lifecycle methods + + private void releaseBarcodeDetector() { + if (mBarcodeDetector != null) { + mBarcodeDetector.release(); + mBarcodeDetector = null; + } + } + + private void createBarcodeDetector() { + mBarcodeDetector = mBuilder.build(); + } +} diff --git a/android/src/main/java/org/reactnative/camera/CameraModule.java b/android/src/main/java/org/reactnative/camera/CameraModule.java index 8f9499a..610db10 100644 --- a/android/src/main/java/org/reactnative/camera/CameraModule.java +++ b/android/src/main/java/org/reactnative/camera/CameraModule.java @@ -2,35 +2,25 @@ package org.reactnative.camera; import android.graphics.Bitmap; import android.os.Build; - -import com.facebook.react.bridge.Arguments; -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.WritableArray; +import com.facebook.react.bridge.*; import com.facebook.react.uimanager.NativeViewHierarchyManager; import com.facebook.react.uimanager.UIBlock; import com.facebook.react.uimanager.UIManagerModule; import com.google.android.cameraview.AspectRatio; import com.google.zxing.BarcodeFormat; - +import org.reactnative.barcodedetector.BarcodeFormatUtils; import org.reactnative.camera.tasks.ResolveTakenPictureAsyncTask; import org.reactnative.camera.utils.ScopedContext; import org.reactnative.facedetector.RNFaceDetector; +import javax.annotation.Nullable; +import java.io.ByteArrayOutputStream; import java.io.File; -import java.nio.ByteBuffer; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.io.ByteArrayOutputStream; - -import javax.annotation.Nullable; - public class CameraModule extends ReactContextBaseJavaModule { private static final String TAG = "CameraModule"; @@ -123,6 +113,11 @@ public class CameraModule extends ReactContextBaseJavaModule { }); } })); + put("GoogleVisionBarcodeDetection", Collections.unmodifiableMap(new HashMap() { + { + put("BarcodeType", BarcodeFormatUtils.REVERSE_FORMATS); + } + })); } private Map getTypeConstants() { diff --git a/android/src/main/java/org/reactnative/camera/CameraViewManager.java b/android/src/main/java/org/reactnative/camera/CameraViewManager.java index 7dfbf7e..ba70dc1 100644 --- a/android/src/main/java/org/reactnative/camera/CameraViewManager.java +++ b/android/src/main/java/org/reactnative/camera/CameraViewManager.java @@ -1,7 +1,6 @@ package org.reactnative.camera; import android.support.annotation.Nullable; - import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.ThemedReactContext; @@ -19,6 +18,7 @@ public class CameraViewManager extends ViewGroupManager { EVENT_ON_MOUNT_ERROR("onMountError"), EVENT_ON_BAR_CODE_READ("onBarCodeRead"), EVENT_ON_FACES_DETECTED("onFacesDetected"), + EVENT_ON_BARCODES_DETECTED("onGoogleVisionBarcodesDetected"), EVENT_ON_FACE_DETECTION_ERROR("onFaceDetectionError"), EVENT_ON_TEXT_RECOGNIZED("onTextRecognized"); @@ -145,6 +145,16 @@ public class CameraViewManager extends ViewGroupManager { view.setFaceDetectionClassifications(classifications); } + @ReactProp(name = "googleVisionBarcodeDetectorEnabled") + public void setGoogleVisionBarcodeDetecting(RNCameraView view, boolean barcodeDetectorEnabled) { + view.setShouldDetectBarcodes(barcodeDetectorEnabled); + } + + @ReactProp(name = "googleVisionBarcodeType") + public void setGoogleVisionBarcodeType(RNCameraView view, int barcodeType) { + view.setGoogleVisionBarcodeType(barcodeType); + } + @ReactProp(name = "textRecognizerEnabled") public void setTextRecognizing(RNCameraView view, boolean textRecognizerEnabled) { view.setShouldRecognizeText(textRecognizerEnabled); diff --git a/android/src/main/java/org/reactnative/camera/RNCameraPackage.java b/android/src/main/java/org/reactnative/camera/RNCameraPackage.java index ccdbd43..a72a71b 100644 --- a/android/src/main/java/org/reactnative/camera/RNCameraPackage.java +++ b/android/src/main/java/org/reactnative/camera/RNCameraPackage.java @@ -8,8 +8,6 @@ import com.facebook.react.uimanager.ViewManager; import com.lwansbrough.RCTCamera.RCTCameraModule; import com.lwansbrough.RCTCamera.RCTCameraViewManager; -import org.reactnative.camera.CameraModule; -import org.reactnative.camera.CameraViewManager; import org.reactnative.facedetector.FaceDetectorModule; import java.util.Arrays; diff --git a/android/src/main/java/org/reactnative/camera/RNCameraView.java b/android/src/main/java/org/reactnative/camera/RNCameraView.java index 90bccf9..1f6f3fd 100644 --- a/android/src/main/java/org/reactnative/camera/RNCameraView.java +++ b/android/src/main/java/org/reactnative/camera/RNCameraView.java @@ -9,46 +9,31 @@ import android.os.Build; import android.support.v4.content.ContextCompat; import android.util.SparseArray; import android.view.View; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.LifecycleEventListener; -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.*; import com.facebook.react.uimanager.ThemedReactContext; import com.google.android.cameraview.CameraView; +import com.google.android.gms.vision.barcode.Barcode; import com.google.android.gms.vision.face.Face; -import com.google.android.gms.vision.text.Text; import com.google.android.gms.vision.text.TextBlock; import com.google.android.gms.vision.text.TextRecognizer; import com.google.zxing.BarcodeFormat; import com.google.zxing.DecodeHintType; import com.google.zxing.MultiFormatReader; import com.google.zxing.Result; - -import org.reactnative.camera.tasks.BarCodeScannerAsyncTask; -import org.reactnative.camera.tasks.BarCodeScannerAsyncTaskDelegate; -import org.reactnative.camera.tasks.FaceDetectorAsyncTask; -import org.reactnative.camera.tasks.FaceDetectorAsyncTaskDelegate; -import org.reactnative.camera.tasks.ResolveTakenPictureAsyncTask; -import org.reactnative.camera.tasks.TextRecognizerAsyncTask; -import org.reactnative.camera.tasks.TextRecognizerAsyncTaskDelegate; +import org.reactnative.camera.tasks.*; import org.reactnative.camera.utils.ImageDimensions; import org.reactnative.camera.utils.RNFileUtils; +import org.reactnative.barcodedetector.RNBarcodeDetector; import org.reactnative.facedetector.RNFaceDetector; import java.io.File; import java.io.IOException; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Queue; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; public class RNCameraView extends CameraView implements LifecycleEventListener, BarCodeScannerAsyncTaskDelegate, FaceDetectorAsyncTaskDelegate, - TextRecognizerAsyncTaskDelegate { + BarcodeDetectorAsyncTaskDelegate, TextRecognizerAsyncTaskDelegate { private ThemedReactContext mThemedReactContext; private Queue mPictureTakenPromises = new ConcurrentLinkedQueue<>(); private Map mPictureTakenOptions = new ConcurrentHashMap<>(); @@ -63,18 +48,22 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, // Concurrency lock for scanners to avoid flooding the runtime public volatile boolean barCodeScannerTaskLock = false; public volatile boolean faceDetectorTaskLock = false; + public volatile boolean barcodeDetectorTaskLock = false; public volatile boolean textRecognizerTaskLock = false; // Scanning-related properties private final MultiFormatReader mMultiFormatReader = new MultiFormatReader(); private final RNFaceDetector mFaceDetector; + private final RNBarcodeDetector mBarcodeDetector; private final TextRecognizer mTextRecognizer; private boolean mShouldDetectFaces = false; + private boolean mShouldDetectBarcodes = false; private boolean mShouldScanBarCodes = false; private boolean mShouldRecognizeText = false; private int mFaceDetectorMode = RNFaceDetector.FAST_MODE; private int mFaceDetectionLandmarks = RNFaceDetector.NO_LANDMARKS; private int mFaceDetectionClassifications = RNFaceDetector.NO_CLASSIFICATIONS; + private int mGoogleVisionBarCodeType = Barcode.ALL_FORMATS; public RNCameraView(ThemedReactContext themedReactContext) { super(themedReactContext, true); @@ -82,6 +71,8 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, mThemedReactContext = themedReactContext; mFaceDetector = new RNFaceDetector(themedReactContext); setupFaceDetector(); + mBarcodeDetector = new RNBarcodeDetector(themedReactContext); + setupBarcodeDetector(); mTextRecognizer = new TextRecognizer.Builder(themedReactContext).build(); themedReactContext.addLifecycleEventListener(this); @@ -118,7 +109,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, } } - private byte[] rotateImage(byte[]imageData,int height,int width){ + private byte[] rotateImage(byte[] imageData, int height, int width) { byte[] rotated = new byte[imageData.length]; for (int y = 0; y < width; y++) { for (int x = 0; x < height; x++) { @@ -131,13 +122,13 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, @Override public void onFramePreview(CameraView cameraView, byte[] data, int width, int height, int rotation) { int correctRotation = RNCameraViewHelper.getCorrectCameraRotation(rotation, getFacing()); - int correctWidth=width; - int correctHeight=height; - byte[] correctData=data; - if(correctRotation==90){ - correctWidth=height; - correctHeight=width; - correctData=rotateImage(data,correctHeight,correctWidth); + int correctWidth = width; + int correctHeight = height; + byte[] correctData = data; + if (correctRotation == 90) { + correctWidth = height; + correctHeight = width; + correctData = rotateImage(data, correctHeight, correctWidth); } if (mShouldScanBarCodes && !barCodeScannerTaskLock && cameraView instanceof BarCodeScannerAsyncTaskDelegate) { barCodeScannerTaskLock = true; @@ -151,6 +142,12 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, new FaceDetectorAsyncTask(delegate, mFaceDetector, correctData, correctWidth, correctHeight, correctRotation).execute(); } + if (mShouldDetectBarcodes && !barcodeDetectorTaskLock && cameraView instanceof BarcodeDetectorAsyncTaskDelegate) { + barcodeDetectorTaskLock = true; + BarcodeDetectorAsyncTaskDelegate delegate = (BarcodeDetectorAsyncTaskDelegate) cameraView; + new BarcodeDetectorAsyncTask(delegate, mBarcodeDetector, correctData, correctWidth, correctHeight, correctRotation).execute(); + } + if (mShouldRecognizeText && !textRecognizerTaskLock && cameraView instanceof TextRecognizerAsyncTaskDelegate) { textRecognizerTaskLock = true; TextRecognizerAsyncTaskDelegate delegate = (TextRecognizerAsyncTaskDelegate) cameraView; @@ -177,11 +174,11 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, correctWidth = (int) width; } else { correctHeight = (int) height; - correctWidth = (int)(height*ratio); + correctWidth = (int) (height * ratio); } int paddingX = (int) ((width - correctWidth) / 2); int paddingY = (int) ((height - correctHeight) / 2); - preview.layout(paddingX, paddingY, correctWidth+paddingX, correctHeight+paddingY); + preview.layout(paddingX, paddingY, correctWidth + paddingX, correctHeight + paddingY); } @Override @@ -265,7 +262,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, public void setShouldScanBarCodes(boolean shouldScanBarCodes) { this.mShouldScanBarCodes = shouldScanBarCodes; - setScanning(mShouldDetectFaces || mShouldScanBarCodes || mShouldRecognizeText); + setScanning(mShouldDetectFaces || mShouldDetectBarcodes || mShouldScanBarCodes || mShouldRecognizeText); } public void onBarCodeRead(Result barCode) { @@ -315,7 +312,12 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, public void setShouldDetectFaces(boolean shouldDetectFaces) { this.mShouldDetectFaces = shouldDetectFaces; - setScanning(mShouldDetectFaces || mShouldScanBarCodes || mShouldRecognizeText); + setScanning(mShouldDetectFaces || mShouldDetectBarcodes || mShouldScanBarCodes || mShouldRecognizeText); + } + + public void setShouldDetectBarcodes(boolean shouldDetectBarcodes) { + this.mShouldDetectBarcodes = shouldDetectBarcodes; + setScanning(mShouldDetectFaces || mShouldDetectBarcodes || mShouldScanBarCodes || mShouldRecognizeText); } public void onFacesDetected(SparseArray facesReported, int sourceWidth, int sourceHeight, int sourceRotation) { @@ -342,9 +344,46 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, faceDetectorTaskLock = false; } + /** + * Initial setup of the barcode detector + */ + private void setupBarcodeDetector() { + mBarcodeDetector.setBarcodeType(mGoogleVisionBarCodeType); + } + + public void setGoogleVisionBarcodeType(int barcodeType) { + mGoogleVisionBarCodeType = barcodeType; + if (mBarcodeDetector != null) { + mBarcodeDetector.setBarcodeType(barcodeType); + } + } + + public void onBarcodesDetected(SparseArray barcodesReported, int sourceWidth, int sourceHeight, int sourceRotation) { + if (!mShouldDetectBarcodes) { + return; + } + + SparseArray barcodesDetected = barcodesReported == null ? new SparseArray() : barcodesReported; + + RNCameraViewHelper.emitBarcodesDetectedEvent(this, barcodesDetected); + } + + public void onBarcodeDetectionError(RNBarcodeDetector barcodeDetector) { + if (!mShouldDetectBarcodes) { + return; + } + + RNCameraViewHelper.emitBarcodeDetectionErrorEvent(this, barcodeDetector); + } + + @Override + public void onBarcodeDetectingTaskCompleted() { + barcodeDetectorTaskLock = false; + } + public void setShouldRecognizeText(boolean shouldRecognizeText) { this.mShouldRecognizeText = shouldRecognizeText; - setScanning(mShouldDetectFaces || mShouldScanBarCodes || mShouldRecognizeText); + setScanning(mShouldDetectFaces || mShouldDetectBarcodes || mShouldScanBarCodes || mShouldRecognizeText); } @Override @@ -392,6 +431,7 @@ public class RNCameraView extends CameraView implements LifecycleEventListener, @Override public void onHostDestroy() { mFaceDetector.release(); + mBarcodeDetector.release(); stop(); } diff --git a/android/src/main/java/org/reactnative/camera/RNCameraViewHelper.java b/android/src/main/java/org/reactnative/camera/RNCameraViewHelper.java index 93d963d..ffbda16 100644 --- a/android/src/main/java/org/reactnative/camera/RNCameraViewHelper.java +++ b/android/src/main/java/org/reactnative/camera/RNCameraViewHelper.java @@ -9,161 +9,155 @@ import android.os.Build; import android.support.media.ExifInterface; import android.util.SparseArray; import android.view.ViewGroup; - import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.UIManagerModule; import com.google.android.cameraview.CameraView; +import com.google.android.gms.vision.barcode.Barcode; import com.google.android.gms.vision.face.Face; import com.google.android.gms.vision.text.TextBlock; import com.google.zxing.Result; - -import org.reactnative.camera.events.BarCodeReadEvent; -import org.reactnative.camera.events.CameraMountErrorEvent; -import org.reactnative.camera.events.CameraReadyEvent; -import org.reactnative.camera.events.FaceDetectionErrorEvent; -import org.reactnative.camera.events.FacesDetectedEvent; -import org.reactnative.camera.events.TextRecognizedEvent; +import org.reactnative.camera.events.*; import org.reactnative.camera.utils.ImageDimensions; +import org.reactnative.barcodedetector.RNBarcodeDetector; import org.reactnative.facedetector.RNFaceDetector; import java.text.SimpleDateFormat; import java.util.Calendar; -import java.util.Locale; public class RNCameraViewHelper { public static final String[][] exifTags = new String[][]{ - {"string", ExifInterface.TAG_ARTIST}, - {"int", ExifInterface.TAG_BITS_PER_SAMPLE}, - {"int", ExifInterface.TAG_COMPRESSION}, - {"string", ExifInterface.TAG_COPYRIGHT}, - {"string", ExifInterface.TAG_DATETIME}, - {"string", ExifInterface.TAG_IMAGE_DESCRIPTION}, - {"int", ExifInterface.TAG_IMAGE_LENGTH}, - {"int", ExifInterface.TAG_IMAGE_WIDTH}, - {"int", ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT}, - {"int", ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH}, - {"string", ExifInterface.TAG_MAKE}, - {"string", ExifInterface.TAG_MODEL}, - {"int", ExifInterface.TAG_ORIENTATION}, - {"int", ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION}, - {"int", ExifInterface.TAG_PLANAR_CONFIGURATION}, - {"double", ExifInterface.TAG_PRIMARY_CHROMATICITIES}, - {"double", ExifInterface.TAG_REFERENCE_BLACK_WHITE}, - {"int", ExifInterface.TAG_RESOLUTION_UNIT}, - {"int", ExifInterface.TAG_ROWS_PER_STRIP}, - {"int", ExifInterface.TAG_SAMPLES_PER_PIXEL}, - {"string", ExifInterface.TAG_SOFTWARE}, - {"int", ExifInterface.TAG_STRIP_BYTE_COUNTS}, - {"int", ExifInterface.TAG_STRIP_OFFSETS}, - {"int", ExifInterface.TAG_TRANSFER_FUNCTION}, - {"double", ExifInterface.TAG_WHITE_POINT}, - {"double", ExifInterface.TAG_X_RESOLUTION}, - {"double", ExifInterface.TAG_Y_CB_CR_COEFFICIENTS}, - {"int", ExifInterface.TAG_Y_CB_CR_POSITIONING}, - {"int", ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING}, - {"double", ExifInterface.TAG_Y_RESOLUTION}, - {"double", ExifInterface.TAG_APERTURE_VALUE}, - {"double", ExifInterface.TAG_BRIGHTNESS_VALUE}, - {"string", ExifInterface.TAG_CFA_PATTERN}, - {"int", ExifInterface.TAG_COLOR_SPACE}, - {"string", ExifInterface.TAG_COMPONENTS_CONFIGURATION}, - {"double", ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL}, - {"int", ExifInterface.TAG_CONTRAST}, - {"int", ExifInterface.TAG_CUSTOM_RENDERED}, - {"string", ExifInterface.TAG_DATETIME_DIGITIZED}, - {"string", ExifInterface.TAG_DATETIME_ORIGINAL}, - {"string", ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION}, - {"double", ExifInterface.TAG_DIGITAL_ZOOM_RATIO}, - {"string", ExifInterface.TAG_EXIF_VERSION}, - {"double", ExifInterface.TAG_EXPOSURE_BIAS_VALUE}, - {"double", ExifInterface.TAG_EXPOSURE_INDEX}, - {"int", ExifInterface.TAG_EXPOSURE_MODE}, - {"int", ExifInterface.TAG_EXPOSURE_PROGRAM}, - {"double", ExifInterface.TAG_EXPOSURE_TIME}, - {"double", ExifInterface.TAG_F_NUMBER}, - {"string", ExifInterface.TAG_FILE_SOURCE}, - {"int", ExifInterface.TAG_FLASH}, - {"double", ExifInterface.TAG_FLASH_ENERGY}, - {"string", ExifInterface.TAG_FLASHPIX_VERSION}, - {"double", ExifInterface.TAG_FOCAL_LENGTH}, - {"int", ExifInterface.TAG_FOCAL_LENGTH_IN_35MM_FILM}, - {"int", ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT}, - {"double", ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION}, - {"double", ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION}, - {"int", ExifInterface.TAG_GAIN_CONTROL}, - {"int", ExifInterface.TAG_ISO_SPEED_RATINGS}, - {"string", ExifInterface.TAG_IMAGE_UNIQUE_ID}, - {"int", ExifInterface.TAG_LIGHT_SOURCE}, - {"string", ExifInterface.TAG_MAKER_NOTE}, - {"double", ExifInterface.TAG_MAX_APERTURE_VALUE}, - {"int", ExifInterface.TAG_METERING_MODE}, - {"int", ExifInterface.TAG_NEW_SUBFILE_TYPE}, - {"string", ExifInterface.TAG_OECF}, - {"int", ExifInterface.TAG_PIXEL_X_DIMENSION}, - {"int", ExifInterface.TAG_PIXEL_Y_DIMENSION}, - {"string", ExifInterface.TAG_RELATED_SOUND_FILE}, - {"int", ExifInterface.TAG_SATURATION}, - {"int", ExifInterface.TAG_SCENE_CAPTURE_TYPE}, - {"string", ExifInterface.TAG_SCENE_TYPE}, - {"int", ExifInterface.TAG_SENSING_METHOD}, - {"int", ExifInterface.TAG_SHARPNESS}, - {"double", ExifInterface.TAG_SHUTTER_SPEED_VALUE}, - {"string", ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE}, - {"string", ExifInterface.TAG_SPECTRAL_SENSITIVITY}, - {"int", ExifInterface.TAG_SUBFILE_TYPE}, - {"string", ExifInterface.TAG_SUBSEC_TIME}, - {"string", ExifInterface.TAG_SUBSEC_TIME_DIGITIZED}, - {"string", ExifInterface.TAG_SUBSEC_TIME_ORIGINAL}, - {"int", ExifInterface.TAG_SUBJECT_AREA}, - {"double", ExifInterface.TAG_SUBJECT_DISTANCE}, - {"int", ExifInterface.TAG_SUBJECT_DISTANCE_RANGE}, - {"int", ExifInterface.TAG_SUBJECT_LOCATION}, - {"string", ExifInterface.TAG_USER_COMMENT}, - {"int", ExifInterface.TAG_WHITE_BALANCE}, - {"int", ExifInterface.TAG_GPS_ALTITUDE_REF}, - {"string", ExifInterface.TAG_GPS_AREA_INFORMATION}, - {"double", ExifInterface.TAG_GPS_DOP}, - {"string", ExifInterface.TAG_GPS_DATESTAMP}, - {"double", ExifInterface.TAG_GPS_DEST_BEARING}, - {"string", ExifInterface.TAG_GPS_DEST_BEARING_REF}, - {"double", ExifInterface.TAG_GPS_DEST_DISTANCE}, - {"string", ExifInterface.TAG_GPS_DEST_DISTANCE_REF}, - {"double", ExifInterface.TAG_GPS_DEST_LATITUDE}, - {"string", ExifInterface.TAG_GPS_DEST_LATITUDE_REF}, - {"double", ExifInterface.TAG_GPS_DEST_LONGITUDE}, - {"string", ExifInterface.TAG_GPS_DEST_LONGITUDE_REF}, - {"int", ExifInterface.TAG_GPS_DIFFERENTIAL}, - {"double", ExifInterface.TAG_GPS_IMG_DIRECTION}, - {"string", ExifInterface.TAG_GPS_IMG_DIRECTION_REF}, - {"string", ExifInterface.TAG_GPS_LATITUDE_REF}, - {"string", ExifInterface.TAG_GPS_LONGITUDE_REF}, - {"string", ExifInterface.TAG_GPS_MAP_DATUM}, - {"string", ExifInterface.TAG_GPS_MEASURE_MODE}, - {"string", ExifInterface.TAG_GPS_PROCESSING_METHOD}, - {"string", ExifInterface.TAG_GPS_SATELLITES}, - {"double", ExifInterface.TAG_GPS_SPEED}, - {"string", ExifInterface.TAG_GPS_SPEED_REF}, - {"string", ExifInterface.TAG_GPS_STATUS}, - {"string", ExifInterface.TAG_GPS_TIMESTAMP}, - {"double", ExifInterface.TAG_GPS_TRACK}, - {"string", ExifInterface.TAG_GPS_TRACK_REF}, - {"string", ExifInterface.TAG_GPS_VERSION_ID}, - {"string", ExifInterface.TAG_INTEROPERABILITY_INDEX}, - {"int", ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH}, - {"int", ExifInterface.TAG_THUMBNAIL_IMAGE_WIDTH}, - {"int", ExifInterface.TAG_DNG_VERSION}, - {"int", ExifInterface.TAG_DEFAULT_CROP_SIZE}, - {"int", ExifInterface.TAG_ORF_PREVIEW_IMAGE_START}, - {"int", ExifInterface.TAG_ORF_PREVIEW_IMAGE_LENGTH}, - {"int", ExifInterface.TAG_ORF_ASPECT_FRAME}, - {"int", ExifInterface.TAG_RW2_SENSOR_BOTTOM_BORDER}, - {"int", ExifInterface.TAG_RW2_SENSOR_LEFT_BORDER}, - {"int", ExifInterface.TAG_RW2_SENSOR_RIGHT_BORDER}, - {"int", ExifInterface.TAG_RW2_SENSOR_TOP_BORDER}, - {"int", ExifInterface.TAG_RW2_ISO}, + {"string", ExifInterface.TAG_ARTIST}, + {"int", ExifInterface.TAG_BITS_PER_SAMPLE}, + {"int", ExifInterface.TAG_COMPRESSION}, + {"string", ExifInterface.TAG_COPYRIGHT}, + {"string", ExifInterface.TAG_DATETIME}, + {"string", ExifInterface.TAG_IMAGE_DESCRIPTION}, + {"int", ExifInterface.TAG_IMAGE_LENGTH}, + {"int", ExifInterface.TAG_IMAGE_WIDTH}, + {"int", ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT}, + {"int", ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH}, + {"string", ExifInterface.TAG_MAKE}, + {"string", ExifInterface.TAG_MODEL}, + {"int", ExifInterface.TAG_ORIENTATION}, + {"int", ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION}, + {"int", ExifInterface.TAG_PLANAR_CONFIGURATION}, + {"double", ExifInterface.TAG_PRIMARY_CHROMATICITIES}, + {"double", ExifInterface.TAG_REFERENCE_BLACK_WHITE}, + {"int", ExifInterface.TAG_RESOLUTION_UNIT}, + {"int", ExifInterface.TAG_ROWS_PER_STRIP}, + {"int", ExifInterface.TAG_SAMPLES_PER_PIXEL}, + {"string", ExifInterface.TAG_SOFTWARE}, + {"int", ExifInterface.TAG_STRIP_BYTE_COUNTS}, + {"int", ExifInterface.TAG_STRIP_OFFSETS}, + {"int", ExifInterface.TAG_TRANSFER_FUNCTION}, + {"double", ExifInterface.TAG_WHITE_POINT}, + {"double", ExifInterface.TAG_X_RESOLUTION}, + {"double", ExifInterface.TAG_Y_CB_CR_COEFFICIENTS}, + {"int", ExifInterface.TAG_Y_CB_CR_POSITIONING}, + {"int", ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING}, + {"double", ExifInterface.TAG_Y_RESOLUTION}, + {"double", ExifInterface.TAG_APERTURE_VALUE}, + {"double", ExifInterface.TAG_BRIGHTNESS_VALUE}, + {"string", ExifInterface.TAG_CFA_PATTERN}, + {"int", ExifInterface.TAG_COLOR_SPACE}, + {"string", ExifInterface.TAG_COMPONENTS_CONFIGURATION}, + {"double", ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL}, + {"int", ExifInterface.TAG_CONTRAST}, + {"int", ExifInterface.TAG_CUSTOM_RENDERED}, + {"string", ExifInterface.TAG_DATETIME_DIGITIZED}, + {"string", ExifInterface.TAG_DATETIME_ORIGINAL}, + {"string", ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION}, + {"double", ExifInterface.TAG_DIGITAL_ZOOM_RATIO}, + {"string", ExifInterface.TAG_EXIF_VERSION}, + {"double", ExifInterface.TAG_EXPOSURE_BIAS_VALUE}, + {"double", ExifInterface.TAG_EXPOSURE_INDEX}, + {"int", ExifInterface.TAG_EXPOSURE_MODE}, + {"int", ExifInterface.TAG_EXPOSURE_PROGRAM}, + {"double", ExifInterface.TAG_EXPOSURE_TIME}, + {"double", ExifInterface.TAG_F_NUMBER}, + {"string", ExifInterface.TAG_FILE_SOURCE}, + {"int", ExifInterface.TAG_FLASH}, + {"double", ExifInterface.TAG_FLASH_ENERGY}, + {"string", ExifInterface.TAG_FLASHPIX_VERSION}, + {"double", ExifInterface.TAG_FOCAL_LENGTH}, + {"int", ExifInterface.TAG_FOCAL_LENGTH_IN_35MM_FILM}, + {"int", ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT}, + {"double", ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION}, + {"double", ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION}, + {"int", ExifInterface.TAG_GAIN_CONTROL}, + {"int", ExifInterface.TAG_ISO_SPEED_RATINGS}, + {"string", ExifInterface.TAG_IMAGE_UNIQUE_ID}, + {"int", ExifInterface.TAG_LIGHT_SOURCE}, + {"string", ExifInterface.TAG_MAKER_NOTE}, + {"double", ExifInterface.TAG_MAX_APERTURE_VALUE}, + {"int", ExifInterface.TAG_METERING_MODE}, + {"int", ExifInterface.TAG_NEW_SUBFILE_TYPE}, + {"string", ExifInterface.TAG_OECF}, + {"int", ExifInterface.TAG_PIXEL_X_DIMENSION}, + {"int", ExifInterface.TAG_PIXEL_Y_DIMENSION}, + {"string", ExifInterface.TAG_RELATED_SOUND_FILE}, + {"int", ExifInterface.TAG_SATURATION}, + {"int", ExifInterface.TAG_SCENE_CAPTURE_TYPE}, + {"string", ExifInterface.TAG_SCENE_TYPE}, + {"int", ExifInterface.TAG_SENSING_METHOD}, + {"int", ExifInterface.TAG_SHARPNESS}, + {"double", ExifInterface.TAG_SHUTTER_SPEED_VALUE}, + {"string", ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE}, + {"string", ExifInterface.TAG_SPECTRAL_SENSITIVITY}, + {"int", ExifInterface.TAG_SUBFILE_TYPE}, + {"string", ExifInterface.TAG_SUBSEC_TIME}, + {"string", ExifInterface.TAG_SUBSEC_TIME_DIGITIZED}, + {"string", ExifInterface.TAG_SUBSEC_TIME_ORIGINAL}, + {"int", ExifInterface.TAG_SUBJECT_AREA}, + {"double", ExifInterface.TAG_SUBJECT_DISTANCE}, + {"int", ExifInterface.TAG_SUBJECT_DISTANCE_RANGE}, + {"int", ExifInterface.TAG_SUBJECT_LOCATION}, + {"string", ExifInterface.TAG_USER_COMMENT}, + {"int", ExifInterface.TAG_WHITE_BALANCE}, + {"int", ExifInterface.TAG_GPS_ALTITUDE_REF}, + {"string", ExifInterface.TAG_GPS_AREA_INFORMATION}, + {"double", ExifInterface.TAG_GPS_DOP}, + {"string", ExifInterface.TAG_GPS_DATESTAMP}, + {"double", ExifInterface.TAG_GPS_DEST_BEARING}, + {"string", ExifInterface.TAG_GPS_DEST_BEARING_REF}, + {"double", ExifInterface.TAG_GPS_DEST_DISTANCE}, + {"string", ExifInterface.TAG_GPS_DEST_DISTANCE_REF}, + {"double", ExifInterface.TAG_GPS_DEST_LATITUDE}, + {"string", ExifInterface.TAG_GPS_DEST_LATITUDE_REF}, + {"double", ExifInterface.TAG_GPS_DEST_LONGITUDE}, + {"string", ExifInterface.TAG_GPS_DEST_LONGITUDE_REF}, + {"int", ExifInterface.TAG_GPS_DIFFERENTIAL}, + {"double", ExifInterface.TAG_GPS_IMG_DIRECTION}, + {"string", ExifInterface.TAG_GPS_IMG_DIRECTION_REF}, + {"string", ExifInterface.TAG_GPS_LATITUDE_REF}, + {"string", ExifInterface.TAG_GPS_LONGITUDE_REF}, + {"string", ExifInterface.TAG_GPS_MAP_DATUM}, + {"string", ExifInterface.TAG_GPS_MEASURE_MODE}, + {"string", ExifInterface.TAG_GPS_PROCESSING_METHOD}, + {"string", ExifInterface.TAG_GPS_SATELLITES}, + {"double", ExifInterface.TAG_GPS_SPEED}, + {"string", ExifInterface.TAG_GPS_SPEED_REF}, + {"string", ExifInterface.TAG_GPS_STATUS}, + {"string", ExifInterface.TAG_GPS_TIMESTAMP}, + {"double", ExifInterface.TAG_GPS_TRACK}, + {"string", ExifInterface.TAG_GPS_TRACK_REF}, + {"string", ExifInterface.TAG_GPS_VERSION_ID}, + {"string", ExifInterface.TAG_INTEROPERABILITY_INDEX}, + {"int", ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH}, + {"int", ExifInterface.TAG_THUMBNAIL_IMAGE_WIDTH}, + {"int", ExifInterface.TAG_DNG_VERSION}, + {"int", ExifInterface.TAG_DEFAULT_CROP_SIZE}, + {"int", ExifInterface.TAG_ORF_PREVIEW_IMAGE_START}, + {"int", ExifInterface.TAG_ORF_PREVIEW_IMAGE_LENGTH}, + {"int", ExifInterface.TAG_ORF_ASPECT_FRAME}, + {"int", ExifInterface.TAG_RW2_SENSOR_BOTTOM_BORDER}, + {"int", ExifInterface.TAG_RW2_SENSOR_LEFT_BORDER}, + {"int", ExifInterface.TAG_RW2_SENSOR_RIGHT_BORDER}, + {"int", ExifInterface.TAG_RW2_SENSOR_TOP_BORDER}, + {"int", ExifInterface.TAG_RW2_ISO}, }; // Mount error event @@ -187,7 +181,7 @@ public class RNCameraViewHelper { ViewGroup view, SparseArray faces, ImageDimensions dimensions - ) { + ) { float density = view.getResources().getDisplayMetrics().density; double scaleX = (double) view.getWidth() / (dimensions.getWidth() * density); @@ -211,6 +205,27 @@ public class RNCameraViewHelper { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event); } + // Barcode detection events + + public static void emitBarcodesDetectedEvent( + ViewGroup view, + SparseArray barcodes + ) { + BarcodesDetectedEvent event = BarcodesDetectedEvent.obtain( + view.getId(), + barcodes + ); + + ReactContext reactContext = (ReactContext) view.getContext(); + reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event); + } + + public static void emitBarcodeDetectionErrorEvent(ViewGroup view, RNBarcodeDetector barcodeDetector) { + BarcodeDetectionErrorEvent event = BarcodeDetectionErrorEvent.obtain(view.getId(), barcodeDetector); + ReactContext reactContext = (ReactContext) view.getContext(); + reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event); + } + // Bar code read event public static void emitBarCodeReadEvent(ViewGroup view, Result barCode) { diff --git a/android/src/main/java/org/reactnative/camera/events/BarcodeDetectionErrorEvent.java b/android/src/main/java/org/reactnative/camera/events/BarcodeDetectionErrorEvent.java new file mode 100644 index 0000000..3539a37 --- /dev/null +++ b/android/src/main/java/org/reactnative/camera/events/BarcodeDetectionErrorEvent.java @@ -0,0 +1,53 @@ +package org.reactnative.camera.events; + +import android.support.v4.util.Pools; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.uimanager.events.Event; +import com.facebook.react.uimanager.events.RCTEventEmitter; +import org.reactnative.camera.CameraViewManager; +import org.reactnative.barcodedetector.RNBarcodeDetector; + +public class BarcodeDetectionErrorEvent extends Event { + + private static final Pools.SynchronizedPool EVENTS_POOL = new Pools.SynchronizedPool<>(3); + private RNBarcodeDetector mBarcodeDetector; + + private BarcodeDetectionErrorEvent() { + } + + public static BarcodeDetectionErrorEvent obtain(int viewTag, RNBarcodeDetector barcodeDetector) { + BarcodeDetectionErrorEvent event = EVENTS_POOL.acquire(); + if (event == null) { + event = new BarcodeDetectionErrorEvent(); + } + event.init(viewTag, barcodeDetector); + return event; + } + + private void init(int viewTag, RNBarcodeDetector faceDetector) { + super.init(viewTag); + mBarcodeDetector = faceDetector; + } + + @Override + public short getCoalescingKey() { + return 0; + } + + @Override + public String getEventName() { + return CameraViewManager.Events.EVENT_ON_MOUNT_ERROR.toString(); + } + + @Override + public void dispatch(RCTEventEmitter rctEventEmitter) { + rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); + } + + private WritableMap serializeEventData() { + WritableMap map = Arguments.createMap(); + map.putBoolean("isOperational", mBarcodeDetector != null && mBarcodeDetector.isOperational()); + return map; + } +} diff --git a/android/src/main/java/org/reactnative/camera/events/BarcodesDetectedEvent.java b/android/src/main/java/org/reactnative/camera/events/BarcodesDetectedEvent.java new file mode 100644 index 0000000..1a35188 --- /dev/null +++ b/android/src/main/java/org/reactnative/camera/events/BarcodesDetectedEvent.java @@ -0,0 +1,85 @@ +package org.reactnative.camera.events; + +import android.support.v4.util.Pools; +import android.util.SparseArray; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.uimanager.events.Event; +import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.google.android.gms.vision.barcode.Barcode; +import org.reactnative.camera.CameraViewManager; +import org.reactnative.barcodedetector.BarcodeFormatUtils; + +public class BarcodesDetectedEvent extends Event { + + private static final Pools.SynchronizedPool EVENTS_POOL = + new Pools.SynchronizedPool<>(3); + + private SparseArray mBarcodes; + + private BarcodesDetectedEvent() { + } + + public static BarcodesDetectedEvent obtain( + int viewTag, + SparseArray barcodes + ) { + BarcodesDetectedEvent event = EVENTS_POOL.acquire(); + if (event == null) { + event = new BarcodesDetectedEvent(); + } + event.init(viewTag, barcodes); + return event; + } + + private void init( + int viewTag, + SparseArray barcodes + ) { + super.init(viewTag); + mBarcodes = barcodes; + } + + /** + * note(@sjchmiela) + * Should the events about detected barcodes coalesce, the best strategy will be + * to ensure that events with different barcodes count are always being transmitted. + */ + @Override + public short getCoalescingKey() { + if (mBarcodes.size() > Short.MAX_VALUE) { + return Short.MAX_VALUE; + } + + return (short) mBarcodes.size(); + } + + @Override + public String getEventName() { + return CameraViewManager.Events.EVENT_ON_BARCODES_DETECTED.toString(); + } + + @Override + public void dispatch(RCTEventEmitter rctEventEmitter) { + rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); + } + + private WritableMap serializeEventData() { + WritableArray barcodesList = Arguments.createArray(); + + for (int i = 0; i < mBarcodes.size(); i++) { + Barcode barcode = mBarcodes.valueAt(i); + WritableMap serializedBarcode = Arguments.createMap(); + serializedBarcode.putString("data", barcode.displayValue); + serializedBarcode.putString("type", BarcodeFormatUtils.get(barcode.format)); + barcodesList.pushMap(serializedBarcode); + } + + WritableMap event = Arguments.createMap(); + event.putString("type", "barcode"); + event.putArray("barcodes", barcodesList); + event.putInt("target", getViewTag()); + return event; + } +} diff --git a/android/src/main/java/org/reactnative/camera/events/FaceDetectionErrorEvent.java b/android/src/main/java/org/reactnative/camera/events/FaceDetectionErrorEvent.java index ff1aa60..55d5d66 100644 --- a/android/src/main/java/org/reactnative/camera/events/FaceDetectionErrorEvent.java +++ b/android/src/main/java/org/reactnative/camera/events/FaceDetectionErrorEvent.java @@ -1,27 +1,26 @@ package org.reactnative.camera.events; import android.support.v4.util.Pools; - -import org.reactnative.camera.CameraViewManager; -import org.reactnative.facedetector.RNFaceDetector; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.RCTEventEmitter; - -import java.util.Date; +import org.reactnative.camera.CameraViewManager; +import org.reactnative.facedetector.RNFaceDetector; public class FaceDetectionErrorEvent extends Event { private static final Pools.SynchronizedPool EVENTS_POOL = new Pools.SynchronizedPool<>(3); private RNFaceDetector mFaceDetector; - private FaceDetectionErrorEvent() {} + + private FaceDetectionErrorEvent() { + } public static FaceDetectionErrorEvent obtain(int viewTag, RNFaceDetector faceDetector) { FaceDetectionErrorEvent event = EVENTS_POOL.acquire(); if (event == null) { event = new FaceDetectionErrorEvent(); } - event.init(viewTag); + event.init(viewTag, faceDetector); return event; } @@ -37,7 +36,7 @@ public class FaceDetectionErrorEvent extends Event { @Override public String getEventName() { - return CameraViewManager.Events.EVENT_ON_MOUNT_ERROR.toString(); + return CameraViewManager.Events.EVENT_ON_FACE_DETECTION_ERROR.toString(); } @Override @@ -47,7 +46,7 @@ public class FaceDetectionErrorEvent extends Event { private WritableMap serializeEventData() { WritableMap map = Arguments.createMap(); - map.putBoolean("isOperational", mFaceDetector != null ? mFaceDetector.isOperational() : false); + map.putBoolean("isOperational", mFaceDetector != null && mFaceDetector.isOperational()); return map; } } diff --git a/android/src/main/java/org/reactnative/camera/events/FacesDetectedEvent.java b/android/src/main/java/org/reactnative/camera/events/FacesDetectedEvent.java index e8472b4..2e5e0d3 100644 --- a/android/src/main/java/org/reactnative/camera/events/FacesDetectedEvent.java +++ b/android/src/main/java/org/reactnative/camera/events/FacesDetectedEvent.java @@ -14,8 +14,6 @@ import com.facebook.react.uimanager.events.RCTEventEmitter; import com.google.android.cameraview.CameraView; import com.google.android.gms.vision.face.Face; -import java.util.Date; - public class FacesDetectedEvent extends Event { private static final Pools.SynchronizedPool EVENTS_POOL = new Pools.SynchronizedPool<>(3); diff --git a/android/src/main/java/org/reactnative/camera/tasks/BarcodeDetectorAsyncTask.java b/android/src/main/java/org/reactnative/camera/tasks/BarcodeDetectorAsyncTask.java new file mode 100644 index 0000000..f463820 --- /dev/null +++ b/android/src/main/java/org/reactnative/camera/tasks/BarcodeDetectorAsyncTask.java @@ -0,0 +1,57 @@ +package org.reactnative.camera.tasks; + +import android.util.SparseArray; +import com.google.android.gms.vision.barcode.Barcode; +import org.reactnative.frame.RNFrame; +import org.reactnative.frame.RNFrameFactory; +import org.reactnative.barcodedetector.RNBarcodeDetector; + +public class BarcodeDetectorAsyncTask extends android.os.AsyncTask> { + + private byte[] mImageData; + private int mWidth; + private int mHeight; + private int mRotation; + private RNBarcodeDetector mBarcodeDetector; + private BarcodeDetectorAsyncTaskDelegate mDelegate; + + public BarcodeDetectorAsyncTask( + BarcodeDetectorAsyncTaskDelegate delegate, + RNBarcodeDetector barcodeDetector, + byte[] imageData, + int width, + int height, + int rotation + ) { + mImageData = imageData; + mWidth = width; + mHeight = height; + mRotation = rotation; + mDelegate = delegate; + mBarcodeDetector = barcodeDetector; + } + + @Override + protected SparseArray doInBackground(Void... ignored) { + if (isCancelled() || mDelegate == null || mBarcodeDetector == null || !mBarcodeDetector.isOperational()) { + return null; + } + + RNFrame frame = RNFrameFactory.buildFrame(mImageData, mWidth, mHeight, mRotation); + return mBarcodeDetector.detect(frame); + } + + @Override + protected void onPostExecute(SparseArray barcodes) { + super.onPostExecute(barcodes); + + if (barcodes == null) { + mDelegate.onBarcodeDetectionError(mBarcodeDetector); + } else { + if (barcodes.size() > 0) { + mDelegate.onBarcodesDetected(barcodes, mWidth, mHeight, mRotation); + } + mDelegate.onBarcodeDetectingTaskCompleted(); + } + } +} diff --git a/android/src/main/java/org/reactnative/camera/tasks/BarcodeDetectorAsyncTaskDelegate.java b/android/src/main/java/org/reactnative/camera/tasks/BarcodeDetectorAsyncTaskDelegate.java new file mode 100644 index 0000000..8cf0b35 --- /dev/null +++ b/android/src/main/java/org/reactnative/camera/tasks/BarcodeDetectorAsyncTaskDelegate.java @@ -0,0 +1,14 @@ +package org.reactnative.camera.tasks; + +import android.util.SparseArray; +import com.google.android.gms.vision.barcode.Barcode; +import org.reactnative.barcodedetector.RNBarcodeDetector; + +public interface BarcodeDetectorAsyncTaskDelegate { + + void onBarcodesDetected(SparseArray barcodes, int sourceWidth, int sourceHeight, int sourceRotation); + + void onBarcodeDetectionError(RNBarcodeDetector barcodeDetector); + + void onBarcodeDetectingTaskCompleted(); +} diff --git a/android/src/main/java/org/reactnative/camera/tasks/FaceDetectorAsyncTask.java b/android/src/main/java/org/reactnative/camera/tasks/FaceDetectorAsyncTask.java index 9ad5986..a060907 100644 --- a/android/src/main/java/org/reactnative/camera/tasks/FaceDetectorAsyncTask.java +++ b/android/src/main/java/org/reactnative/camera/tasks/FaceDetectorAsyncTask.java @@ -1,11 +1,10 @@ package org.reactnative.camera.tasks; import android.util.SparseArray; - -import org.reactnative.facedetector.RNFaceDetector; -import org.reactnative.facedetector.RNFrame; -import org.reactnative.facedetector.RNFrameFactory; import com.google.android.gms.vision.face.Face; +import org.reactnative.frame.RNFrame; +import org.reactnative.frame.RNFrameFactory; +import org.reactnative.facedetector.RNFaceDetector; public class FaceDetectorAsyncTask extends android.os.AsyncTask> { private byte[] mImageData; @@ -48,7 +47,9 @@ public class FaceDetectorAsyncTask extends android.os.AsyncTask 0) { + mDelegate.onFacesDetected(faces, mWidth, mHeight, mRotation); + } mDelegate.onFaceDetectingTaskCompleted(); } } diff --git a/android/src/main/java/org/reactnative/camera/tasks/TextRecognizerAsyncTask.java b/android/src/main/java/org/reactnative/camera/tasks/TextRecognizerAsyncTask.java index e1be5b3..b444559 100644 --- a/android/src/main/java/org/reactnative/camera/tasks/TextRecognizerAsyncTask.java +++ b/android/src/main/java/org/reactnative/camera/tasks/TextRecognizerAsyncTask.java @@ -4,8 +4,8 @@ import android.util.SparseArray; import com.google.android.gms.vision.text.TextBlock; import com.google.android.gms.vision.text.TextRecognizer; -import org.reactnative.facedetector.RNFrame; -import org.reactnative.facedetector.RNFrameFactory; +import org.reactnative.frame.RNFrame; +import org.reactnative.frame.RNFrameFactory; public class TextRecognizerAsyncTask extends android.os.AsyncTask> { diff --git a/android/src/main/java/org/reactnative/facedetector/FaceDetectorModule.java b/android/src/main/java/org/reactnative/facedetector/FaceDetectorModule.java index 03a0916..62aac6d 100644 --- a/android/src/main/java/org/reactnative/facedetector/FaceDetectorModule.java +++ b/android/src/main/java/org/reactnative/facedetector/FaceDetectorModule.java @@ -1,7 +1,5 @@ package org.reactnative.facedetector; -import android.content.Context; - import org.reactnative.facedetector.tasks.FileFaceDetectionAsyncTask; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; diff --git a/android/src/main/java/org/reactnative/facedetector/RNFaceDetector.java b/android/src/main/java/org/reactnative/facedetector/RNFaceDetector.java index 91c58a8..6df2ae4 100644 --- a/android/src/main/java/org/reactnative/facedetector/RNFaceDetector.java +++ b/android/src/main/java/org/reactnative/facedetector/RNFaceDetector.java @@ -1,12 +1,12 @@ package org.reactnative.facedetector; import android.content.Context; -import android.util.Log; import android.util.SparseArray; import org.reactnative.camera.utils.ImageDimensions; import com.google.android.gms.vision.face.Face; import com.google.android.gms.vision.face.FaceDetector; +import org.reactnative.frame.RNFrame; public class RNFaceDetector { public static int ALL_CLASSIFICATIONS = FaceDetector.ALL_CLASSIFICATIONS; diff --git a/android/src/main/java/org/reactnative/facedetector/tasks/FileFaceDetectionAsyncTask.java b/android/src/main/java/org/reactnative/facedetector/tasks/FileFaceDetectionAsyncTask.java index d2b5335..73ce1e6 100644 --- a/android/src/main/java/org/reactnative/facedetector/tasks/FileFaceDetectionAsyncTask.java +++ b/android/src/main/java/org/reactnative/facedetector/tasks/FileFaceDetectionAsyncTask.java @@ -10,21 +10,18 @@ import android.util.Log; import android.util.SparseArray; import org.reactnative.facedetector.RNFaceDetector; -import org.reactnative.facedetector.RNFrame; -import org.reactnative.facedetector.RNFrameFactory; +import org.reactnative.frame.RNFrame; +import org.reactnative.frame.RNFrameFactory; import org.reactnative.facedetector.FaceDetectorUtils; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; -import com.google.android.gms.vision.Frame; import com.google.android.gms.vision.face.Face; import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; public class FileFaceDetectionAsyncTask extends AsyncTask> { private static final String ERROR_TAG = "E_FACE_DETECTION_FAILED"; diff --git a/android/src/main/java/org/reactnative/facedetector/RNFrame.java b/android/src/main/java/org/reactnative/frame/RNFrame.java similarity index 76% rename from android/src/main/java/org/reactnative/facedetector/RNFrame.java rename to android/src/main/java/org/reactnative/frame/RNFrame.java index 80295f7..ff66b45 100644 --- a/android/src/main/java/org/reactnative/facedetector/RNFrame.java +++ b/android/src/main/java/org/reactnative/frame/RNFrame.java @@ -1,11 +1,11 @@ -package org.reactnative.facedetector; +package org.reactnative.frame; import org.reactnative.camera.utils.ImageDimensions; import com.google.android.gms.vision.Frame; /** * Wrapper around Frame allowing us to track Frame dimensions. - * Tracking dimensions is used in RNFaceDetector to provide painless FaceDetector recreation + * Tracking dimensions is used in RNFaceDetector and RNBarcodeDetector to provide painless FaceDetector/BarcodeDetector recreation * when image dimensions change. */ diff --git a/android/src/main/java/org/reactnative/facedetector/RNFrameFactory.java b/android/src/main/java/org/reactnative/frame/RNFrameFactory.java similarity index 97% rename from android/src/main/java/org/reactnative/facedetector/RNFrameFactory.java rename to android/src/main/java/org/reactnative/frame/RNFrameFactory.java index 339fb35..9f9b85b 100644 --- a/android/src/main/java/org/reactnative/facedetector/RNFrameFactory.java +++ b/android/src/main/java/org/reactnative/frame/RNFrameFactory.java @@ -1,4 +1,4 @@ -package org.reactnative.facedetector; +package org.reactnative.frame; import android.graphics.Bitmap; import android.graphics.ImageFormat; diff --git a/src/RNCamera.js b/src/RNCamera.js index 9b1f127..26b1ec4 100644 --- a/src/RNCamera.js +++ b/src/RNCamera.js @@ -66,9 +66,11 @@ type PropsType = (typeof View.props) & { type?: number | string, onCameraReady?: Function, onBarCodeRead?: Function, + onGoogleVisionBarcodesDetected?: Function, faceDetectionMode?: number, flashMode?: number | string, barCodeTypes?: Array, + googleVisionBarcodeType?: number, whiteBalance?: number | string, faceDetectionLandmarks?: number, autoFocus?: string | boolean | number, @@ -79,7 +81,7 @@ type PropsType = (typeof View.props) & { useCamera2Api?: boolean, playSoundOnCapture?: boolean, }; - + type StateType = { isAuthorized: boolean, isAuthorizationChecked: boolean, @@ -110,6 +112,9 @@ const CameraManager: Object = NativeModules.RNCameraManager || none: 0, }, }, + GoogleVisionBarcodeDetection: { + BarcodeType: 0, + } }; const EventThrottleMs = 500; @@ -123,6 +128,7 @@ export default class Camera extends React.Component { VideoQuality: CameraManager.VideoQuality, VideoCodec: CameraManager.VideoCodec, BarCodeType: CameraManager.BarCodeType, + GoogleVisionBarcodeDetection: CameraManager.GoogleVisionBarcodeDetection, FaceDetection: CameraManager.FaceDetection, }; @@ -135,6 +141,7 @@ export default class Camera extends React.Component { faceDetectionMode: (CameraManager.FaceDetection || {}).Mode, faceDetectionLandmarks: (CameraManager.FaceDetection || {}).Landmarks, faceDetectionClassifications: (CameraManager.FaceDetection || {}).Classifications, + googleVisionBarcodeType: (CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeType, }; static propTypes = { @@ -145,12 +152,14 @@ export default class Camera extends React.Component { onMountError: PropTypes.func, onCameraReady: PropTypes.func, onBarCodeRead: PropTypes.func, + onGoogleVisionBarcodesDetected: PropTypes.func, onFacesDetected: PropTypes.func, onTextRecognized: PropTypes.func, faceDetectionMode: PropTypes.number, faceDetectionLandmarks: PropTypes.number, faceDetectionClassifications: PropTypes.number, barCodeTypes: PropTypes.arrayOf(PropTypes.string), + googleVisionBarcodeType: PropTypes.number, type: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), flashMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), whiteBalance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), @@ -174,6 +183,7 @@ export default class Camera extends React.Component { whiteBalance: CameraManager.WhiteBalance.auto, faceDetectionMode: (CameraManager.FaceDetection || {}).fast, barCodeTypes: Object.values(CameraManager.BarCodeType), + googleVisionBarcodeType: (CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeType, faceDetectionLandmarks: ((CameraManager.FaceDetection || {}).Landmarks || {}).none, faceDetectionClassifications: ((CameraManager.FaceDetection || {}).Classifications || {}).none, permissionDialogTitle: '', @@ -320,6 +330,7 @@ export default class Camera extends React.Component { ref={this._setReference} onMountError={this._onMountError} onCameraReady={this._onCameraReady} + onGoogleVisionBarcodesDetected={this._onObjectDetected(this.props.onGoogleVisionBarcodesDetected)} onBarCodeRead={this._onObjectDetected(this.props.onBarCodeRead)} onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)} onTextRecognized={this._onObjectDetected(this.props.onTextRecognized)} @@ -339,6 +350,10 @@ export default class Camera extends React.Component { newProps.barCodeScannerEnabled = true; } + if (props.onGoogleVisionBarcodesDetected) { + newProps.googleVisionBarcodeDetectorEnabled = true; + } + if (props.onFacesDetected) { newProps.faceDetectorEnabled = true; } @@ -348,6 +363,8 @@ export default class Camera extends React.Component { } if (Platform.OS === 'ios') { + delete newProps.googleVisionBarcodeType; + delete newProps.googleVisionBarcodeDetectorEnabled; delete newProps.ratio; delete newProps.textRecognizerEnabled; } @@ -372,10 +389,12 @@ const RNCamera = requireNativeComponent('RNCamera', Camera, { accessibilityLabel: true, accessibilityLiveRegion: true, barCodeScannerEnabled: true, + googleVisionBarcodeDetectorEnabled: true, faceDetectorEnabled: true, textRecognizerEnabled: true, importantForAccessibility: true, onBarCodeRead: true, + onGoogleVisionBarcodesDetected: true, onCameraReady: true, onFaceDetected: true, onLayout: true, diff --git a/types/index.d.ts b/types/index.d.ts index 51c608e..238810e 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -6,7 +6,7 @@ * Author notes: * I've tried to find a easy tool to convert from Flow to Typescript definition files (.d.ts). * So we woudn't have to do it manually... Sadly, I haven't found it. - * + * * If you are seeing this from the future, please, send us your cutting-edge technology :) (if it exists) */ import { Component } from 'react'; @@ -23,6 +23,8 @@ type VideoCodec = { 'H264': symbol, 'JPEG': symbol, 'HVEC': symbol, 'AppleProRes type FaceDetectionClassifications = { all: any, none: any }; type FaceDetectionLandmarks = { all: any, none: any }; type FaceDetectionMode = { fast: any, accurate: any }; +type GoogleVisionBarcodeType = { CODE_128: any, CODE_39: any, CODABAR: any, DATA_MATRIX: any, EAN_13: any, EAN_8: any, ITF: any, + QR_CODE: any, UPC_A: any, UPC_E: any, PDF417: any, AZTEC: any } export interface Constants { AutoFocus: AutoFocus; @@ -36,6 +38,9 @@ export interface Constants { Classifications: FaceDetectionClassifications; Landmarks: FaceDetectionLandmarks; Mode: FaceDetectionMode; + }, + GoogleVisionBarcodeDetection: { + BarcodeType: GoogleVisionBarcodeType } } @@ -57,6 +62,7 @@ export interface RNCameraProps { // -- BARCODE PROPS barCodeTypes?: Array; + googleVisionBarcodeType?: keyof GoogleVisionBarcodeType; onBarCodeRead?(event: { data: string, type: keyof BarCodeType, @@ -66,9 +72,10 @@ export interface RNCameraProps { */ bounds: [Point, Point] | { origin: Point, size: Size } }): void; - + // -- FACE DETECTION PROPS + onGoogleVisionBarcodesDetected?(response: { barcodes: Barcode[] }): void; onFacesDetected?(response: { faces: Face[] }): void; onFaceDetectionError?(response: { isOperational: boolean }): void; faceDetectionMode?: keyof FaceDetectionMode; @@ -88,7 +95,7 @@ export interface RNCameraProps { playSoundOnCapture?: boolean; // -- IOS ONLY PROPS - + /** iOS Only */ captureAudio?: boolean; } @@ -103,6 +110,11 @@ interface Size { height: T; } +interface Barcode { + data: string; + type: string; +} + interface Face { faceID?: number, bounds: { @@ -148,7 +160,7 @@ interface TakePictureOptions { skipProcessing?: boolean; /** Android only */ fixOrientation?: boolean; - + /** iOS only */ forceUpOrientation?: boolean; }