diff --git a/android/build.gradle b/android/build.gradle index 802fc7b..bc17418 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -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' } diff --git a/android/src/main/java/com/wix/RNCameraKit/Utils.java b/android/src/main/java/com/wix/RNCameraKit/Utils.java index 2f8d095..f9e0f10 100644 --- a/android/src/main/java/com/wix/RNCameraKit/Utils.java +++ b/android/src/main/java/com/wix/RNCameraKit/Utils.java @@ -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(); + } } diff --git a/android/src/main/java/com/wix/RNCameraKit/camera/CameraView.java b/android/src/main/java/com/wix/RNCameraKit/camera/CameraView.java index b78af34..be30e00 100644 --- a/android/src/main/java/com/wix/RNCameraKit/camera/CameraView.java +++ b/android/src/main/java/com/wix/RNCameraKit/camera/CameraView.java @@ -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); } } } diff --git a/android/src/main/java/com/wix/RNCameraKit/camera/CameraViewManager.java b/android/src/main/java/com/wix/RNCameraKit/camera/CameraViewManager.java index 05be099..267e93a 100644 --- a/android/src/main/java/com/wix/RNCameraKit/camera/CameraViewManager.java +++ b/android/src/main/java/com/wix/RNCameraKit/camera/CameraViewManager.java @@ -51,10 +51,15 @@ public class CameraViewManager extends SimpleViewManager { 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 { } private static void releaseCamera() { + camera.setOneShotPreviewCallback(null); cameraReleased.set(true); camera.release(); } @@ -161,10 +167,10 @@ public class CameraViewManager extends SimpleViewManager { 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 { } 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); } }); } diff --git a/android/src/main/java/com/wix/RNCameraKit/camera/barcode/BarcodeFrame.java b/android/src/main/java/com/wix/RNCameraKit/camera/barcode/BarcodeFrame.java new file mode 100644 index 0000000..b4646d7 --- /dev/null +++ b/android/src/main/java/com/wix/RNCameraKit/camera/barcode/BarcodeFrame.java @@ -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); + } +} diff --git a/android/src/main/java/com/wix/RNCameraKit/camera/barcode/BarcodeScanner.java b/android/src/main/java/com/wix/RNCameraKit/camera/barcode/BarcodeScanner.java index 7f57fef..08a5911 100644 --- a/android/src/main/java/com/wix/RNCameraKit/camera/barcode/BarcodeScanner.java +++ b/android/src/main/java/com/wix/RNCameraKit/camera/barcode/BarcodeScanner.java @@ -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 ALL_FORMATS = new ArrayList<>(); - private List mFormats; - private ResultHandler mResultHandler; + private static final List 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 formats) { - mFormats = formats; - initMultiFormatReader(); - } - - public void setResultHandler(ResultHandler resultHandler) { - mResultHandler = resultHandler; - } - - public Collection getFormats() { - if(mFormats == null) { - return ALL_FORMATS; - } - return mFormats; - } - - private void initMultiFormatReader() { - Map hints = new EnumMap<>(DecodeHintType.class); - hints.put(DecodeHintType.POSSIBLE_FORMATS, getFormats()); + public BarcodeScanner(@NonNull Camera.PreviewCallback previewCallback, @NonNull ResultHandler resultHandler) { + Map 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; + } } diff --git a/android/src/main/java/com/wix/RNCameraKit/camera/barcode/RotateLuminanceSource.java b/android/src/main/java/com/wix/RNCameraKit/camera/barcode/RotateLuminanceSource.java new file mode 100644 index 0000000..a76a1f4 --- /dev/null +++ b/android/src/main/java/com/wix/RNCameraKit/camera/barcode/RotateLuminanceSource.java @@ -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); + } +} diff --git a/android/src/main/res/values/colors.xml b/android/src/main/res/values/colors.xml new file mode 100644 index 0000000..fbf35ef --- /dev/null +++ b/android/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #BF000000 + \ No newline at end of file diff --git a/android/src/main/res/values/dimens.xml b/android/src/main/res/values/dimens.xml new file mode 100644 index 0000000..16e2367 --- /dev/null +++ b/android/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + + 30dp + \ No newline at end of file diff --git a/example-android/app/build.gradle b/example-android/app/build.gradle index 383e3ff..0eb5cb8 100644 --- a/example-android/app/build.gradle +++ b/example-android/app/build.gradle @@ -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 } diff --git a/example-android/app/src/main/java/com/camerakit/MainApplication.java b/example-android/app/src/main/java/com/camerakit/MainApplication.java index 4fce4dc..f142e8e 100644 --- a/example-android/app/src/main/java/com/camerakit/MainApplication.java +++ b/example-android/app/src/main/java/com/camerakit/MainApplication.java @@ -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; diff --git a/example-android/build.gradle b/example-android/build.gradle index c5fecab..304b7c7 100644 --- a/example-android/build.gradle +++ b/example-android/build.gradle @@ -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() } } diff --git a/example-js-code/src/BarcodeScreen.js b/example-js-code/src/BarcodeScreen.js index 4ad756e..cde3eab 100644 --- a/example-js-code/src/BarcodeScreen.js +++ b/example-js-code/src/BarcodeScreen.js @@ -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} diff --git a/index.js b/index.js index 551bd4b..7c5303e 100644 --- a/index.js +++ b/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'); diff --git a/old-example/src/BarcodeScreen.js b/old-example/src/BarcodeScreen.js index 081c636..4e873cb 100644 --- a/old-example/src/BarcodeScreen.js +++ b/old-example/src/BarcodeScreen.js @@ -51,7 +51,7 @@ export default class CameraScreen extends Component { hideControls={true} // offsetForScannerFrame = {10} // heightForScannerFrame = {300} - colorForScannerFrame = {'blue'} + colorForScannerFrame={'blue'} /> ); }