Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make scan box size customizable #701

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions android/src/main/java/com/rncamerakit/CKCamera.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.util.Size
import com.facebook.react.uimanager.UIManagerHelper
import com.google.mlkit.vision.barcode.common.Barcode
import com.rncamerakit.events.*
Expand Down Expand Up @@ -106,6 +107,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
private var scanBarcode: Boolean = false
private var frameColor = Color.GREEN
private var laserColor = Color.RED
private var barcodeFrameSize: Size? = null

private fun getActivity() : Activity {
return currentContext.currentActivity!!
Expand Down Expand Up @@ -653,6 +655,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
fun setShowFrame(enabled: Boolean) {
if (enabled) {
barcodeFrame = BarcodeFrame(context)
barcodeFrame!!.setFrameSize(barcodeFrameSize)
val actualPreviewWidth = resources.displayMetrics.widthPixels
val actualPreviewHeight = resources.displayMetrics.heightPixels
val height: Int = convertDeviceHeightToSupportedAspectRatio(actualPreviewWidth, actualPreviewHeight)
Expand Down Expand Up @@ -680,6 +683,13 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
}
}

fun setBarcodeFrameSize(size: Size) {
barcodeFrameSize = size
if (barcodeFrame != null) {
barcodeFrame!!.setFrameSize(size)
}
}

private fun convertDeviceHeightToSupportedAspectRatio(actualWidth: Int, actualHeight: Int): Int {
val maxScreenRatio = 16 / 9f
return (if (actualHeight / actualWidth > maxScreenRatio) actualWidth * maxScreenRatio else actualHeight).toInt()
Expand Down
12 changes: 12 additions & 0 deletions android/src/main/java/com/rncamerakit/CKCameraManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package com.rncamerakit

import android.graphics.Color
import android.util.Log
import android.util.Size
import androidx.annotation.ColorInt
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.ReadableType
import com.facebook.react.common.MapBuilder
import com.facebook.react.common.ReactConstants.TAG
Expand Down Expand Up @@ -116,6 +118,16 @@ class CKCameraManager : SimpleViewManager<CKCamera>(), CKCameraManagerInterface<
view.setFrameColor(color ?: Color.GREEN)
}

@ReactProp(name = "barcodeFrameSize")
override fun setBarcodeFrameSize(view: CKCamera, frameSize: ReadableMap?) {
if (frameSize == null || !frameSize.hasKey("width") || !frameSize.hasKey("height")) {
return
}
val width = frameSize.getInt("width")
val height = frameSize.getInt("height")
view.setBarcodeFrameSize(Size(width, height))
}

@ReactProp(name = "outputPath")
override fun setOutputPath(view: CKCamera, path: String?) {
view.setOutputPath(path ?: "")
Expand Down
21 changes: 17 additions & 4 deletions android/src/main/java/com/rncamerakit/barcode/BarcodeFrame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.rncamerakit.barcode

import android.content.Context
import android.graphics.*
import android.util.Log
import android.util.Size
import android.view.View
import androidx.annotation.ColorInt

Expand All @@ -14,6 +16,7 @@ class BarcodeFrame(context: Context) : View(context) {
private var laserPaint: Paint = Paint()
var frameRect: Rect = Rect()

private var barcodeFrameSize = DEFAULT_SIZE
private var frameWidth = 0
private var frameHeight = 0
private var borderMargin = 0
Expand All @@ -31,14 +34,18 @@ class BarcodeFrame(context: Context) : View(context) {

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
calculateFrameRect()
}

private fun calculateFrameRect() {
val marginHeight = 40
val marginWidth = 40
val frameMaxWidth = 1200
val frameMaxHeight = 600
val frameMaxWidth = barcodeFrameSize.width * context.resources.displayMetrics.density
val frameMaxHeight = barcodeFrameSize.height * context.resources.displayMetrics.density
val frameMinWidth = 100
val frameMinHeight = 100
frameWidth = max(frameMinWidth, min(frameMaxWidth, measuredWidth - (marginWidth * 2)))
frameHeight = max(frameMinHeight, min(frameMaxHeight, measuredHeight - (marginHeight * 2)))
frameWidth = max(frameMinWidth, min(frameMaxWidth.toInt(), measuredWidth - (marginWidth * 2)))
frameHeight = max(frameMinHeight, min(frameMaxHeight.toInt(), measuredHeight - (marginHeight * 2)))
frameRect.left = (measuredWidth / 2) - (frameWidth / 2)
frameRect.right = (measuredWidth / 2) + (frameWidth / 2)
frameRect.top = (measuredHeight / 2) - (frameHeight / 2)
Expand Down Expand Up @@ -79,9 +86,15 @@ class BarcodeFrame(context: Context) : View(context) {
laserPaint.color = laserColor
}

fun setFrameSize(size: Size?) {
barcodeFrameSize = size ?: DEFAULT_SIZE
calculateFrameRect()
}

companion object {
private const val STROKE_WIDTH = 5
private const val ANIMATION_SPEED = 4
private val DEFAULT_SIZE = Size(300, 150)
}

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ColorPropConverter;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.BaseViewManager;
import com.facebook.react.uimanager.BaseViewManagerDelegate;
import com.facebook.react.uimanager.LayoutShadowNode;
Expand Down Expand Up @@ -56,6 +57,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "frameColor":
mViewManager.setFrameColor(view, ColorPropConverter.getColor(value, view.getContext()));
break;
case "barcodeFrameSize":
mViewManager.setBarcodeFrameSize(view, value == null ? null : (ReadableMap) value);
break;
case "ratioOverlay":
mViewManager.setRatioOverlay(view, value == null ? null : (String) value);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableMap;

public interface CKCameraManagerInterface<T extends View> {
void setFlashMode(T view, @Nullable String value);
Expand All @@ -24,6 +25,7 @@ public interface CKCameraManagerInterface<T extends View> {
void setShowFrame(T view, boolean value);
void setLaserColor(T view, @Nullable Integer value);
void setFrameColor(T view, @Nullable Integer value);
void setBarcodeFrameSize(T view, @Nullable ReadableMap value);
void setRatioOverlay(T view, @Nullable String value);
void setRatioOverlayColor(T view, @Nullable Integer value);
void setResetFocusTimeout(T view, int value);
Expand Down
1 change: 1 addition & 0 deletions example/src/BarcodeScreenExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ const BarcodeExample = ({ onBack }: { onBack: () => void }) => {
frameColor="white"
scanBarcode
showFrame
barcodeFrameSize={{ width: 300, height: 150 }}
onReadCode={(event) => {
Vibration.vibrate(100);
setBarcode(event.nativeEvent.codeStringValue);
Expand Down
1 change: 1 addition & 0 deletions ios/ReactNativeCameraKit/CKCameraManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ @interface RCT_EXTERN_MODULE(CKCameraManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(scanThrottleDelay, NSInteger)
RCT_EXPORT_VIEW_PROPERTY(laserColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(frameColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(barcodeFrameSize, NSDictionary)

RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onCaptureButtonPressIn, RCTDirectEventBlock)
Expand Down
1 change: 1 addition & 0 deletions ios/ReactNativeCameraKit/CameraProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ protocol CameraProtocol: AnyObject, FocusInterfaceViewDelegate {
func update(maxZoom: Double?)
func update(resizeMode: ResizeMode)
func update(maxPhotoQualityPrioritization: MaxPhotoQualityPrioritization?)
func update(barcodeFrameSize: CGSize?)

func zoomPinchStart()
func zoomPinchChange(pinchScale: CGFloat)
Expand Down
10 changes: 8 additions & 2 deletions ios/ReactNativeCameraKit/CameraView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public class CameraView: UIView {
@objc public var scanThrottleDelay = 2000
@objc public var frameColor: UIColor?
@objc public var laserColor: UIColor?
@objc public var barcodeFrameSize: NSDictionary?

// other
@objc public var onOrientationChange: RCTDirectEventBlock?
@objc public var onZoom: RCTDirectEventBlock?
Expand Down Expand Up @@ -229,15 +231,19 @@ public class CameraView: UIView {
})
}



if changedProps.contains("showFrame") || changedProps.contains("scanBarcode") {
DispatchQueue.main.async {
self.scannerInterfaceView.isHidden = !self.showFrame

self.camera.update(scannerFrameSize: self.showFrame ? self.scannerInterfaceView.frameSize : nil)
}
}

if changedProps.contains("barcodeFrameSize"), let barcodeFrameSize, showFrame, scanBarcode {
if let width = barcodeFrameSize["width"] as? CGFloat, let height = barcodeFrameSize["height"] as? CGFloat {
self.scannerInterfaceView.update(frameSize: CGSize(width: width, height: height))
}
}

if changedProps.contains("laserColor"), let laserColor {
scannerInterfaceView.update(laserColor: laserColor)
Expand Down
5 changes: 5 additions & 0 deletions ios/ReactNativeCameraKit/RealCamera.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
private var focusFinished: (() -> Void)?
private var onBarcodeRead: ((_ barcode: String,_ codeFormat : CodeFormat) -> Void)?
private var scannerFrameSize: CGRect? = nil
private var barcodeFrameSize: CGSize? = nil
private var onOrientationChange: RCTDirectEventBlock?
private var onZoomCallback: RCTDirectEventBlock?
private var lastOnZoom: Double?
Expand Down Expand Up @@ -387,6 +388,10 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
}
}

func update(barcodeFrameSize: CGSize?) {
self.barcodeFrameSize = barcodeFrameSize
}

func update(scannerFrameSize: CGRect?) {
guard self.scannerFrameSize != scannerFrameSize else { return }
self.sessionQueue.async {
Expand Down
21 changes: 15 additions & 6 deletions ios/ReactNativeCameraKit/ScannerInterfaceView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ class ScannerInterfaceView: UIView {
private let leftOverlayView = UIView()
private let rightOverlayView = UIView()

private var barcodeFrameWidth: CGFloat = 300
private var barcodeFrameHeight: CGFloat = 150

// MARK: - Constants

private let frameOffset: CGFloat = 30
private let frameHeight: CGFloat = 200
private let overlayColor: UIColor = .black.withAlphaComponent(0.4)

// MARK: - Lifecycle
Expand Down Expand Up @@ -46,7 +48,7 @@ class ScannerInterfaceView: UIView {
override func draw(_ rect: CGRect) {
super.draw(rect)

frameView.frame = CGRect(x: 0, y: 0, width: bounds.size.width - 2 * frameOffset, height: frameHeight)
frameView.frame = CGRect(x: 0, y: 0, width: barcodeFrameWidth, height: barcodeFrameHeight)
frameView.center = center

updateOverlaySize(frameView.frame)
Expand Down Expand Up @@ -74,16 +76,23 @@ class ScannerInterfaceView: UIView {
frameView.update(laserColor: laserColor)
}

func update(frameSize: CGSize) {
barcodeFrameWidth = frameSize.width
barcodeFrameHeight = frameSize.height
frameView.setNeedsDisplay()
setNeedsDisplay()
}

// MARK: - Private

private func updateOverlaySize(_ frameRect: CGRect) {
topOverlayView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frameRect.origin.y)
leftOverlayView.frame = CGRect(x: 0, y: frameRect.origin.y, width: frameOffset, height: frameHeight)
rightOverlayView.frame = CGRect(x: frameRect.size.width + frameOffset, y: frameRect.origin.y, width: frameOffset, height: frameHeight)
leftOverlayView.frame = CGRect(x: 0, y: frameRect.origin.y, width: (frame.size.width - barcodeFrameWidth) / 2, height: barcodeFrameHeight)
rightOverlayView.frame = CGRect(x: (frame.size.width - barcodeFrameWidth) / 2 + barcodeFrameWidth, y: frameRect.origin.y, width: (frame.size.width - barcodeFrameWidth) / 2, height: barcodeFrameHeight)
bottomOverlayView.frame = CGRect(
x: 0,
y: frameRect.origin.y + frameHeight,
y: frameRect.origin.y + barcodeFrameHeight,
width: frame.size.width,
height: frame.size.height - frameRect.origin.y - frameHeight)
height: frame.size.height - frameRect.origin.y - barcodeFrameHeight)
}
}
5 changes: 5 additions & 0 deletions ios/ReactNativeCameraKit/SimulatorCamera.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class SimulatorCamera: CameraProtocol {
private var zoom: Double?
private var maxZoom: Double?
private var resizeMode: ResizeMode = .contain
private var barcodeFrameSize: CGSize?

var previewView: UIView { mockPreview }

Expand Down Expand Up @@ -196,4 +197,8 @@ class SimulatorCamera: CameraProtocol {
}
}
}

func update(barcodeFrameSize: CGSize?) {
self.barcodeFrameSize = barcodeFrameSize
}
}
1 change: 1 addition & 0 deletions src/CameraProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export interface CameraProps extends ViewProps {
showFrame?: boolean;
laserColor?: number | string;
frameColor?: number | string;
barcodeFrameSize?: { width: number; height: number };
onReadCode?: (event: OnReadCodeData) => void;
// Specific to iOS
ratioOverlay?: string;
Expand Down