diff --git a/android/src/main/java/com/rncamerakit/CKCamera.kt b/android/src/main/java/com/rncamerakit/CKCamera.kt index 5f0a498a1..987d46cdc 100644 --- a/android/src/main/java/com/rncamerakit/CKCamera.kt +++ b/android/src/main/java/com/rncamerakit/CKCamera.kt @@ -41,6 +41,7 @@ import kotlin.math.min import android.graphics.Canvas import android.graphics.Paint import android.graphics.RectF +import com.google.mlkit.vision.barcode.common.Barcode class RectOverlay constructor(context: Context) : View(context) { @@ -457,9 +458,11 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs rectOverlay.drawRectBounds(focusRects) } - private fun onBarcodeRead(barcodes: List) { + private fun onBarcodeRead(barcodes: List) { val event: WritableMap = Arguments.createMap() - event.putString("codeStringValue", barcodes.first()) + event.putString("codeStringValue", barcodes.first().rawValue) + val codeFormat = CodeFormat.fromBarcodeType(barcodes.first().format); + event.putString("codeFormat",codeFormat.code ); currentContext.getJSModule(RCTEventEmitter::class.java).receiveEvent( id, "onReadCode", diff --git a/android/src/main/java/com/rncamerakit/CodeFormat.kt b/android/src/main/java/com/rncamerakit/CodeFormat.kt new file mode 100644 index 000000000..207c7151d --- /dev/null +++ b/android/src/main/java/com/rncamerakit/CodeFormat.kt @@ -0,0 +1,56 @@ +package com.rncamerakit + +import com.google.mlkit.vision.barcode.common.Barcode + +enum class CodeFormat(val code: String) { + CODE_128("code-128"), + CODE_39("code-39"), + CODE_93("code-93"), + CODABAR("codabar"), + EAN_13("ean-13"), + EAN_8("ean-8"), + ITF("itf"), + UPC_E("upc-e"), + QR("qr"), + PDF_417("pdf-417"), + AZTEC("aztec"), + DATA_MATRIX("data-matrix"), + UNKNOWN("unknown"); + + fun toBarcodeType(): Int { + return when (this) { + CODE_128 -> Barcode.FORMAT_CODE_128 + CODE_39 -> Barcode.FORMAT_CODE_39 + CODE_93 -> Barcode.FORMAT_CODE_93 + CODABAR -> Barcode.FORMAT_CODABAR + EAN_13 -> Barcode.FORMAT_EAN_13 + EAN_8 -> Barcode.FORMAT_EAN_8 + ITF -> Barcode.FORMAT_ITF + UPC_E -> Barcode.FORMAT_UPC_E + QR -> Barcode.FORMAT_QR_CODE + PDF_417 -> Barcode.FORMAT_PDF417 + AZTEC -> Barcode.FORMAT_AZTEC + DATA_MATRIX -> Barcode.FORMAT_DATA_MATRIX + UNKNOWN -> -1 // Or any other default value you prefer + } + } + + companion object { + fun fromBarcodeType(@Barcode.BarcodeFormat barcodeType: Int): CodeFormat = + when (barcodeType) { + Barcode.FORMAT_CODE_128 -> CODE_128 + Barcode.FORMAT_CODE_39 -> CODE_39 + Barcode.FORMAT_CODE_93 -> CODE_93 + Barcode.FORMAT_CODABAR -> CODABAR + Barcode.FORMAT_EAN_13 -> EAN_13 + Barcode.FORMAT_EAN_8 -> EAN_8 + Barcode.FORMAT_ITF -> ITF + Barcode.FORMAT_UPC_E -> UPC_E + Barcode.FORMAT_QR_CODE -> QR + Barcode.FORMAT_PDF417 -> PDF_417 + Barcode.FORMAT_AZTEC -> AZTEC + Barcode.FORMAT_DATA_MATRIX -> DATA_MATRIX + else -> UNKNOWN + } + } +} diff --git a/android/src/main/java/com/rncamerakit/QRCodeAnalyzer.kt b/android/src/main/java/com/rncamerakit/QRCodeAnalyzer.kt index 839daf666..7af1df106 100644 --- a/android/src/main/java/com/rncamerakit/QRCodeAnalyzer.kt +++ b/android/src/main/java/com/rncamerakit/QRCodeAnalyzer.kt @@ -5,10 +5,11 @@ import androidx.camera.core.ExperimentalGetImage import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageProxy import com.google.mlkit.vision.barcode.BarcodeScanning +import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage class QRCodeAnalyzer ( - private val onQRCodesDetected: (qrCodes: List) -> Unit + private val onQRCodesDetected: (qrCodes: List) -> Unit ) : ImageAnalysis.Analyzer { @SuppressLint("UnsafeExperimentalUsageError") @ExperimentalGetImage @@ -18,9 +19,9 @@ class QRCodeAnalyzer ( val scanner = BarcodeScanning.getClient() scanner.process(inputImage) .addOnSuccessListener { barcodes -> - val strBarcodes = mutableListOf() + val strBarcodes = mutableListOf() barcodes.forEach { barcode -> - strBarcodes.add(barcode.rawValue ?: return@forEach) + strBarcodes.add(barcode ?: return@forEach) } onQRCodesDetected(strBarcodes) } diff --git a/example/src/BarcodeScreenExample.tsx b/example/src/BarcodeScreenExample.tsx index 440b5c199..71654a794 100644 --- a/example/src/BarcodeScreenExample.tsx +++ b/example/src/BarcodeScreenExample.tsx @@ -151,6 +151,8 @@ const BarcodeExample = ({ onBack }: { onBack: () => void }) => { Vibration.vibrate(100); setBarcode(event.nativeEvent.codeStringValue); console.log('barcode', event.nativeEvent.codeStringValue); + console.log('codeFormat', event.nativeEvent.codeFormat); + }} /> diff --git a/ios/ReactNativeCameraKit.xcodeproj/project.pbxproj b/ios/ReactNativeCameraKit.xcodeproj/project.pbxproj index c070d40b2..1c871aaa4 100644 --- a/ios/ReactNativeCameraKit.xcodeproj/project.pbxproj +++ b/ios/ReactNativeCameraKit.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 46C558CF2A4AAD7300C68BA0 /* FocusInterfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C558CE2A4AAD7300C68BA0 /* FocusInterfaceView.swift */; }; 46F30C012A3A859B000597F6 /* ScannerFrameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46F30C002A3A859B000597F6 /* ScannerFrameView.swift */; }; 46F30C032A3ABB9D000597F6 /* ScannerInterfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46F30C022A3ABB9D000597F6 /* ScannerInterfaceView.swift */; }; + B5C747452B35924D00C95030 /* CodeFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C747442B35924D00C95030 /* CodeFormat.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -55,6 +56,7 @@ 46C558CE2A4AAD7300C68BA0 /* FocusInterfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusInterfaceView.swift; sourceTree = ""; }; 46F30C002A3A859B000597F6 /* ScannerFrameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScannerFrameView.swift; sourceTree = ""; }; 46F30C022A3ABB9D000597F6 /* ScannerInterfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScannerInterfaceView.swift; sourceTree = ""; }; + B5C747442B35924D00C95030 /* CodeFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeFormat.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -104,6 +106,7 @@ 46C558CE2A4AAD7300C68BA0 /* FocusInterfaceView.swift */, 4620AA682A2BFDBC00BC8929 /* ReactNativeCameraKit-Bridging-Header.h */, 463096892A2C7D89002ABA1A /* ReactNativeCameraKit.h */, + B5C747442B35924D00C95030 /* CodeFormat.swift */, ); path = ReactNativeCameraKit; sourceTree = ""; @@ -181,6 +184,7 @@ 26550AF61CFC7086007FF2DF /* CKCameraManager.m in Sources */, 46C558CB2A4AAB3400C68BA0 /* CameraProtocol.swift in Sources */, 4620AA722A2C4FA500BC8929 /* CameraManager.swift in Sources */, + B5C747452B35924D00C95030 /* CodeFormat.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/ReactNativeCameraKit/CameraProtocol.swift b/ios/ReactNativeCameraKit/CameraProtocol.swift index cc8c62f35..34a14e93b 100644 --- a/ios/ReactNativeCameraKit/CameraProtocol.swift +++ b/ios/ReactNativeCameraKit/CameraProtocol.swift @@ -8,7 +8,7 @@ import AVFoundation protocol CameraProtocol: AnyObject, FocusInterfaceViewDelegate { var previewView: UIView { get } - func setup(cameraType: CameraType, supportedBarcodeType: [AVMetadataObject.ObjectType]) + func setup(cameraType: CameraType, supportedBarcodeType: [CodeFormat]) func cameraRemovedFromSuperview() func update(torchMode: TorchMode) @@ -23,8 +23,9 @@ protocol CameraProtocol: AnyObject, FocusInterfaceViewDelegate { func zoomPinchChange(pinchScale: CGFloat) func isBarcodeScannerEnabled(_ isEnabled: Bool, - supportedBarcodeType: [AVMetadataObject.ObjectType], - onBarcodeRead: ((_ barcode: String) -> Void)?) + supportedBarcodeTypes: [CodeFormat], + onBarcodeRead: ((_ barcode: String, _ codeFormat: CodeFormat) -> Void)?) + func update(scannerFrameSize: CGRect?) func capturePicture(onWillCapture: @escaping () -> Void, diff --git a/ios/ReactNativeCameraKit/CameraView.swift b/ios/ReactNativeCameraKit/CameraView.swift index 787c0c50d..6e4a2abb5 100644 --- a/ios/ReactNativeCameraKit/CameraView.swift +++ b/ios/ReactNativeCameraKit/CameraView.swift @@ -20,10 +20,10 @@ class CameraView: UIView { // scanner private var lastBarcodeDetectedTime: TimeInterval = 0 private var scannerInterfaceView: ScannerInterfaceView - private var supportedBarcodeType: [AVMetadataObject.ObjectType] = [.upce, .code39, .code39Mod43, - .ean13, .ean8, .code93, - .code128, .pdf417, .qr, - .aztec, .dataMatrix, .interleaved2of5] + private var supportedBarcodeType: [CodeFormat] = { + return CodeFormat.allCases + }() + // camera private var ratioOverlayView: RatioOverlayView? @@ -69,7 +69,6 @@ class CameraView: UIView { setupCamera() } } - private func setupCamera() { if hasPropBeenSetup && hasPermissionBeenGranted && !hasCameraBeenSetup { hasCameraBeenSetup = true @@ -77,6 +76,7 @@ class CameraView: UIView { } } + // MARK: Lifecycle @available(*, unavailable) @@ -186,10 +186,14 @@ class CameraView: UIView { // Scanner if changedProps.contains("scanBarcode") || changedProps.contains("onReadCode") { camera.isBarcodeScannerEnabled(scanBarcode, - supportedBarcodeType: supportedBarcodeType, - onBarcodeRead: { [weak self] barcode in self?.onBarcodeRead(barcode: barcode) }) + supportedBarcodeTypes: supportedBarcodeType, + onBarcodeRead: { [weak self] (barcode, codeFormat) in + self?.onBarcodeRead(barcode: barcode, codeFormat: codeFormat) + }) } + + if changedProps.contains("showFrame") || changedProps.contains("scanBarcode") { DispatchQueue.main.async { self.scannerInterfaceView.isHidden = !self.showFrame @@ -330,7 +334,7 @@ class CameraView: UIView { return temporaryFileURL } - private func onBarcodeRead(barcode: String) { + private func onBarcodeRead(barcode: String, codeFormat:CodeFormat) { // Throttle barcode detection let now = Date.timeIntervalSinceReferenceDate guard lastBarcodeDetectedTime + Double(scanThrottleDelay) / 1000 < now else { @@ -339,7 +343,7 @@ class CameraView: UIView { lastBarcodeDetectedTime = now - onReadCode?(["codeStringValue": barcode]) + onReadCode?(["codeStringValue": barcode,"codeFormat":codeFormat.rawValue]) } // MARK: - Gesture selectors diff --git a/ios/ReactNativeCameraKit/CodeFormat.swift b/ios/ReactNativeCameraKit/CodeFormat.swift new file mode 100644 index 000000000..bbf3415f1 --- /dev/null +++ b/ios/ReactNativeCameraKit/CodeFormat.swift @@ -0,0 +1,60 @@ +// +// CodeFormat.swift +// ReactNativeCameraKit +// +// Created by Imdad on 2023-12-22. +// + +import Foundation +import AVFoundation + +enum CodeFormat: String, CaseIterable { + case code128 = "code-128" + case code39 = "code-39" + case code93 = "code-93" + case ean13 = "ean-13" + case ean8 = "ean-8" + case itf14 = "itf-14" + case upce = "upc-e" + case qr = "qr" + case pdf417 = "pdf-417" + case aztec = "aztec" + case dataMatrix = "data-matrix" + case unknown = "unknown" + + // Convert from AVMetadataObject.ObjectType to CodeFormat + static func fromAVMetadataObjectType(_ type: AVMetadataObject.ObjectType) -> CodeFormat { + switch type { + case .code128: return .code128 + case .code39: return .code39 + case .code93: return .code93 + case .ean13: return .ean13 + case .ean8: return .ean8 + case .itf14: return .itf14 + case .upce: return .upce + case .qr: return .qr + case .pdf417: return .pdf417 + case .aztec: return .aztec + case .dataMatrix: return .dataMatrix + default: return .unknown + } + } + + // Convert from CodeFormat to AVMetadataObject.ObjectType + func toAVMetadataObjectType() -> AVMetadataObject.ObjectType { + switch self { + case .code128: return .code128 + case .code39: return .code39 + case .code93: return .code93 + case .ean13: return .ean13 + case .ean8: return .ean8 + case .itf14: return .itf14 + case .upce: return .upce + case .qr: return .qr + case .pdf417: return .pdf417 + case .aztec: return .aztec + case .dataMatrix: return .dataMatrix + case .unknown: return .init(rawValue: "unknown") + } + } +} diff --git a/ios/ReactNativeCameraKit/RealCamera.swift b/ios/ReactNativeCameraKit/RealCamera.swift index d0d9f702c..2cfab7b08 100644 --- a/ios/ReactNativeCameraKit/RealCamera.swift +++ b/ios/ReactNativeCameraKit/RealCamera.swift @@ -34,8 +34,8 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega private var torchMode: TorchMode = .off private var resetFocus: (() -> Void)? private var focusFinished: (() -> Void)? - private var onBarcodeRead: ((_ barcode: String) -> Void)? - private var scannerFrameSize: CGRect? + private var onBarcodeRead: ((_ barcode: String,_ codeFormat : CodeFormat) -> Void)? + private var scannerFrameSize: CGRect? = nil private var onOrientationChange: RCTDirectEventBlock? private var onZoomCallback: RCTDirectEventBlock? private var lastOnZoom: Double? @@ -93,7 +93,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega // MARK: - Public - func setup(cameraType: CameraType, supportedBarcodeType: [AVMetadataObject.ObjectType]) { + func setup(cameraType: CameraType, supportedBarcodeType: [CodeFormat]) { DispatchQueue.main.async { self.cameraPreview.session = self.session self.cameraPreview.previewLayer.videoGravity = .resizeAspect @@ -340,14 +340,15 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega } func isBarcodeScannerEnabled(_ isEnabled: Bool, - supportedBarcodeType: [AVMetadataObject.ObjectType], - onBarcodeRead: ((_ barcode: String) -> Void)?) { + supportedBarcodeTypes supportedBarcodeType: [CodeFormat], + onBarcodeRead: ((_ barcode: String,_ codeFormat:CodeFormat) -> Void)?) { sessionQueue.async { self.onBarcodeRead = onBarcodeRead let newTypes: [AVMetadataObject.ObjectType] if isEnabled && onBarcodeRead != nil { let availableTypes = self.metadataOutput.availableMetadataObjectTypes - newTypes = supportedBarcodeType.filter { type in availableTypes.contains(type) } + newTypes = supportedBarcodeType.map { $0.toAVMetadataObjectType() } + .filter { availableTypes.contains($0) } } else { newTypes = [] } @@ -396,8 +397,10 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega let codeStringValue = machineReadableCodeObject.stringValue else { return } + // Determine the barcode type and convert it to CodeFormat + let barcodeType = CodeFormat.fromAVMetadataObjectType(machineReadableCodeObject.type) - onBarcodeRead?(codeStringValue) + onBarcodeRead?(codeStringValue,barcodeType) } // MARK: - Private @@ -453,7 +456,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega } private func setupCaptureSession(cameraType: CameraType, - supportedBarcodeType: [AVMetadataObject.ObjectType]) -> SetupResult { + supportedBarcodeType: [CodeFormat]) -> SetupResult { guard let videoDevice = self.getBestDevice(for: cameraType), let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice) else { return .sessionConfigurationFailed @@ -489,7 +492,10 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) let availableTypes = self.metadataOutput.availableMetadataObjectTypes - let filteredTypes = supportedBarcodeType.filter { type in availableTypes.contains(type) } + let filteredTypes = supportedBarcodeType + .map { $0.toAVMetadataObjectType() } + .filter { availableTypes.contains($0) } + metadataOutput.metadataObjectTypes = filteredTypes } diff --git a/ios/ReactNativeCameraKit/SimulatorCamera.swift b/ios/ReactNativeCameraKit/SimulatorCamera.swift index d67f680d2..f6a4b9be0 100644 --- a/ios/ReactNativeCameraKit/SimulatorCamera.swift +++ b/ios/ReactNativeCameraKit/SimulatorCamera.swift @@ -27,7 +27,7 @@ class SimulatorCamera: CameraProtocol { // MARK: - Public - func setup(cameraType: CameraType, supportedBarcodeType: [AVMetadataObject.ObjectType]) { + func setup(cameraType: CameraType, supportedBarcodeType: [CodeFormat]) { DispatchQueue.main.async { self.mockPreview.cameraTypeLabel.text = "Camera type: \(cameraType)" } @@ -162,8 +162,8 @@ class SimulatorCamera: CameraProtocol { } func isBarcodeScannerEnabled(_ isEnabled: Bool, - supportedBarcodeType: [AVMetadataObject.ObjectType], - onBarcodeRead: ((_ barcode: String) -> Void)?) {} + supportedBarcodeTypes: [CodeFormat], + onBarcodeRead: ((_ barcode: String,_ codeFormat:CodeFormat) -> Void)?) {} func update(scannerFrameSize: CGRect?) {} func capturePicture(onWillCapture: @escaping () -> Void, diff --git a/src/Camera.d.ts b/src/Camera.d.ts index 05a0c0e38..4b7c38c12 100644 --- a/src/Camera.d.ts +++ b/src/Camera.d.ts @@ -1,9 +1,10 @@ -import { CameraApi, FlashMode, FocusMode, ZoomMode, TorchMode, CameraType } from './types'; +import { CameraApi, FlashMode, FocusMode, ZoomMode, TorchMode, CameraType, CodeFormat } from './types'; import { Orientation } from './index'; export type OnReadCodeData = { nativeEvent: { codeStringValue: string; + codeFormat: CodeFormat; }; }; diff --git a/src/types.ts b/src/types.ts index 20d2b26a8..5bc8946a5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,6 +3,8 @@ export enum CameraType { Back = 'back', } +export type CodeFormat = 'code-128' | 'code-39' | 'code-93' | 'codabar' | 'ean-13' | 'ean-8' | 'itf' | 'upc-e' | 'qr' | 'pdf-417' | 'aztec' | 'data-matrix' | 'unknown'; + export type TorchMode = 'on' | 'off'; export type FlashMode = 'on' | 'off' | 'auto'; @@ -28,3 +30,5 @@ export type CameraApi = { requestDeviceCameraAuthorization: () => Promise; checkDeviceCameraAuthorizationStatus: () => Promise; }; + +