Merge pull request #141 from wix/barcode_key
Support diff orientations and barcode alignment
This commit is contained in:
commit
7bfd606673
@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25.0.1"
|
||||
buildToolsVersion "26.0.3"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
@ -14,7 +14,7 @@ android {
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
warning 'InvalidPackage'
|
||||
warning 'InvalidPackage'
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,5 @@ dependencies {
|
||||
compile 'com.facebook.react:react-native:+'
|
||||
compile 'com.android.support:recyclerview-v7:25.0.1'
|
||||
compile 'com.google.zxing:core:3.3.0'
|
||||
compile 'me.dm7.barcodescanner:core:1.9.8'
|
||||
compile group: 'com.drewnoakes', name: 'metadata-extractor', version: '2.9.1'
|
||||
}
|
||||
|
||||
@ -216,4 +216,9 @@ public class Utils {
|
||||
public static int convertDeviceHeightToSupportedAspectRatio(float actualWidth, float actualHeight) {
|
||||
return (int) (actualHeight / actualWidth > MAX_SCREEN_RATIO ? actualWidth * MAX_SCREEN_RATIO : actualHeight);
|
||||
}
|
||||
|
||||
|
||||
public static void runOnWorkerThread(Runnable runnable) {
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.wix.RNCameraKit.camera;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
@ -12,26 +11,20 @@ import android.widget.FrameLayout;
|
||||
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.wix.RNCameraKit.Utils;
|
||||
|
||||
import me.dm7.barcodescanner.core.IViewFinder;
|
||||
import me.dm7.barcodescanner.core.ViewFinderView;
|
||||
import com.wix.RNCameraKit.camera.barcode.BarcodeFrame;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
public class CameraView extends FrameLayout implements SurfaceHolder.Callback {
|
||||
private ThemedReactContext context;
|
||||
private SurfaceView surface;
|
||||
|
||||
private Rect viewFrameRect;
|
||||
private IViewFinder viewFinder;
|
||||
private Rect frameRect;
|
||||
private BarcodeFrame barcodeFrame;
|
||||
@ColorInt private int frameColor = Color.GREEN;
|
||||
@ColorInt private int laserColor = Color.RED;
|
||||
|
||||
public CameraView(ThemedReactContext context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
|
||||
|
||||
surface = new SurfaceView(context);
|
||||
setBackgroundColor(Color.BLACK);
|
||||
addView(surface, MATCH_PARENT, MATCH_PARENT);
|
||||
@ -44,9 +37,10 @@ public class CameraView extends FrameLayout implements SurfaceHolder.Callback {
|
||||
CameraViewManager.getCamera().autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -60,8 +54,8 @@ public class CameraView extends FrameLayout implements SurfaceHolder.Callback {
|
||||
int actualPreviewHeight = getResources().getDisplayMetrics().heightPixels;
|
||||
int height = Utils.convertDeviceHeightToSupportedAspectRatio(actualPreviewWidth, actualPreviewHeight);
|
||||
surface.layout(0, 0, actualPreviewWidth, height);
|
||||
if (viewFinder != null) {
|
||||
((View) viewFinder).layout(0, 0, actualPreviewWidth, height);
|
||||
if (barcodeFrame != null) {
|
||||
((View) barcodeFrame).layout(0, 0, actualPreviewWidth, height);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,66 +96,48 @@ public class CameraView extends FrameLayout implements SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
public void showFrame() {
|
||||
viewFinder = createViewFinderView(getContext());
|
||||
addView((View) viewFinder);
|
||||
barcodeFrame = new BarcodeFrame(getContext());
|
||||
barcodeFrame.setFrameColor(frameColor);
|
||||
barcodeFrame.setLaserColor(laserColor);
|
||||
addView(barcodeFrame);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
private IViewFinder createViewFinderView(Context context) {
|
||||
ViewFinderView viewFinderView = new ViewFinderView(context);
|
||||
viewFinderView.setBorderColor(frameColor);
|
||||
viewFinderView.setLaserColor(laserColor);
|
||||
viewFinderView.setLaserEnabled(true);
|
||||
viewFinderView.setBorderStrokeWidth(5);
|
||||
viewFinderView.setBorderLineLength(60);
|
||||
viewFinderView.setMaskColor(Color.argb(60, 0, 0, 0));
|
||||
|
||||
viewFinderView.setSquareViewFinder(true);
|
||||
viewFinderView.setViewFinderOffset(11);
|
||||
return viewFinderView;
|
||||
}
|
||||
|
||||
public Rect getFramingRectInPreview(int previewWidth, int previewHeight) {
|
||||
if (viewFrameRect == null) {
|
||||
if (viewFinder != null) {
|
||||
Rect framingRect = viewFinder.getFramingRect();
|
||||
int viewFinderViewWidth = viewFinder.getWidth();
|
||||
int viewFinderViewHeight = viewFinder.getHeight();
|
||||
if (framingRect == null || viewFinderViewWidth == 0 || viewFinderViewHeight == 0) {
|
||||
return null;
|
||||
if (frameRect == null) {
|
||||
if (barcodeFrame != null) {
|
||||
Rect framingRect = new Rect(barcodeFrame.getFrameRect());
|
||||
int frameWidth = barcodeFrame.getWidth();
|
||||
int frameHeight = barcodeFrame.getHeight();
|
||||
|
||||
if (previewWidth < frameWidth) {
|
||||
framingRect.left = framingRect.left * previewWidth / frameWidth;
|
||||
framingRect.right = framingRect.right * previewWidth / frameWidth;
|
||||
}
|
||||
if (previewHeight < frameHeight) {
|
||||
framingRect.top = framingRect.top * previewHeight / frameHeight;
|
||||
framingRect.bottom = framingRect.bottom * previewHeight / frameHeight;
|
||||
}
|
||||
|
||||
Rect rect = new Rect(framingRect);
|
||||
|
||||
if (previewWidth < viewFinderViewWidth) {
|
||||
rect.left = rect.left * previewWidth / viewFinderViewWidth;
|
||||
rect.right = rect.right * previewWidth / viewFinderViewWidth;
|
||||
}
|
||||
|
||||
if (previewHeight < viewFinderViewHeight) {
|
||||
rect.top = rect.top * previewHeight / viewFinderViewHeight;
|
||||
rect.bottom = rect.bottom * previewHeight / viewFinderViewHeight;
|
||||
}
|
||||
|
||||
viewFrameRect = rect;
|
||||
frameRect = framingRect;
|
||||
} else {
|
||||
viewFrameRect = new Rect(0, 0, previewWidth, previewHeight);
|
||||
frameRect = new Rect(0, 0, previewWidth, previewHeight);
|
||||
}
|
||||
}
|
||||
return viewFrameRect;
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
public void setFrameColor(@ColorInt int color) {
|
||||
this.frameColor = color;
|
||||
if (viewFinder != null) {
|
||||
viewFinder.setBorderColor(frameColor);
|
||||
if (barcodeFrame != null) {
|
||||
barcodeFrame.setFrameColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public void setLaserColor(@ColorInt int color) {
|
||||
this.laserColor = color;
|
||||
if (viewFinder != null) {
|
||||
viewFinder.setLaserColor(laserColor);
|
||||
if (barcodeFrame != null) {
|
||||
barcodeFrame.setLaserColor(laserColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,10 +51,15 @@ public class CameraViewManager extends SimpleViewManager<CameraView> {
|
||||
private static BarcodeScanner scanner;
|
||||
private static Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
|
||||
@Override
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
if (scanner != null) {
|
||||
scanner.onPreviewFrame(data, camera);
|
||||
}
|
||||
public void onPreviewFrame(final byte[] data, final Camera camera) {
|
||||
Utils.runOnWorkerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (scanner != null) {
|
||||
scanner.onPreviewFrame(data, camera);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -137,6 +142,7 @@ public class CameraViewManager extends SimpleViewManager<CameraView> {
|
||||
}
|
||||
|
||||
private static void releaseCamera() {
|
||||
camera.setOneShotPreviewCallback(null);
|
||||
cameraReleased.set(true);
|
||||
camera.release();
|
||||
}
|
||||
@ -161,10 +167,10 @@ public class CameraViewManager extends SimpleViewManager<CameraView> {
|
||||
try {
|
||||
camera.stopPreview();
|
||||
camera.setPreviewDisplay(cameraViews.peek().getHolder());
|
||||
camera.startPreview();
|
||||
if (shouldScan) {
|
||||
camera.setOneShotPreviewCallback(previewCallback);
|
||||
}
|
||||
camera.startPreview();
|
||||
} catch (IOException | RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -274,13 +280,13 @@ public class CameraViewManager extends SimpleViewManager<CameraView> {
|
||||
}
|
||||
|
||||
public static void setBarcodeScanner() {
|
||||
scanner = new BarcodeScanner(reactContext, previewCallback);
|
||||
scanner.setResultHandler(new BarcodeScanner.ResultHandler() {
|
||||
scanner = new BarcodeScanner(previewCallback, new BarcodeScanner.ResultHandler() {
|
||||
@Override
|
||||
public void handleResult(Result rawResult) {
|
||||
public void handleResult(Result result) {
|
||||
WritableMap event = Arguments.createMap();
|
||||
event.putString("codeStringValue", rawResult.getText());
|
||||
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(cameraViews.peek().getId(), "onReadCode", event);
|
||||
event.putString("codeStringValue", result.getText());
|
||||
if (!cameraViews.empty())
|
||||
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(cameraViews.peek().getId(), "onReadCode", event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,110 @@
|
||||
package com.wix.RNCameraKit.camera.barcode;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.view.View;
|
||||
|
||||
import com.wix.RNCameraKit.R;
|
||||
|
||||
public class BarcodeFrame extends View {
|
||||
|
||||
private static final int STROKE_WIDTH = 5;
|
||||
private static final int ANIMATION_SPEED = 8;
|
||||
private static final int WIDTH_SCALE = 7;
|
||||
private static final double HEIGHT_SCALE = 2.75;
|
||||
|
||||
private Paint dimPaint;
|
||||
private Paint framePaint;
|
||||
private Paint borderPaint;
|
||||
private Paint laserPaint;
|
||||
private Rect frameRect;
|
||||
private int width;
|
||||
private int height;
|
||||
private int borderMargin;
|
||||
|
||||
private long previousFrameTime = System.currentTimeMillis();
|
||||
private int laserY;
|
||||
|
||||
public BarcodeFrame(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
framePaint = new Paint();
|
||||
framePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
dimPaint = new Paint();
|
||||
dimPaint.setStyle(Paint.Style.FILL);
|
||||
dimPaint.setColor(context.getResources().getColor(R.color.bg_dark));
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setStyle(Paint.Style.STROKE);
|
||||
borderPaint.setStrokeWidth(STROKE_WIDTH);
|
||||
laserPaint = new Paint();
|
||||
laserPaint.setStyle(Paint.Style.STROKE);
|
||||
laserPaint.setStrokeWidth(STROKE_WIDTH);
|
||||
|
||||
frameRect = new Rect();
|
||||
borderMargin = context.getResources().getDimensionPixelSize(R.dimen.border_length);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
width = getMeasuredWidth();
|
||||
height = getMeasuredHeight();
|
||||
int marginWidth = width / WIDTH_SCALE;
|
||||
int marginHeight = (int) (height / HEIGHT_SCALE);
|
||||
|
||||
frameRect.left = marginWidth;
|
||||
frameRect.right = width - marginWidth;
|
||||
frameRect.top = marginHeight;
|
||||
frameRect.bottom = height - marginHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
long timeElapsed = (System.currentTimeMillis() - previousFrameTime);
|
||||
super.onDraw(canvas);
|
||||
canvas.drawRect(0, 0, width, height, dimPaint);
|
||||
canvas.drawRect(frameRect, framePaint);
|
||||
drawBorder(canvas);
|
||||
drawLaser(canvas, timeElapsed);
|
||||
previousFrameTime = System.currentTimeMillis();
|
||||
this.invalidate(frameRect);
|
||||
}
|
||||
|
||||
private void drawBorder(Canvas canvas) {
|
||||
canvas.drawLine(frameRect.left, frameRect.top, frameRect.left, frameRect.top + borderMargin, borderPaint);
|
||||
canvas.drawLine(frameRect.left, frameRect.top, frameRect.left + borderMargin, frameRect.top, borderPaint);
|
||||
canvas.drawLine(frameRect.left, frameRect.bottom, frameRect.left, frameRect.bottom - borderMargin, borderPaint);
|
||||
canvas.drawLine(frameRect.left, frameRect.bottom, frameRect.left + borderMargin, frameRect.bottom, borderPaint);
|
||||
canvas.drawLine(frameRect.right, frameRect.top, frameRect.right - borderMargin, frameRect.top, borderPaint);
|
||||
canvas.drawLine(frameRect.right, frameRect.top, frameRect.right, frameRect.top + borderMargin, borderPaint);
|
||||
canvas.drawLine(frameRect.right, frameRect.bottom, frameRect.right, frameRect.bottom - borderMargin, borderPaint);
|
||||
canvas.drawLine(frameRect.right, frameRect.bottom, frameRect.right - borderMargin, frameRect.bottom, borderPaint);
|
||||
}
|
||||
|
||||
private void drawLaser(Canvas canvas, long timeElapsed) {
|
||||
if (laserY > frameRect.bottom || laserY < frameRect.top) laserY = frameRect.top;
|
||||
canvas.drawLine(frameRect.left + STROKE_WIDTH, laserY, frameRect.right - STROKE_WIDTH, laserY, laserPaint);
|
||||
laserY += (timeElapsed) / ANIMATION_SPEED;
|
||||
}
|
||||
|
||||
public Rect getFrameRect() {
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
public void setFrameColor(@ColorInt int borderColor) {
|
||||
borderPaint.setColor(borderColor);
|
||||
}
|
||||
|
||||
public void setLaserColor(@ColorInt int laserColor) {
|
||||
laserPaint.setColor(laserColor);
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,12 @@
|
||||
package com.wix.RNCameraKit.camera.barcode;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
@ -15,33 +14,26 @@ import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.PlanarYUVLuminanceSource;
|
||||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.wix.RNCameraKit.camera.CameraViewManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.dm7.barcodescanner.core.DisplayUtils;
|
||||
|
||||
public class BarcodeScanner {
|
||||
|
||||
public interface ResultHandler {
|
||||
void handleResult(Result rawResult);
|
||||
void handleResult(Result result);
|
||||
}
|
||||
|
||||
private MultiFormatReader mMultiFormatReader;
|
||||
public static final List<BarcodeFormat> ALL_FORMATS = new ArrayList<>();
|
||||
private List<BarcodeFormat> mFormats;
|
||||
private ResultHandler mResultHandler;
|
||||
private static final List<BarcodeFormat> ALL_FORMATS = new ArrayList<>();
|
||||
private ResultHandler resultHandler;
|
||||
|
||||
private Context context;
|
||||
private Camera.PreviewCallback previewCallback;
|
||||
|
||||
static {
|
||||
@ -64,154 +56,90 @@ public class BarcodeScanner {
|
||||
ALL_FORMATS.add(BarcodeFormat.UPC_EAN_EXTENSION);
|
||||
}
|
||||
|
||||
public BarcodeScanner(Context context, Camera.PreviewCallback previewCallback) {
|
||||
this.context = context;
|
||||
this.previewCallback = previewCallback;
|
||||
initMultiFormatReader();
|
||||
}
|
||||
|
||||
public void setFormats(List<BarcodeFormat> formats) {
|
||||
mFormats = formats;
|
||||
initMultiFormatReader();
|
||||
}
|
||||
|
||||
public void setResultHandler(ResultHandler resultHandler) {
|
||||
mResultHandler = resultHandler;
|
||||
}
|
||||
|
||||
public Collection<BarcodeFormat> getFormats() {
|
||||
if(mFormats == null) {
|
||||
return ALL_FORMATS;
|
||||
}
|
||||
return mFormats;
|
||||
}
|
||||
|
||||
private void initMultiFormatReader() {
|
||||
Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class);
|
||||
hints.put(DecodeHintType.POSSIBLE_FORMATS, getFormats());
|
||||
public BarcodeScanner(@NonNull Camera.PreviewCallback previewCallback, @NonNull ResultHandler resultHandler) {
|
||||
Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
|
||||
hints.put(DecodeHintType.POSSIBLE_FORMATS, ALL_FORMATS);
|
||||
mMultiFormatReader = new MultiFormatReader();
|
||||
mMultiFormatReader.setHints(hints);
|
||||
|
||||
this.previewCallback = previewCallback;
|
||||
this.resultHandler = resultHandler;
|
||||
}
|
||||
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
if(mResultHandler == null) {
|
||||
return;
|
||||
}
|
||||
public void onPreviewFrame(byte[] data, final Camera camera) {
|
||||
try {
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
Camera.Size size = parameters.getPreviewSize();
|
||||
Camera.Size size = camera.getParameters().getPreviewSize();
|
||||
int width = size.width;
|
||||
int height = size.height;
|
||||
|
||||
if (DisplayUtils.getScreenOrientation(context) == Configuration.ORIENTATION_PORTRAIT) {
|
||||
int rotationCount = CameraViewManager.getRotationCount();
|
||||
if (rotationCount == 1 || rotationCount == 3) {
|
||||
int tmp = width;
|
||||
width = height;
|
||||
height = tmp;
|
||||
}
|
||||
data = getRotatedData(data, camera);
|
||||
}
|
||||
int tmp = width;
|
||||
width = height;
|
||||
height = tmp;
|
||||
data = getRotatedData(data, camera);
|
||||
|
||||
Result rawResult = null;
|
||||
PlanarYUVLuminanceSource source = buildLuminanceSource(data, width, height);
|
||||
final Result result = decodeResult(getLuminanceSource(data, width, height));
|
||||
|
||||
if (source != null) {
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
try {
|
||||
rawResult = mMultiFormatReader.decodeWithState(bitmap);
|
||||
} catch (ReaderException re) {
|
||||
// continue
|
||||
} catch (NullPointerException npe) {
|
||||
// This is terrible
|
||||
} catch (ArrayIndexOutOfBoundsException ignored) {
|
||||
|
||||
} finally {
|
||||
mMultiFormatReader.reset();
|
||||
}
|
||||
|
||||
if (rawResult == null) {
|
||||
LuminanceSource invertedSource = source.invert();
|
||||
bitmap = new BinaryBitmap(new HybridBinarizer(invertedSource));
|
||||
try {
|
||||
rawResult = mMultiFormatReader.decodeWithState(bitmap);
|
||||
} catch (NotFoundException e) {
|
||||
// continue
|
||||
} finally {
|
||||
mMultiFormatReader.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Result finalRawResult = rawResult;
|
||||
|
||||
if (finalRawResult != null) {
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.post(new Runnable() {
|
||||
if (result != null) {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Stopping the preview can take a little long.
|
||||
// So we want to set result handler to null to discard subsequent calls to
|
||||
// onPreviewFrame.
|
||||
ResultHandler tmpResultHandler = mResultHandler;
|
||||
mResultHandler = null;
|
||||
|
||||
//TODO:decide if I need to do this
|
||||
//stopCameraPreview();
|
||||
if (tmpResultHandler != null) {
|
||||
tmpResultHandler.handleResult(finalRawResult);
|
||||
}
|
||||
resultHandler.handleResult(result);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
camera.setOneShotPreviewCallback(previewCallback);
|
||||
}
|
||||
} catch(RuntimeException e) {
|
||||
// TODO: Terrible hack. It is possible that this method is invoked after camera is released.
|
||||
camera.setOneShotPreviewCallback(previewCallback);
|
||||
} catch (RuntimeException e) {
|
||||
Log.w("CameraKit", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
|
||||
Rect rect = CameraViewManager.getFramingRectInPreview(width, height);
|
||||
if (rect == null) {
|
||||
return null;
|
||||
}
|
||||
// Go ahead and assume it's YUV rather than die.
|
||||
PlanarYUVLuminanceSource source = null;
|
||||
@Nullable
|
||||
private Result decodeResult(LuminanceSource source) {
|
||||
Result rawResult = null;
|
||||
if (source != null) {
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
try {
|
||||
rawResult = mMultiFormatReader.decodeWithState(bitmap);
|
||||
} catch (ReaderException ignored) {
|
||||
} finally {
|
||||
mMultiFormatReader.reset();
|
||||
}
|
||||
|
||||
try {
|
||||
source = new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
|
||||
rect.width(), rect.height(), false);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (rawResult == null && source.isRotateSupported()) {
|
||||
LuminanceSource rotatedSource = source.rotateCounterClockwise();
|
||||
bitmap = new BinaryBitmap(new HybridBinarizer(rotatedSource));
|
||||
try {
|
||||
rawResult = mMultiFormatReader.decodeWithState(bitmap);
|
||||
} catch (ReaderException ignored) {
|
||||
} finally {
|
||||
mMultiFormatReader.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rawResult;
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
private LuminanceSource getLuminanceSource(byte[] data, int width, int height) {
|
||||
Rect rect = CameraViewManager.getFramingRectInPreview(width, height);
|
||||
try {
|
||||
return new RotateLuminanceSource(data, width, height, rect.left, rect.top,
|
||||
rect.width(), rect.height(), false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte[] getRotatedData(byte[] data, Camera camera) {
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
Camera.Size size = parameters.getPreviewSize();
|
||||
int width = size.width;
|
||||
int height = size.height;
|
||||
private byte[] getRotatedData(byte[] data, Camera camera) {
|
||||
Camera.Size size = camera.getParameters().getPreviewSize();
|
||||
int width = size.width;
|
||||
int height = size.height;
|
||||
|
||||
int rotationCount = CameraViewManager.getRotationCount();
|
||||
|
||||
if (rotationCount == 1 || rotationCount == 3) {
|
||||
for (int i = 0; i < rotationCount; i++) {
|
||||
byte[] rotatedData = new byte[data.length];
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++)
|
||||
rotatedData[x * height + height - y - 1] = data[x + y * width];
|
||||
}
|
||||
data = rotatedData;
|
||||
int tmp = width;
|
||||
width = height;
|
||||
height = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
byte[] rotatedData = new byte[data.length];
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++)
|
||||
rotatedData[x * height + height - y - 1] = data[x + y * width];
|
||||
}
|
||||
return rotatedData;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,164 @@
|
||||
package com.wix.RNCameraKit.camera.barcode;
|
||||
|
||||
import com.google.zxing.LuminanceSource;
|
||||
|
||||
/**
|
||||
* This class is mostly copy of {@link com.google.zxing.PlanarYUVLuminanceSource} class.
|
||||
* The only difference is adding {@code rotateCounterClockwise()} method
|
||||
* It was copied due to PlanarYUVLuminanceSource being final, so it can not be extended
|
||||
* Zxing license - {https://github.com/zxing/zxing/blob/master/LICENSE}
|
||||
*/
|
||||
public class RotateLuminanceSource extends LuminanceSource {
|
||||
|
||||
private static final int THUMBNAIL_SCALE_FACTOR = 2;
|
||||
|
||||
private final byte[] yuvData;
|
||||
private final int dataWidth;
|
||||
private final int dataHeight;
|
||||
private final int left;
|
||||
private final int top;
|
||||
|
||||
RotateLuminanceSource(byte[] yuvData,
|
||||
int dataWidth,
|
||||
int dataHeight,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height,
|
||||
boolean reverseHorizontal) {
|
||||
super(width, height);
|
||||
|
||||
if (left + width > dataWidth || top + height > dataHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
|
||||
this.yuvData = yuvData;
|
||||
this.dataWidth = dataWidth;
|
||||
this.dataHeight = dataHeight;
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
if (reverseHorizontal) {
|
||||
reverseHorizontal(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
if (y < 0 || y >= getHeight()) {
|
||||
throw new IllegalArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
int width = getWidth();
|
||||
if (row == null || row.length < width) {
|
||||
row = new byte[width];
|
||||
}
|
||||
int offset = (y + top) * dataWidth + left;
|
||||
System.arraycopy(yuvData, offset, row, 0, width);
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if (width == dataWidth && height == dataHeight) {
|
||||
return yuvData;
|
||||
}
|
||||
|
||||
int area = width * height;
|
||||
byte[] matrix = new byte[area];
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if (width == dataWidth) {
|
||||
System.arraycopy(yuvData, inputOffset, matrix, 0, area);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
System.arraycopy(yuvData, inputOffset, matrix, outputOffset, width);
|
||||
inputOffset += dataWidth;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new RotateLuminanceSource(yuvData,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
this.left + left,
|
||||
this.top + top,
|
||||
width,
|
||||
height,
|
||||
false);
|
||||
}
|
||||
|
||||
public int[] renderThumbnail() {
|
||||
int width = getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
int height = getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
int[] pixels = new int[width * height];
|
||||
byte[] yuv = yuvData;
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff;
|
||||
pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
|
||||
}
|
||||
inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return width of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
public int getThumbnailWidth() {
|
||||
return getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return height of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
public int getThumbnailHeight() {
|
||||
return getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
private void reverseHorizontal(int width, int height) {
|
||||
byte[] yuvData = this.yuvData;
|
||||
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
|
||||
int middle = rowStart + width / 2;
|
||||
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
|
||||
byte temp = yuvData[x1];
|
||||
yuvData[x1] = yuvData[x2];
|
||||
yuvData[x2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRotateSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource rotateCounterClockwise() {
|
||||
byte[] rotatedData = new byte[yuvData.length];
|
||||
for (int y = 0; y < dataHeight; y++) {
|
||||
for (int x = 0; x < dataWidth; x++)
|
||||
rotatedData[x * dataHeight + dataHeight - y - 1] = yuvData[x + y * dataWidth];
|
||||
}
|
||||
return new RotateLuminanceSource(rotatedData, dataHeight, dataWidth, top, (dataWidth - left - getWidth()), getHeight(), getWidth(), false);
|
||||
}
|
||||
}
|
||||
4
android/src/main/res/values/colors.xml
Normal file
4
android/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="bg_dark">#BF000000</color>
|
||||
</resources>
|
||||
4
android/src/main/res/values/dimens.xml
Normal file
4
android/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="border_length">30dp</dimen>
|
||||
</resources>
|
||||
@ -25,18 +25,19 @@ def enableSeparateBuildPerCPUArchitecture = false
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '26.0.2'
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '26.0.3'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.camerakit"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
targetSdkVersion 25
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
multiDexEnabled true
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
@ -70,7 +71,8 @@ android {
|
||||
dependencies {
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile project(":rncamerakit")
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.android.support:appcompat-v7:25.0.1"
|
||||
compile 'com.android.support:multidex:1.0.3'
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package com.camerakit;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.support.multidex.MultiDex;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
@ -35,6 +37,12 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
MultiDex.install(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
@ -20,5 +21,6 @@ allprojects {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,8 +45,6 @@ export default class CameraScreen extends Component {
|
||||
scanBarcode={true}
|
||||
laserColor={"blue"}
|
||||
frameColor={"yellow"}
|
||||
|
||||
//onReadCode={((event) => Alert.alert(`Qr code found ${event.nativeEvent.codeStringValue} `))}
|
||||
onReadCode = {((event) => this.setState({ example: CheckingScreen}))}
|
||||
hideControls={true}
|
||||
// offsetForScannerFrame = {10}
|
||||
|
||||
2
index.js
2
index.js
@ -1,4 +1,4 @@
|
||||
// This index is the example app index,
|
||||
// if you look for react-naitve-camera-kit index,
|
||||
// please jump src/index 😅
|
||||
require('./ExampleCode/app');
|
||||
require('./example-js-code/app');
|
||||
|
||||
@ -51,7 +51,7 @@ export default class CameraScreen extends Component {
|
||||
hideControls={true}
|
||||
// offsetForScannerFrame = {10}
|
||||
// heightForScannerFrame = {300}
|
||||
colorForScannerFrame = {'blue'}
|
||||
colorForScannerFrame={'blue'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user