Apply barcode scanner on preview frames.

This commit is contained in:
plougsgaard 2016-09-15 01:11:11 +02:00
parent ce11617743
commit 918c00f003

View File

@ -8,8 +8,17 @@ import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.view.TextureView;
import android.os.AsyncTask;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.util.List;
import java.util.EnumMap;
import java.util.EnumSet;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
@ -18,17 +27,24 @@ import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
class RCTCameraViewFinder extends TextureView implements TextureView.SurfaceTextureListener {
class RCTCameraViewFinder extends TextureView implements TextureView.SurfaceTextureListener, Camera.PreviewCallback {
private int _cameraType;
private SurfaceTexture _surfaceTexture;
private boolean _isStarting;
private boolean _isStopping;
private Camera _camera;
// concurrency lock for barcode scanner to avoid flooding the runtime
public static volatile boolean barcodeScannerTaskLock = false;
// reader instance for the barcode scanner
private final MultiFormatReader _multiFormatReader = new MultiFormatReader();
public RCTCameraViewFinder(Context context, int type) {
super(context);
this.setSurfaceTextureListener(this);
this._cameraType = type;
this.initBarcodeReader();
}
@Override
@ -119,6 +135,8 @@ class RCTCameraViewFinder extends TextureView implements TextureView.SurfaceText
_camera.setParameters(parameters);
_camera.setPreviewTexture(_surfaceTexture);
_camera.startPreview();
// send previews to `onPreviewFrame`
_camera.setPreviewCallback(this);
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
@ -136,6 +154,7 @@ class RCTCameraViewFinder extends TextureView implements TextureView.SurfaceText
try {
if (_camera != null) {
_camera.stopPreview();
// stop sending previews to `onPreviewFrame`
_camera.setPreviewCallback(null);
RCTCamera.getInstance().releaseCameraInstance(_cameraType);
_camera = null;
@ -148,4 +167,85 @@ class RCTCameraViewFinder extends TextureView implements TextureView.SurfaceText
}
}
}
/**
* Initialize the barcode decoder.
*
* TODO add hints for the `barCodeTypes` prop instead of the hardcoded `EAN_13`
*/
private void initBarcodeReader() {
EnumMap<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
EnumSet<BarcodeFormat> decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
decodeFormats.add(BarcodeFormat.EAN_13);
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
_multiFormatReader.setHints(hints);
}
/**
* Spawn a barcode reader task if
* - the barcode scanner is enabled (has a onBarCodeRead function)
* - one isn't already running
*
* See {Camera.PreviewCallback}
*/
public void onPreviewFrame(byte[] data, Camera camera) {
if (RCTCamera.getInstance().isBarcodeScannerEnabled() && !RCTCameraViewFinder.barcodeScannerTaskLock) {
RCTCameraViewFinder.barcodeScannerTaskLock = true;
new ReaderAsyncTask(camera, data).execute();
}
}
private class ReaderAsyncTask extends AsyncTask<Void, Void, Void> {
private byte[] imageData;
private final Camera camera;
ReaderAsyncTask(Camera camera, byte[] imageData) {
this.camera = camera;
this.imageData = imageData;
}
@Override
protected Void doInBackground(Void... ignored) {
if (isCancelled()) {
return null;
}
Camera.Size size = camera.getParameters().getPreviewSize();
int width = size.width;
int height = size.height;
// rotate for zxing if orientation is portrait
if (RCTCamera.getInstance().getActualDeviceOrientation() == 0) {
byte[] rotated = new byte[imageData.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
rotated[x * height + height - y - 1] = imageData[x + y * width];
}
}
width = size.height;
height = size.width;
imageData = rotated;
}
try {
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(imageData, width, height, 0, 0, width, height, false);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result = _multiFormatReader.decodeWithState(bitmap);
ReactContext reactContext = RCTCameraModule.getReactContextSingleton();
WritableMap event = Arguments.createMap();
event.putString("data", result.getText());
event.putString("type", result.getBarcodeFormat().toString());
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("CameraBarCodeReadAndroid", event);
} catch (Throwable t) {
// meh
} finally {
_multiFormatReader.reset();
RCTCameraViewFinder.barcodeScannerTaskLock = false;
return null;
}
}
}
}