Skip to content

Commit

Permalink
feat: add barcodeFrameSize prop
Browse files Browse the repository at this point in the history
  • Loading branch information
phlpsong committed Feb 24, 2025
1 parent 84b33c6 commit 855b90b
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 12 deletions.
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

0 comments on commit 855b90b

Please sign in to comment.