From 6d0bed7ce0860e14e78e830eb546ec558de1f827 Mon Sep 17 00:00:00 2001 From: Ilya Pasternak Date: Mon, 16 Jun 2025 09:54:16 +0300 Subject: [PATCH 1/7] add forbiddenBarcodeTypes property --- .../src/main/java/com/rncamerakit/CKCamera.kt | 36 +++++++++++++++++-- .../java/com/rncamerakit/CKCameraManager.kt | 5 +++ .../main/java/com/rncamerakit/CodeFormat.kt | 7 ++++ .../viewmanagers/CKCameraManagerDelegate.java | 4 +++ .../CKCameraManagerInterface.java | 2 ++ ios/ReactNativeCameraKit/CKCameraManager.m | 1 + .../CKCameraViewComponentView.mm | 9 +++++ ios/ReactNativeCameraKit/CameraView.swift | 27 +++++++++++--- src/CameraProps.ts | 1 + src/index.ts | 12 ++++++- src/specs/CameraNativeComponent.ts | 1 + src/types.ts | 1 + 12 files changed, 99 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/rncamerakit/CKCamera.kt b/android/src/main/java/com/rncamerakit/CKCamera.kt index 98aa78474..47d7a2bf7 100644 --- a/android/src/main/java/com/rncamerakit/CKCamera.kt +++ b/android/src/main/java/com/rncamerakit/CKCamera.kt @@ -28,6 +28,7 @@ import androidx.lifecycle.LifecycleObserver import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.Promise import com.facebook.react.bridge.WritableMap +import com.facebook.react.bridge.ReadableArray import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.events.RCTEventEmitter import com.rncamerakit.barcode.BarcodeFrame @@ -108,6 +109,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs private var frameColor = Color.GREEN private var laserColor = Color.RED private var barcodeFrameSize: Size? = null + private var forbiddenBarcodeTypes: ReadableArray? = null private fun getActivity() : Activity { return currentContext.currentActivity!! @@ -334,9 +336,23 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs return@QRCodeAnalyzer } + val forbiddenTypes = convertForbiddenBarcodeTypes() + + val filteredByType = if (forbiddenTypes.isEmpty()) { + barcodes + } else { + barcodes.filterNot { barcode -> + forbiddenTypes.contains(barcode.format) + } + } + + if (filteredByType.isEmpty()) { + return@QRCodeAnalyzer + } + val barcodeFrame = barcodeFrame; if (barcodeFrame == null) { - onBarcodeRead(barcodes) + onBarcodeRead(filteredByType) return@QRCodeAnalyzer } @@ -344,7 +360,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs val scaleX = viewFinder.width.toFloat() / imageSize.height val scaleY = viewFinder.height.toFloat() / imageSize.width - val filteredBarcodes = barcodes.filter { barcode -> + val filteredBarcodes = filteredByType.filter { barcode -> val barcodeBoundingBox = barcode.boundingBox ?: return@filter false; val scaledBarcodeBoundingBox = Rect( (barcodeBoundingBox.left * scaleX).toInt(), @@ -691,6 +707,10 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs } } + fun setForbiddenBarcodeTypes(types: ReadableArray?) { + forbiddenBarcodeTypes = types + } + private fun convertDeviceHeightToSupportedAspectRatio(actualWidth: Int, actualHeight: Int): Int { val maxScreenRatio = 16 / 9f return (if (actualHeight / actualWidth > maxScreenRatio) actualWidth * maxScreenRatio else actualHeight).toInt() @@ -711,6 +731,18 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs return false } + private fun convertForbiddenBarcodeTypes(): Set { + val types = forbiddenBarcodeTypes + if (types == null || types.size() == 0) { + return emptySet() + } + + return (0 until types.size()).mapNotNull { index -> + val typeName = types.getString(index) ?: return@mapNotNull null + CodeFormat.getBarcodeType(typeName) + }.toSet() + } + companion object { private const val TAG = "CameraKit" diff --git a/android/src/main/java/com/rncamerakit/CKCameraManager.kt b/android/src/main/java/com/rncamerakit/CKCameraManager.kt index 04999082b..ccb8dc5d9 100644 --- a/android/src/main/java/com/rncamerakit/CKCameraManager.kt +++ b/android/src/main/java/com/rncamerakit/CKCameraManager.kt @@ -143,6 +143,11 @@ class CKCameraManager : SimpleViewManager(), CKCameraManagerInterface< view.setShutterPhotoSound(enabled); } + @ReactProp(name = "forbiddenBarcodeTypes") + override fun setForbiddenBarcodeTypes(view: CKCamera, types: ReadableArray?) { + view.setForbiddenBarcodeTypes(types) + } + // Methods only available on iOS override fun setRatioOverlay(view: CKCamera?, value: String?) = Unit diff --git a/android/src/main/java/com/rncamerakit/CodeFormat.kt b/android/src/main/java/com/rncamerakit/CodeFormat.kt index 207c7151d..cafa87b08 100644 --- a/android/src/main/java/com/rncamerakit/CodeFormat.kt +++ b/android/src/main/java/com/rncamerakit/CodeFormat.kt @@ -10,6 +10,7 @@ enum class CodeFormat(val code: String) { EAN_13("ean-13"), EAN_8("ean-8"), ITF("itf"), + UPC_A("upc-a"), UPC_E("upc-e"), QR("qr"), PDF_417("pdf-417"), @@ -26,6 +27,7 @@ enum class CodeFormat(val code: String) { EAN_13 -> Barcode.FORMAT_EAN_13 EAN_8 -> Barcode.FORMAT_EAN_8 ITF -> Barcode.FORMAT_ITF + UPC_A -> Barcode.FORMAT_UPC_A UPC_E -> Barcode.FORMAT_UPC_E QR -> Barcode.FORMAT_QR_CODE PDF_417 -> Barcode.FORMAT_PDF417 @@ -45,6 +47,7 @@ enum class CodeFormat(val code: String) { Barcode.FORMAT_EAN_13 -> EAN_13 Barcode.FORMAT_EAN_8 -> EAN_8 Barcode.FORMAT_ITF -> ITF + Barcode.FORMAT_UPC_A -> UPC_A Barcode.FORMAT_UPC_E -> UPC_E Barcode.FORMAT_QR_CODE -> QR Barcode.FORMAT_PDF417 -> PDF_417 @@ -52,5 +55,9 @@ enum class CodeFormat(val code: String) { Barcode.FORMAT_DATA_MATRIX -> DATA_MATRIX else -> UNKNOWN } + fun getBarcodeType(typeString: String?): Int { + val codeFormat = values().firstOrNull { it.code == typeString } ?: UNKNOWN + return codeFormat.toBarcodeType() + } } } diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java index c37c7f009..a446614c8 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java @@ -13,6 +13,7 @@ import androidx.annotation.Nullable; import com.facebook.react.bridge.ColorPropConverter; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableArray; import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; import com.facebook.react.uimanager.LayoutShadowNode; @@ -87,6 +88,9 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "shutterAnimationDuration": mViewManager.setShutterAnimationDuration(view, value == null ? 0 : ((Double) value).intValue()); break; + case "forbiddenBarcodeTypes": + mViewManager.setForbiddenBarcodeTypes(view, (ReadableArray) value); + break; case "outputPath": mViewManager.setOutputPath(view, value == null ? null : (String) value); break; diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java index b6ead01bb..81a3895ba 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java @@ -12,6 +12,7 @@ import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableArray; public interface CKCameraManagerInterface { void setFlashMode(T view, @Nullable String value); @@ -36,4 +37,5 @@ public interface CKCameraManagerInterface { void setShutterPhotoSound(T view, boolean value); void setShutterAnimationDuration(T view, int value); void setOutputPath(T view, @Nullable String value); + void setForbiddenBarcodeTypes(T view, @Nullable ReadableArray value); } diff --git a/ios/ReactNativeCameraKit/CKCameraManager.m b/ios/ReactNativeCameraKit/CKCameraManager.m index 57503ba39..f93b31745 100644 --- a/ios/ReactNativeCameraKit/CKCameraManager.m +++ b/ios/ReactNativeCameraKit/CKCameraManager.m @@ -30,6 +30,7 @@ @interface RCT_EXTERN_MODULE(CKCameraManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(laserColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(frameColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(barcodeFrameSize, NSDictionary) +RCT_EXPORT_VIEW_PROPERTY(forbiddenBarcodeTypes, NSArray) RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onCaptureButtonPressIn, RCTDirectEventBlock) diff --git a/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm b/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm index 356d35be7..b32fa39cd 100644 --- a/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm +++ b/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm @@ -243,6 +243,15 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & _view.barcodeFrameSize = @{@"width": @(barcodeWidth), @"height": @(barcodeHeight)}; [changedProps addObject:@"barcodeFrameSize"]; } + folly::dynamic forbiddenBarcodeTypesDynamic = folly::dynamic::array(); + for (const auto& type : newProps.forbiddenBarcodeTypes) { + forbiddenBarcodeTypesDynamic.push_back(type); + } + id forbiddenBarcodeTypes = CKConvertFollyDynamicToId(forbiddenBarcodeTypesDynamic); + if (forbiddenBarcodeTypes != nil && [forbiddenBarcodeTypes isKindOfClass:NSArray.class]) { + _view.forbiddenBarcodeTypes = forbiddenBarcodeTypes; + [changedProps addObject:@"forbiddenBarcodeTypes"]; + } [super updateProps:props oldProps:oldProps]; diff --git a/ios/ReactNativeCameraKit/CameraView.swift b/ios/ReactNativeCameraKit/CameraView.swift index ad161e5ab..2e65c5e26 100644 --- a/ios/ReactNativeCameraKit/CameraView.swift +++ b/ios/ReactNativeCameraKit/CameraView.swift @@ -49,6 +49,7 @@ public class CameraView: UIView { @objc public var frameColor: UIColor? @objc public var laserColor: UIColor? @objc public var barcodeFrameSize: NSDictionary? + @objc public var forbiddenBarcodeTypes: NSArray? // other @objc public var onOrientationChange: RCTDirectEventBlock? @@ -81,12 +82,14 @@ public class CameraView: UIView { } private func setupCamera() { if hasPropBeenSetup && hasPermissionBeenGranted && !hasCameraBeenSetup { + let allowedBarcodeTypes = convertAllowedBarcodeTypes() + hasCameraBeenSetup = true #if targetEnvironment(macCatalyst) // Force front camera on Mac Catalyst during initial setup - camera.setup(cameraType: .front, supportedBarcodeType: scanBarcode && onReadCode != nil ? supportedBarcodeType : []) + camera.setup(cameraType: .front, supportedBarcodeType: scanBarcode && onReadCode != nil ? allowedBarcodeTypes : []) #else - camera.setup(cameraType: cameraType, supportedBarcodeType: scanBarcode && onReadCode != nil ? supportedBarcodeType : []) + camera.setup(cameraType: cameraType, supportedBarcodeType: scanBarcode && onReadCode != nil ? allowedBarcodeTypes : []) #endif } } @@ -235,9 +238,11 @@ public class CameraView: UIView { } // Scanner - if changedProps.contains("scanBarcode") || changedProps.contains("onReadCode") { + if changedProps.contains("scanBarcode") || changedProps.contains("onReadCode") || changedProps.contains("forbiddenBarcodeTypes") { + let allowedBarcodeTypes: [CodeFormat] = convertAllowedBarcodeTypes() + camera.isBarcodeScannerEnabled(scanBarcode, - supportedBarcodeTypes: supportedBarcodeType, + supportedBarcodeTypes: allowedBarcodeTypes, onBarcodeRead: { [weak self] (barcode, codeFormat) in self?.onBarcodeRead(barcode: barcode, codeFormat: codeFormat) }) @@ -423,6 +428,20 @@ public class CameraView: UIView { onReadCode?(["codeStringValue": barcode,"codeFormat":codeFormat.rawValue]) } + private func convertAllowedBarcodeTypes() -> [CodeFormat] { + guard let forbiddenTypes = forbiddenBarcodeTypes as? [String] else { + return CodeFormat.allCases + } + + let forbiddenFormats = forbiddenTypes.compactMap { type in + return CodeFormat(rawValue: type) + } + + return CodeFormat.allCases.filter { codeFormat in + !forbiddenFormats.contains(codeFormat) + } + } + // MARK: - Gesture selectors @objc func handlePinchToZoomRecognizer(_ pinchRecognizer: UIPinchGestureRecognizer) { diff --git a/src/CameraProps.ts b/src/CameraProps.ts index cb63710dc..ba73a48e9 100644 --- a/src/CameraProps.ts +++ b/src/CameraProps.ts @@ -113,4 +113,5 @@ export interface CameraProps extends ViewProps { shutterPhotoSound?: boolean; onCaptureButtonPressIn?: ({ nativeEvent: {} }) => void; onCaptureButtonPressOut?: ({ nativeEvent: {} }) => void; + forbiddenBarcodeTypes?: CodeFormat[]; } diff --git a/src/index.ts b/src/index.ts index 528e0956d..0f0c296ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import { type TorchMode, type ZoomMode, type ResizeMode, + type CodeFormat, } from './types'; const { CameraKit } = NativeModules; @@ -25,4 +26,13 @@ export const Orientation = { export default CameraKit; export { Camera, CameraType }; -export type { TorchMode, FlashMode, FocusMode, ZoomMode, CameraApi, CaptureData, ResizeMode }; +export type { + TorchMode, + FlashMode, + FocusMode, + ZoomMode, + CameraApi, + CaptureData, + ResizeMode, + CodeFormat, +}; diff --git a/src/specs/CameraNativeComponent.ts b/src/specs/CameraNativeComponent.ts index 35ba97fa7..1c73d63c9 100644 --- a/src/specs/CameraNativeComponent.ts +++ b/src/specs/CameraNativeComponent.ts @@ -48,6 +48,7 @@ export interface NativeProps extends ViewProps { shutterPhotoSound?: boolean; onCaptureButtonPressIn?: DirectEventHandler<{}>; onCaptureButtonPressOut?: DirectEventHandler<{}>; + forbiddenBarcodeTypes?: string[]; // not mentioned in props but available on the native side shutterAnimationDuration?: Int32; diff --git a/src/types.ts b/src/types.ts index acae32bad..80d17f8a4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,6 +11,7 @@ export type CodeFormat = | 'ean-13' | 'ean-8' | 'itf' + | 'upc-a' | 'upc-e' | 'qr' | 'pdf-417' From cc6d18ceaa385b54f2f27c0a9657ebaa9f26e9e8 Mon Sep 17 00:00:00 2001 From: Ilya Pasternak Date: Wed, 2 Jul 2025 14:52:26 +0300 Subject: [PATCH 2/7] change forbiddenBarcodeTypes property to allowedBarcodeTypes --- .../src/main/java/com/rncamerakit/CKCamera.kt | 18 +++++++++--------- .../java/com/rncamerakit/CKCameraManager.kt | 6 +++--- .../viewmanagers/CKCameraManagerDelegate.java | 4 ++-- .../viewmanagers/CKCameraManagerInterface.java | 2 +- ios/ReactNativeCameraKit/CKCameraManager.m | 2 +- .../CKCameraViewComponentView.mm | 14 +++++++------- ios/ReactNativeCameraKit/CameraView.swift | 14 ++++---------- src/CameraProps.ts | 2 +- src/specs/CameraNativeComponent.ts | 2 +- 9 files changed, 29 insertions(+), 35 deletions(-) diff --git a/android/src/main/java/com/rncamerakit/CKCamera.kt b/android/src/main/java/com/rncamerakit/CKCamera.kt index 47d7a2bf7..48e7d5067 100644 --- a/android/src/main/java/com/rncamerakit/CKCamera.kt +++ b/android/src/main/java/com/rncamerakit/CKCamera.kt @@ -109,7 +109,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs private var frameColor = Color.GREEN private var laserColor = Color.RED private var barcodeFrameSize: Size? = null - private var forbiddenBarcodeTypes: ReadableArray? = null + private var allowedBarcodeTypes: ReadableArray? = null private fun getActivity() : Activity { return currentContext.currentActivity!! @@ -336,13 +336,13 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs return@QRCodeAnalyzer } - val forbiddenTypes = convertForbiddenBarcodeTypes() + val allowedTypes = convertAllowedBarcodeTypes() - val filteredByType = if (forbiddenTypes.isEmpty()) { + val filteredByType = if (allowedTypes.isEmpty()) { barcodes } else { - barcodes.filterNot { barcode -> - forbiddenTypes.contains(barcode.format) + barcodes.filter { barcode -> + allowedTypes.contains(barcode.format) } } @@ -707,8 +707,8 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs } } - fun setForbiddenBarcodeTypes(types: ReadableArray?) { - forbiddenBarcodeTypes = types + fun setAllowedBarcodeTypes(types: ReadableArray?) { + allowedBarcodeTypes = types } private fun convertDeviceHeightToSupportedAspectRatio(actualWidth: Int, actualHeight: Int): Int { @@ -731,8 +731,8 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs return false } - private fun convertForbiddenBarcodeTypes(): Set { - val types = forbiddenBarcodeTypes + private fun convertAllowedBarcodeTypes(): Set { + val types = allowedBarcodeTypes if (types == null || types.size() == 0) { return emptySet() } diff --git a/android/src/main/java/com/rncamerakit/CKCameraManager.kt b/android/src/main/java/com/rncamerakit/CKCameraManager.kt index ccb8dc5d9..902724b67 100644 --- a/android/src/main/java/com/rncamerakit/CKCameraManager.kt +++ b/android/src/main/java/com/rncamerakit/CKCameraManager.kt @@ -143,9 +143,9 @@ class CKCameraManager : SimpleViewManager(), CKCameraManagerInterface< view.setShutterPhotoSound(enabled); } - @ReactProp(name = "forbiddenBarcodeTypes") - override fun setForbiddenBarcodeTypes(view: CKCamera, types: ReadableArray?) { - view.setForbiddenBarcodeTypes(types) + @ReactProp(name = "allowedBarcodeTypes") + override fun setAllowedBarcodeTypes(view: CKCamera, types: ReadableArray?) { + view.setAllowedBarcodeTypes(types) } // Methods only available on iOS diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java index a446614c8..1326d0d97 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java @@ -88,8 +88,8 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "shutterAnimationDuration": mViewManager.setShutterAnimationDuration(view, value == null ? 0 : ((Double) value).intValue()); break; - case "forbiddenBarcodeTypes": - mViewManager.setForbiddenBarcodeTypes(view, (ReadableArray) value); + case "allowedBarcodeTypes": + mViewManager.setAllowedBarcodeTypes(view, (ReadableArray) value); break; case "outputPath": mViewManager.setOutputPath(view, value == null ? null : (String) value); diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java index 81a3895ba..4f1b4e36b 100644 --- a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java +++ b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java @@ -37,5 +37,5 @@ public interface CKCameraManagerInterface { void setShutterPhotoSound(T view, boolean value); void setShutterAnimationDuration(T view, int value); void setOutputPath(T view, @Nullable String value); - void setForbiddenBarcodeTypes(T view, @Nullable ReadableArray value); + void setAllowedBarcodeTypes(T view, @Nullable ReadableArray value); } diff --git a/ios/ReactNativeCameraKit/CKCameraManager.m b/ios/ReactNativeCameraKit/CKCameraManager.m index f93b31745..6f88fd979 100644 --- a/ios/ReactNativeCameraKit/CKCameraManager.m +++ b/ios/ReactNativeCameraKit/CKCameraManager.m @@ -30,7 +30,7 @@ @interface RCT_EXTERN_MODULE(CKCameraManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(laserColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(frameColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(barcodeFrameSize, NSDictionary) -RCT_EXPORT_VIEW_PROPERTY(forbiddenBarcodeTypes, NSArray) +RCT_EXPORT_VIEW_PROPERTY(allowedBarcodeTypes, NSArray) RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onCaptureButtonPressIn, RCTDirectEventBlock) diff --git a/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm b/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm index 2f7da7548..8a5c9c5c2 100644 --- a/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm +++ b/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm @@ -243,14 +243,14 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & _view.barcodeFrameSize = @{@"width": @(barcodeWidth), @"height": @(barcodeHeight)}; [changedProps addObject:@"barcodeFrameSize"]; } - folly::dynamic forbiddenBarcodeTypesDynamic = folly::dynamic::array(); - for (const auto& type : newProps.forbiddenBarcodeTypes) { - forbiddenBarcodeTypesDynamic.push_back(type); + folly::dynamic allowedBarcodeTypesDynamic = folly::dynamic::array(); + for (const auto& type : newProps.allowedBarcodeTypes) { + allowedBarcodeTypesDynamic.push_back(type); } - id forbiddenBarcodeTypes = CKConvertFollyDynamicToId(forbiddenBarcodeTypesDynamic); - if (forbiddenBarcodeTypes != nil && [forbiddenBarcodeTypes isKindOfClass:NSArray.class]) { - _view.forbiddenBarcodeTypes = forbiddenBarcodeTypes; - [changedProps addObject:@"forbiddenBarcodeTypes"]; + id allowedBarcodeTypes = CKConvertFollyDynamicToId(allowedBarcodeTypesDynamic); + if (allowedBarcodeTypes != nil && [allowedBarcodeTypes isKindOfClass:NSArray.class]) { + _view.allowedBarcodeTypes = allowedBarcodeTypes; + [changedProps addObject:@"allowedBarcodeTypes"]; } diff --git a/ios/ReactNativeCameraKit/CameraView.swift b/ios/ReactNativeCameraKit/CameraView.swift index 2e65c5e26..51586a3ba 100644 --- a/ios/ReactNativeCameraKit/CameraView.swift +++ b/ios/ReactNativeCameraKit/CameraView.swift @@ -49,7 +49,7 @@ public class CameraView: UIView { @objc public var frameColor: UIColor? @objc public var laserColor: UIColor? @objc public var barcodeFrameSize: NSDictionary? - @objc public var forbiddenBarcodeTypes: NSArray? + @objc public var allowedBarcodeTypes: NSArray? // other @objc public var onOrientationChange: RCTDirectEventBlock? @@ -238,7 +238,7 @@ public class CameraView: UIView { } // Scanner - if changedProps.contains("scanBarcode") || changedProps.contains("onReadCode") || changedProps.contains("forbiddenBarcodeTypes") { + if changedProps.contains("scanBarcode") || changedProps.contains("onReadCode") || changedProps.contains("allowedBarcodeTypes") { let allowedBarcodeTypes: [CodeFormat] = convertAllowedBarcodeTypes() camera.isBarcodeScannerEnabled(scanBarcode, @@ -429,17 +429,11 @@ public class CameraView: UIView { } private func convertAllowedBarcodeTypes() -> [CodeFormat] { - guard let forbiddenTypes = forbiddenBarcodeTypes as? [String] else { + guard let allowedTypes = allowedBarcodeTypes as? [String] else { return CodeFormat.allCases } - let forbiddenFormats = forbiddenTypes.compactMap { type in - return CodeFormat(rawValue: type) - } - - return CodeFormat.allCases.filter { codeFormat in - !forbiddenFormats.contains(codeFormat) - } + return allowedTypes.compactMap { CodeFormat(rawValue: $0) } } // MARK: - Gesture selectors diff --git a/src/CameraProps.ts b/src/CameraProps.ts index ba73a48e9..4a18c0698 100644 --- a/src/CameraProps.ts +++ b/src/CameraProps.ts @@ -113,5 +113,5 @@ export interface CameraProps extends ViewProps { shutterPhotoSound?: boolean; onCaptureButtonPressIn?: ({ nativeEvent: {} }) => void; onCaptureButtonPressOut?: ({ nativeEvent: {} }) => void; - forbiddenBarcodeTypes?: CodeFormat[]; + allowedBarcodeTypes?: CodeFormat[]; } diff --git a/src/specs/CameraNativeComponent.ts b/src/specs/CameraNativeComponent.ts index 1c73d63c9..1bce82101 100644 --- a/src/specs/CameraNativeComponent.ts +++ b/src/specs/CameraNativeComponent.ts @@ -48,7 +48,7 @@ export interface NativeProps extends ViewProps { shutterPhotoSound?: boolean; onCaptureButtonPressIn?: DirectEventHandler<{}>; onCaptureButtonPressOut?: DirectEventHandler<{}>; - forbiddenBarcodeTypes?: string[]; + allowedBarcodeTypes?: string[]; // not mentioned in props but available on the native side shutterAnimationDuration?: Int32; From f8be0f08e89f208459c2f0b881ffe92ab861eeda Mon Sep 17 00:00:00 2001 From: Kseniya Vinnichek Date: Wed, 19 Nov 2025 11:32:43 +0100 Subject: [PATCH 3/7] correct allowed barcode types validation, remove invalid reference, and prevent fallback errors --- .../src/main/java/com/rncamerakit/CKCamera.kt | 60 +++++++++++-------- ios/ReactNativeCameraKit/CameraView.swift | 15 ++--- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/android/src/main/java/com/rncamerakit/CKCamera.kt b/android/src/main/java/com/rncamerakit/CKCamera.kt index 48e7d5067..9ab197b1c 100644 --- a/android/src/main/java/com/rncamerakit/CKCamera.kt +++ b/android/src/main/java/com/rncamerakit/CKCamera.kt @@ -109,7 +109,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs private var frameColor = Color.GREEN private var laserColor = Color.RED private var barcodeFrameSize: Size? = null - private var allowedBarcodeTypes: ReadableArray? = null + private var allowedBarcodeTypes: Array? = null private fun getActivity() : Activity { return currentContext.currentActivity!! @@ -332,9 +332,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs if (scanBarcode) { val analyzer = QRCodeAnalyzer { barcodes, imageSize -> - if (barcodes.isEmpty()) { - return@QRCodeAnalyzer - } + if (barcodes.isEmpty()) return@QRCodeAnalyzer val allowedTypes = convertAllowedBarcodeTypes() @@ -342,7 +340,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs barcodes } else { barcodes.filter { barcode -> - allowedTypes.contains(barcode.format) + barcode.format in allowedTypes } } @@ -350,29 +348,35 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs return@QRCodeAnalyzer } - val barcodeFrame = barcodeFrame; - if (barcodeFrame == null) { + val frame = barcodeFrame + val vf = viewFinder + + // If there is no frame filter → return everything + if (frame == null) { onBarcodeRead(filteredByType) return@QRCodeAnalyzer } + val frameRect = frame.frameRect + // Calculate scaling factors (image is always rotated by 90 degrees) - val scaleX = viewFinder.width.toFloat() / imageSize.height - val scaleY = viewFinder.height.toFloat() / imageSize.width + val scaleX = vf.width.toFloat() / imageSize.height + val scaleY = vf.height.toFloat() / imageSize.width - val filteredBarcodes = filteredByType.filter { barcode -> - val barcodeBoundingBox = barcode.boundingBox ?: return@filter false; + // Keep only barcodes that fall inside the frame rect + val insideFrameBarcodes = formatFiltered.filter { barcode -> + val barcodeBoundingBox = barcode.boundingBox ?: return@filter false val scaledBarcodeBoundingBox = Rect( (barcodeBoundingBox.left * scaleX).toInt(), (barcodeBoundingBox.top * scaleY).toInt(), (barcodeBoundingBox.right * scaleX).toInt(), (barcodeBoundingBox.bottom * scaleY).toInt() ) - barcodeFrame.frameRect.contains(scaledBarcodeBoundingBox) + frameRect.contains(scaledBarcodeBoundingBox) } - if (filteredBarcodes.isNotEmpty()) { - onBarcodeRead(filteredBarcodes) + if (insideFrameBarcodes.isNotEmpty()) { + onBarcodeRead(insideFrameBarcodes) } } imageAnalyzer!!.setAnalyzer(cameraExecutor, analyzer) @@ -708,7 +712,23 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs } fun setAllowedBarcodeTypes(types: ReadableArray?) { - allowedBarcodeTypes = types + if (types == null || types.size() == 0) { + allowedBarcodeTypes = emptyArray() + return + } + + // Convert only valid CodeFormat values + val converted = mutableListOf() + + for (i in 0 until types.size()) { + val name = types.getString(i) ?: continue + val format = CodeFormat.fromName(name) + if (format != null) { + converted.add(format) + } + } + + allowedBarcodeTypes = converted.toTypedArray() } private fun convertDeviceHeightToSupportedAspectRatio(actualWidth: Int, actualHeight: Int): Int { @@ -732,15 +752,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs } private fun convertAllowedBarcodeTypes(): Set { - val types = allowedBarcodeTypes - if (types == null || types.size() == 0) { - return emptySet() - } - - return (0 until types.size()).mapNotNull { index -> - val typeName = types.getString(index) ?: return@mapNotNull null - CodeFormat.getBarcodeType(typeName) - }.toSet() + return allowedBarcodeTypes?.map { it.barcodeType }?.toSet() ?: emptySet() } companion object { diff --git a/ios/ReactNativeCameraKit/CameraView.swift b/ios/ReactNativeCameraKit/CameraView.swift index 51586a3ba..055d1d594 100644 --- a/ios/ReactNativeCameraKit/CameraView.swift +++ b/ios/ReactNativeCameraKit/CameraView.swift @@ -21,9 +21,6 @@ public class CameraView: UIView { // scanner private var lastBarcodeDetectedTime: TimeInterval = 0 private var scannerInterfaceView: ScannerInterfaceView - private var supportedBarcodeType: [CodeFormat] = { - return CodeFormat.allCases - }() // camera private var ratioOverlayView: RatioOverlayView? @@ -82,14 +79,14 @@ public class CameraView: UIView { } private func setupCamera() { if hasPropBeenSetup && hasPermissionBeenGranted && !hasCameraBeenSetup { - let allowedBarcodeTypes = convertAllowedBarcodeTypes() + let convertedAllowedTypes = convertAllowedBarcodeTypes() hasCameraBeenSetup = true #if targetEnvironment(macCatalyst) // Force front camera on Mac Catalyst during initial setup - camera.setup(cameraType: .front, supportedBarcodeType: scanBarcode && onReadCode != nil ? allowedBarcodeTypes : []) + camera.setup(cameraType: .front, supportedBarcodeType: scanBarcode && onReadCode != nil ? convertedAllowedTypes : []) #else - camera.setup(cameraType: cameraType, supportedBarcodeType: scanBarcode && onReadCode != nil ? allowedBarcodeTypes : []) + camera.setup(cameraType: cameraType, supportedBarcodeType: scanBarcode && onReadCode != nil ? convertedAllowedTypes : []) #endif } } @@ -239,10 +236,10 @@ public class CameraView: UIView { // Scanner if changedProps.contains("scanBarcode") || changedProps.contains("onReadCode") || changedProps.contains("allowedBarcodeTypes") { - let allowedBarcodeTypes: [CodeFormat] = convertAllowedBarcodeTypes() + let convertedAllowedTypes: [CodeFormat] = convertAllowedBarcodeTypes() camera.isBarcodeScannerEnabled(scanBarcode, - supportedBarcodeTypes: allowedBarcodeTypes, + supportedBarcodeTypes: convertedAllowedTypes, onBarcodeRead: { [weak self] (barcode, codeFormat) in self?.onBarcodeRead(barcode: barcode, codeFormat: codeFormat) }) @@ -429,7 +426,7 @@ public class CameraView: UIView { } private func convertAllowedBarcodeTypes() -> [CodeFormat] { - guard let allowedTypes = allowedBarcodeTypes as? [String] else { + guard let allowedTypes = allowedBarcodeTypes as? [String], !allowedTypes.isEmpty else { return CodeFormat.allCases } From 4cbec3904273bf1e7ae69f6d4fa267fe9544c71e Mon Sep 17 00:00:00 2001 From: Kseniya Vinnichek Date: Thu, 20 Nov 2025 13:08:08 +0100 Subject: [PATCH 4/7] refactoring --- android/src/main/java/com/rncamerakit/CKCamera.kt | 3 --- .../facebook/react/viewmanagers/CKCameraManagerDelegate.java | 0 .../facebook/react/viewmanagers/CKCameraManagerInterface.java | 0 3 files changed, 3 deletions(-) delete mode 100644 android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java delete mode 100644 android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java diff --git a/android/src/main/java/com/rncamerakit/CKCamera.kt b/android/src/main/java/com/rncamerakit/CKCamera.kt index 53ffe5243..2afc91f4f 100644 --- a/android/src/main/java/com/rncamerakit/CKCamera.kt +++ b/android/src/main/java/com/rncamerakit/CKCamera.kt @@ -372,7 +372,6 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs (barcodeBoundingBox.right * scaleX).toInt(), (barcodeBoundingBox.bottom * scaleY).toInt() ) - frameRect.contains(scaledBarcodeBoundingBox) } @@ -380,9 +379,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs if (filteredBarcodes.isNotEmpty()) { onBarcodeRead(filteredBarcodes) } - }, scanThrottleDelay) - imageAnalyzer!!.setAnalyzer(cameraExecutor, analyzer) useCases.add(imageAnalyzer) } diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerDelegate.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/CKCameraManagerInterface.java deleted file mode 100644 index e69de29bb..000000000 From d42ef6290b2b5e0e371140e4e69e86ec925314d9 Mon Sep 17 00:00:00 2001 From: Kseniya Vinnichek Date: Thu, 20 Nov 2025 13:37:03 +0100 Subject: [PATCH 5/7] update README with allowedBarcodeTypes usage and supported formats --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9c5a2ebea..44978739b 100644 --- a/README.md +++ b/README.md @@ -173,18 +173,19 @@ Additionally, the Camera can be used for barcode scanning ### Camera Props (Optional) | Props | Type | Description | -| ------------------------------ | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ------------------------------ | -------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `ref` | Ref | Reference on the camera view | | `style` | StyleProp\ | Style to apply on the camera view | | `flashMode` | `'on'`/`'off'`/`'auto'` | Camera flash mode. Default: `auto` | | `focusMode` | `'on'`/`'off'` | Camera focus mode. Default: `on` | -| `zoomMode` | `'on'`/`'off'` | Enable the pinch to zoom gesture. Default: `on`. If `on`, you must pass `zoom` as `undefined` or avoid setting `zoomMode` to allow pinch to zoom | +| `zoomMode` | `'on'`/`'off'` | Enable the pinch to zoom gesture. Default: `on`. If `on`, you must pass `zoom` as `undefined` or avoid setting `zoomMode` to allow pinch to zoom | | `zoom` | `number` | Control the zoom. Default: `1.0` | | `maxZoom` | `number` | Maximum zoom allowed (but not beyond what camera allows). Default: `undefined` (camera default max) | | `onZoom` | Function | Callback when user makes a pinch gesture, regardless of what the `zoom` prop was set to. Returned event contains `zoom`. Ex: `onZoom={(e) => console.log(e.nativeEvent.zoom)}`. | | `torchMode` | `'on'`/`'off'` | Toggle flash light when camera is active. Default: `off` | | `cameraType` | CameraType.Back/CameraType.Front | Choose what camera to use. Default: `CameraType.Back` | | `onOrientationChange` | Function | Callback when physical device orientation changes. Returned event contains `orientation`. Ex: `onOrientationChange={(event) => console.log(event.nativeEvent.orientation)}`. Use `import { Orientation } from 'react-native-camera-kit'; if (event.nativeEvent.orientation === Orientation.PORTRAIT) { ... }` to understand the new value | +| `allowedBarcodeTypes` | string[] | Limits which barcode formats can be detected. Ex: `['code-128', 'code-39', 'code-93', 'codabar', 'ean-13', 'ean-8', 'itf', 'upc-a', 'upc-e', 'pdf-417', 'aztec', 'data-matrix', 'code-128']`. If empty or omitted, all supported formats are scanned. | | **Android only** | | `onError` | Function | Android only. Callback when camera fails to initialize. Ex: `onError={(e) => console.log(e.nativeEvent.errorMessage)}`. | | `shutterPhotoSound` | `boolean` | Android only. Enable or disable the shutter sound when capturing a photo. Default: `true` | @@ -195,13 +196,13 @@ Additionally, the Camera can be used for barcode scanning | `resetFocusWhenMotionDetected` | Boolean | Dismiss tap to focus when focus area content changes. Native iOS feature, see documentation: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624644-subjectareachangemonitoringenabl?language=objc). Default `true`. | | `resizeMode` | `'cover' / 'contain'` | Determines the scaling and cropping behavior of content within the view. `cover` (resizeAspectFill on iOS) scales the content to fill the view completely, potentially cropping content if its aspect ratio differs from the view. `contain` (resizeAspect on iOS) scales the content to fit within the view's bounds without cropping, ensuring all content is visible but may introduce letterboxing. Default behavior depends on the specific use case. | | `scanThrottleDelay` | `number` | Duration between scan detection in milliseconds. Default 2000 (2s) | -| `maxPhotoQualityPrioritization` | `'balanced'` / `'quality'` / `'speed'` | [iOS 13 and newer](https://developer.apple.com/documentation/avfoundation/avcapturephotooutput/3182995-maxphotoqualityprioritization). `'speed'` provides a 60-80% median capture time reduction vs 'quality' setting. Tested on iPhone 6S Max (66% faster) and iPhone 15 Pro Max (76% faster!). Default `balanced` | -| `onCaptureButtonPressIn` | Function | Callback when iPhone capture button is pressed in or Android volume or camera button is pressed in. Ex: `onCaptureButtonPressIn={() => console.log("volume button pressed in")}` | -| `onCaptureButtonPressOut` | Function | Callback when iPhone capture button is released or Android volume or camera button is released. Ex: `onCaptureButtonPressOut={() => console.log("volume button released")}` | +| `maxPhotoQualityPrioritization` | `'balanced'` / `'quality'` / `'speed'` | [iOS 13 and newer](https://developer.apple.com/documentation/avfoundation/avcapturephotooutput/3182995-maxphotoqualityprioritization). `'speed'` provides a 60-80% median capture time reduction vs 'quality' setting. Tested on iPhone 6S Max (66% faster) and iPhone 15 Pro Max (76% faster!). Default `balanced` | +| `onCaptureButtonPressIn` | Function | Callback when iPhone capture button is pressed in or Android volume or camera button is pressed in. Ex: `onCaptureButtonPressIn={() => console.log("volume button pressed in")}` | +| `onCaptureButtonPressOut` | Function | Callback when iPhone capture button is released or Android volume or camera button is released. Ex: `onCaptureButtonPressOut={() => console.log("volume button released")}` | | **Barcode only** | | `scanBarcode` | `boolean` | Enable barcode scanner. Default: `false` | | `showFrame` | `boolean` | Show frame in barcode scanner. Default: `false` | -| `barcodeFrameSize` | `object` | Frame size of barcode scanner. Default: `{ width: 300, height: 150 }` | +| `barcodeFrameSize` | `object` | Frame size of barcode scanner. Default: `{ width: 300, height: 150 }` | | `laserColor` | Color | Color of barcode scanner laser visualization. Default: `red` | | `frameColor` | Color | Color of barcode scanner frame visualization. Default: `yellow` | | `onReadCode` | Function | Callback when scanner successfully reads barcode. Returned event contains `codeStringValue`. Default: `null`. Ex: `onReadCode={(event) => console.log(event.nativeEvent.codeStringValue)}` | From 2a1f06aa126f53ad478ee2c71b9ad23ebde2db20 Mon Sep 17 00:00:00 2001 From: Seph Soliman Date: Tue, 30 Dec 2025 15:11:19 -0800 Subject: [PATCH 6/7] Fixed optional behavior of allowedBarcodeTypes Added more barcode types --- example/.nvmrc => .nvmrc | 0 example/ios/Podfile.lock | 4 +- example/src/BarcodeScreenExample.tsx | 5 +- .../CKCameraViewComponentView.mm | 22 +++++--- ios/ReactNativeCameraKit/CodeFormat.swift | 20 ++++++- src/Camera.android.tsx | 4 +- src/Camera.ios.tsx | 4 +- src/types.ts | 55 ++++++++++++++----- 8 files changed, 82 insertions(+), 32 deletions(-) rename example/.nvmrc => .nvmrc (100%) diff --git a/example/.nvmrc b/.nvmrc similarity index 100% rename from example/.nvmrc rename to .nvmrc diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index f790041ff..39afb1208 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2223,7 +2223,7 @@ PODS: - React-perflogger (= 0.81.0) - React-utils (= 0.81.0) - SocketRocket - - ReactNativeCameraKit (16.1.1): + - ReactNativeCameraKit (16.1.3): - boost - DoubleConversion - fast_float @@ -2552,7 +2552,7 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: c91900fa724baee992f01c05eeb4c9e01a807f78 ReactCodegen: a55799cae416c387aeaae3aabc1bc0289ac19cee ReactCommon: 116d6ee71679243698620d8cd9a9042541e44aa6 - ReactNativeCameraKit: b01e637c97fb6eefe43eff31917d1410fc77e1f8 + ReactNativeCameraKit: 269e2abf46202f729ac49a07829d0b6d1b5a91ad SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 00013dd9cde63a2d98e8002fcc4f5ddb66c10782 diff --git a/example/src/BarcodeScreenExample.tsx b/example/src/BarcodeScreenExample.tsx index f125091a1..9819938a5 100644 --- a/example/src/BarcodeScreenExample.tsx +++ b/example/src/BarcodeScreenExample.tsx @@ -148,13 +148,13 @@ const BarcodeExample = ({ onBack }: { onBack: () => void }) => { frameColor="white" scanBarcode showFrame + allowedBarcodeTypes={['qr', 'ean-13']} barcodeFrameSize={{ width: 300, height: 150 }} onReadCode={(event) => { Vibration.vibrate(100); setBarcode(event.nativeEvent.codeStringValue); console.log('barcode', event.nativeEvent.codeStringValue); console.log('codeFormat', event.nativeEvent.codeFormat); - }} /> @@ -233,8 +233,7 @@ const styles = StyleSheet.create({ backBtnContainer: { alignItems: 'flex-start', }, - captureButtonContainer: { - }, + captureButtonContainer: {}, textNumberContainer: { position: 'absolute', top: 0, diff --git a/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm b/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm index 379b446bf..3b064c3b8 100644 --- a/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm +++ b/ios/ReactNativeCameraKit/CKCameraViewComponentView.mm @@ -248,17 +248,21 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & _view.barcodeFrameSize = @{@"width": @(barcodeWidth), @"height": @(barcodeHeight)}; [changedProps addObject:@"barcodeFrameSize"]; } - folly::dynamic allowedBarcodeTypesDynamic = folly::dynamic::array(); - for (const auto& type : newProps.allowedBarcodeTypes) { - allowedBarcodeTypesDynamic.push_back(type); - } - id allowedBarcodeTypes = CKConvertFollyDynamicToId(allowedBarcodeTypesDynamic); - if (allowedBarcodeTypes != nil && [allowedBarcodeTypes isKindOfClass:NSArray.class]) { - _view.allowedBarcodeTypes = allowedBarcodeTypes; - [changedProps addObject:@"allowedBarcodeTypes"]; + // Since viewprops optional props isn't supported in all RN versions, + // we assume empty arrays mean it's not defined / ignore changes to it. + // if the user/dev wants to NOT define the prop, they can simply use scanBarcode={false} + if (!newProps.allowedBarcodeTypes.empty()) { + folly::dynamic allowedBarcodeTypesDynamic = folly::dynamic::array(); + for (const auto& type : newProps.allowedBarcodeTypes) { + allowedBarcodeTypesDynamic.push_back(type); + } + id allowedBarcodeTypes = CKConvertFollyDynamicToId(allowedBarcodeTypesDynamic); + if (allowedBarcodeTypes != nil && [allowedBarcodeTypes isKindOfClass:NSArray.class]) { + _view.allowedBarcodeTypes = allowedBarcodeTypes; + [changedProps addObject:@"allowedBarcodeTypes"]; + } } - [super updateProps:props oldProps:oldProps]; [_view didSetProps:changedProps]; } diff --git a/ios/ReactNativeCameraKit/CodeFormat.swift b/ios/ReactNativeCameraKit/CodeFormat.swift index bbf3415f1..5371f4970 100644 --- a/ios/ReactNativeCameraKit/CodeFormat.swift +++ b/ios/ReactNativeCameraKit/CodeFormat.swift @@ -12,6 +12,7 @@ enum CodeFormat: String, CaseIterable { case code128 = "code-128" case code39 = "code-39" case code93 = "code-93" + case codabar = "codabar" case ean13 = "ean-13" case ean8 = "ean-8" case itf14 = "itf-14" @@ -20,13 +21,21 @@ enum CodeFormat: String, CaseIterable { case pdf417 = "pdf-417" case aztec = "aztec" case dataMatrix = "data-matrix" + case code39Mod43 = "code-39-mod-43" + case interleaved2of5 = "interleaved-2of5" case unknown = "unknown" // Convert from AVMetadataObject.ObjectType to CodeFormat static func fromAVMetadataObjectType(_ type: AVMetadataObject.ObjectType) -> CodeFormat { + if #available(iOS 15.4, *) { + if (type == .codabar) { + return .codabar + } + } switch type { case .code128: return .code128 case .code39: return .code39 + case .code39Mod43: return .code39Mod43 case .code93: return .code93 case .ean13: return .ean13 case .ean8: return .ean8 @@ -36,15 +45,22 @@ enum CodeFormat: String, CaseIterable { case .pdf417: return .pdf417 case .aztec: return .aztec case .dataMatrix: return .dataMatrix + case .interleaved2of5: return .interleaved2of5 default: return .unknown } } // Convert from CodeFormat to AVMetadataObject.ObjectType func toAVMetadataObjectType() -> AVMetadataObject.ObjectType { + if #available(iOS 15.4, *) { + if (self == .codabar) { + return .codabar + } + } switch self { case .code128: return .code128 case .code39: return .code39 + case .code39Mod43: return .code39Mod43 case .code93: return .code93 case .ean13: return .ean13 case .ean8: return .ean8 @@ -54,7 +70,9 @@ enum CodeFormat: String, CaseIterable { case .pdf417: return .pdf417 case .aztec: return .aztec case .dataMatrix: return .dataMatrix - case .unknown: return .init(rawValue: "unknown") + case .interleaved2of5: return .interleaved2of5 + case .unknown: fallthrough + default: return .init(rawValue: "unknown") } } } diff --git a/src/Camera.android.tsx b/src/Camera.android.tsx index 17309e9f9..56cddbccd 100644 --- a/src/Camera.android.tsx +++ b/src/Camera.android.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { findNodeHandle, processColor } from 'react-native'; -import type { CameraApi } from './types'; +import { supportedCodeFormats, type CameraApi } from './types'; import type { CameraProps } from './CameraProps'; import NativeCamera from './specs/CameraNativeComponent'; import NativeCameraKitModule from './specs/NativeCameraKitModule'; @@ -15,6 +15,8 @@ const Camera = React.forwardRef((props, ref) => { props.maxZoom = props.maxZoom ?? -1; props.scanThrottleDelay = props.scanThrottleDelay ?? -1; + props.allowedBarcodeTypes = props.allowedBarcodeTypes ?? supportedCodeFormats; + React.useImperativeHandle(ref, () => ({ capture: async (options = {}) => { return await NativeCameraKitModule.capture(options, findNodeHandle(nativeRef.current) ?? undefined); diff --git a/src/Camera.ios.tsx b/src/Camera.ios.tsx index 2c5c970f4..45175acff 100644 --- a/src/Camera.ios.tsx +++ b/src/Camera.ios.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { findNodeHandle } from 'react-native'; -import type { CameraApi } from './types'; +import { supportedCodeFormats, type CameraApi } from './types'; import type { CameraProps } from './CameraProps'; import NativeCamera from './specs/CameraNativeComponent'; import NativeCameraKitModule from './specs/NativeCameraKitModule'; @@ -15,6 +15,8 @@ const Camera = React.forwardRef((props, ref) => { props.maxZoom = props.maxZoom ?? -1; props.scanThrottleDelay = props.scanThrottleDelay ?? -1; + props.allowedBarcodeTypes = props.allowedBarcodeTypes ?? supportedCodeFormats; + props.resetFocusTimeout = props.resetFocusTimeout ?? 0; props.resetFocusWhenMotionDetected = props.resetFocusWhenMotionDetected ?? true; diff --git a/src/types.ts b/src/types.ts index 80d17f8a4..ec6fb1209 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,21 +3,46 @@ export enum CameraType { Back = 'back', } -export type CodeFormat = - | 'code-128' - | 'code-39' - | 'code-93' - | 'codabar' - | 'ean-13' - | 'ean-8' - | 'itf' - | 'upc-a' - | 'upc-e' - | 'qr' - | 'pdf-417' - | 'aztec' - | 'data-matrix' - | 'unknown'; +const codeFormatAndroid = [ + 'code-128', + 'code-39', + 'code-93', + 'codabar', + 'ean-13', + 'ean-8', + 'itf', + 'upc-a', + 'upc-e', + 'qr', + 'pdf-417', + 'aztec', + 'data-matrix', + 'unknown', +] as const; + +const codeFormatIOS = [ + 'code-128', + 'code-39', + 'code-93', + 'codabar', // only iOS 15.4+ + 'ean-13', + 'ean-8', + 'itf-14', + 'upc-e', + 'qr', + 'pdf-417', + 'aztec', + 'data-matrix', + 'code-39-mod-43', + 'interleaved-2of5', +] as const; + +export const supportedCodeFormats = Array.from(new Set([...codeFormatAndroid, ...codeFormatIOS])); + +type CodeFormatAndroid = (typeof codeFormatAndroid)[number]; +type CodeFormatIOS = (typeof codeFormatIOS)[number]; + +export type CodeFormat = CodeFormatAndroid | CodeFormatIOS | 'unknown'; export type TorchMode = 'on' | 'off'; From ea894d96ad3194ab9ab2638b66dc954e94af5d63 Mon Sep 17 00:00:00 2001 From: Seph Soliman Date: Tue, 30 Dec 2025 16:03:40 -0800 Subject: [PATCH 7/7] Fixed Android allowed barcode types --- android/src/main/java/com/rncamerakit/CKCamera.kt | 2 +- android/src/main/java/com/rncamerakit/CodeFormat.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/rncamerakit/CKCamera.kt b/android/src/main/java/com/rncamerakit/CKCamera.kt index 2afc91f4f..8dd5a6e0d 100644 --- a/android/src/main/java/com/rncamerakit/CKCamera.kt +++ b/android/src/main/java/com/rncamerakit/CKCamera.kt @@ -764,7 +764,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs } private fun convertAllowedBarcodeTypes(): Set { - return allowedBarcodeTypes?.map { it.barcodeType }?.toSet() ?: emptySet() + return allowedBarcodeTypes?.map { it.toBarcodeType() }?.toSet() ?: emptySet() } companion object { diff --git a/android/src/main/java/com/rncamerakit/CodeFormat.kt b/android/src/main/java/com/rncamerakit/CodeFormat.kt index cafa87b08..907c6c39e 100644 --- a/android/src/main/java/com/rncamerakit/CodeFormat.kt +++ b/android/src/main/java/com/rncamerakit/CodeFormat.kt @@ -55,9 +55,9 @@ enum class CodeFormat(val code: String) { Barcode.FORMAT_DATA_MATRIX -> DATA_MATRIX else -> UNKNOWN } - fun getBarcodeType(typeString: String?): Int { - val codeFormat = values().firstOrNull { it.code == typeString } ?: UNKNOWN - return codeFormat.toBarcodeType() + + fun fromName(name: String?): CodeFormat? { + return values().firstOrNull { it.code == name } } } }