@cleanuidev/react-native-scanner 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +609 -0
  3. package/Scanner.podspec +20 -0
  4. package/android/build.gradle +90 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +8 -0
  7. package/android/src/main/java/com/scanner/CameraInfoModule.kt +253 -0
  8. package/android/src/main/java/com/scanner/ScannerPackage.kt +21 -0
  9. package/android/src/main/java/com/scanner/ScannerView.kt +783 -0
  10. package/android/src/main/java/com/scanner/ScannerViewManager.kt +181 -0
  11. package/android/src/main/java/com/scanner/utils/BarcodeFrameManager.kt +170 -0
  12. package/android/src/main/java/com/scanner/views/BarcodeFrameOverlayView.kt +43 -0
  13. package/android/src/main/java/com/scanner/views/FocusAreaView.kt +124 -0
  14. package/ios/BarcodeDetectionManager.swift +229 -0
  15. package/ios/BarcodeFrameManager.swift +175 -0
  16. package/ios/BarcodeFrameOverlayView.swift +102 -0
  17. package/ios/CameraManager.swift +396 -0
  18. package/ios/CoordinateTransformer.swift +140 -0
  19. package/ios/FocusAreaOverlayView.swift +161 -0
  20. package/ios/Models.swift +341 -0
  21. package/ios/Protocols.swift +194 -0
  22. package/ios/ScannerView.h +14 -0
  23. package/ios/ScannerView.mm +358 -0
  24. package/ios/ScannerViewImpl.swift +580 -0
  25. package/ios/react-native-scanner-Bridging-Header.h +26 -0
  26. package/lib/module/CameraInfoModule.js +8 -0
  27. package/lib/module/CameraInfoModule.js.map +1 -0
  28. package/lib/module/ScannerViewNativeComponent.ts +121 -0
  29. package/lib/module/hooks/useCameraInfo.js +106 -0
  30. package/lib/module/hooks/useCameraInfo.js.map +1 -0
  31. package/lib/module/index.js +13 -0
  32. package/lib/module/index.js.map +1 -0
  33. package/lib/module/package.json +1 -0
  34. package/lib/module/types.js +47 -0
  35. package/lib/module/types.js.map +1 -0
  36. package/lib/typescript/package.json +1 -0
  37. package/lib/typescript/src/CameraInfoModule.d.ts +8 -0
  38. package/lib/typescript/src/CameraInfoModule.d.ts.map +1 -0
  39. package/lib/typescript/src/ScannerViewNativeComponent.d.ts +91 -0
  40. package/lib/typescript/src/ScannerViewNativeComponent.d.ts.map +1 -0
  41. package/lib/typescript/src/hooks/useCameraInfo.d.ts +25 -0
  42. package/lib/typescript/src/hooks/useCameraInfo.d.ts.map +1 -0
  43. package/lib/typescript/src/index.d.ts +8 -0
  44. package/lib/typescript/src/index.d.ts.map +1 -0
  45. package/lib/typescript/src/types.d.ts +145 -0
  46. package/lib/typescript/src/types.d.ts.map +1 -0
  47. package/package.json +178 -0
  48. package/src/CameraInfoModule.ts +11 -0
  49. package/src/ScannerViewNativeComponent.ts +121 -0
  50. package/src/hooks/useCameraInfo.ts +190 -0
  51. package/src/index.tsx +30 -0
  52. package/src/types.ts +177 -0
@@ -0,0 +1,229 @@
1
+ //
2
+ // BarcodeDetectionManager.swift
3
+ // react-native-scanner
4
+ //
5
+ // Manages barcode detection using Vision framework
6
+ //
7
+
8
+ import Foundation
9
+ import Vision
10
+ import AVFoundation
11
+
12
+ /// Manages barcode detection using Vision framework
13
+ class BarcodeDetectionManager: NSObject, BarcodeScannerProtocol {
14
+
15
+ // MARK: - Public Properties
16
+
17
+ /// Delegate for detection events
18
+ weak var delegate: BarcodeDetectionDelegate?
19
+
20
+ // MARK: - Private Properties
21
+
22
+ /// Supported barcode symbologies
23
+ private var supportedSymbologies: [VNBarcodeSymbology]
24
+
25
+ /// Scan strategy
26
+ private var scanStrategy: BarcodeScanStrategy
27
+
28
+ /// Whether scanning is paused
29
+ private var scanningPaused: Bool
30
+
31
+ /// Background queue for detection
32
+ private let detectionQueue: DispatchQueue
33
+
34
+ // MARK: - Initialization
35
+
36
+ override init() {
37
+ // Default: all supported formats
38
+ self.supportedSymbologies = [
39
+ .qr, .code128, .code39, .ean13, .ean8,
40
+ .upce, .pdf417, .aztec, .dataMatrix, .itf14
41
+ ]
42
+ self.scanStrategy = .defaultStrategy
43
+ self.scanningPaused = false
44
+ self.detectionQueue = DispatchQueue(label: "com.scanner.detection")
45
+
46
+ super.init()
47
+ }
48
+
49
+ // MARK: - Public Methods
50
+
51
+ /// Detect barcodes in a sample buffer
52
+ /// - Parameter sampleBuffer: The sample buffer to analyze
53
+ func detectBarcodes(in sampleBuffer: CMSampleBuffer) {
54
+ guard !scanningPaused else { return }
55
+
56
+ guard let pixelBuffer = getPixelBuffer(from: sampleBuffer) else {
57
+ return
58
+ }
59
+
60
+ let request = createDetectionRequest()
61
+ let orientation = getImageOrientation(from: sampleBuffer)
62
+
63
+ let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer,
64
+ orientation: orientation,
65
+ options: [:])
66
+
67
+ detectionQueue.async { [weak self] in
68
+ do {
69
+ try handler.perform([request])
70
+ } catch {
71
+ print("[BarcodeDetectionManager] Detection failed: \(error)")
72
+ DispatchQueue.main.async {
73
+ self?.delegate?.barcodeDetectionManager(self!, didFailWith: error)
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ /// Detect barcodes in a CMSampleBuffer with completion
80
+ /// - Parameters:
81
+ /// - sampleBuffer: The sample buffer to analyze
82
+ /// - completion: Completion handler with observations
83
+ func detectBarcodes(in sampleBuffer: CMSampleBuffer,
84
+ completion: @escaping ([VNBarcodeObservation]) -> Void) {
85
+ guard !scanningPaused else {
86
+ completion([])
87
+ return
88
+ }
89
+
90
+ guard let pixelBuffer = getPixelBuffer(from: sampleBuffer) else {
91
+ completion([])
92
+ return
93
+ }
94
+
95
+ let request = VNDetectBarcodesRequest { request, error in
96
+ if let error = error {
97
+ print("[BarcodeDetectionManager] Detection error: \(error)")
98
+ completion([])
99
+ return
100
+ }
101
+
102
+ guard let results = request.results as? [VNBarcodeObservation] else {
103
+ completion([])
104
+ return
105
+ }
106
+
107
+ completion(results)
108
+ }
109
+
110
+ request.symbologies = supportedSymbologies
111
+
112
+ let orientation = getImageOrientation(from: sampleBuffer)
113
+ let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer,
114
+ orientation: orientation,
115
+ options: [:])
116
+
117
+ detectionQueue.async {
118
+ do {
119
+ try handler.perform([request])
120
+ } catch {
121
+ print("[BarcodeDetectionManager] Detection failed: \(error)")
122
+ completion([])
123
+ }
124
+ }
125
+ }
126
+
127
+ // MARK: - BarcodeScannerProtocol Methods
128
+
129
+ /// Pause barcode scanning
130
+ func pauseScanning() {
131
+ scanningPaused = true
132
+ print("[BarcodeDetectionManager] Scanning paused")
133
+ }
134
+
135
+ /// Resume barcode scanning
136
+ func resumeScanning() {
137
+ scanningPaused = false
138
+ print("[BarcodeDetectionManager] Scanning resumed")
139
+ }
140
+
141
+ /// Check if scanning is paused
142
+ /// - Returns: True if scanning is paused
143
+ func isScanningPaused() -> Bool {
144
+ // Implementation: Return paused state
145
+ return scanningPaused
146
+ }
147
+
148
+ /// Set the barcode formats to detect
149
+ /// - Parameter formats: Array of barcode formats
150
+ func setBarcodeFormats(_ formats: [BarcodeFormat]) {
151
+ if formats.isEmpty {
152
+ // If empty, use all supported formats
153
+ supportedSymbologies = [
154
+ .qr, .code128, .code39, .ean13, .ean8,
155
+ .upce, .pdf417, .aztec, .dataMatrix, .itf14
156
+ ]
157
+ } else {
158
+ supportedSymbologies = formats.map { $0.visionSymbology }
159
+ }
160
+ print("[BarcodeDetectionManager] Barcode formats updated: \(supportedSymbologies)")
161
+ }
162
+
163
+ /// Set the scan strategy
164
+ /// - Parameter strategy: The scan strategy to use
165
+ func setScanStrategy(_ strategy: BarcodeScanStrategy) {
166
+ scanStrategy = strategy
167
+ print("[BarcodeDetectionManager] Scan strategy set to: \(strategy.rawValue)")
168
+ }
169
+
170
+ // MARK: - Private Methods
171
+
172
+ /// Create a barcode detection request
173
+ /// - Returns: Configured VNDetectBarcodesRequest
174
+ private func createDetectionRequest() -> VNDetectBarcodesRequest {
175
+ let request = VNDetectBarcodesRequest { [weak self] request, error in
176
+ self?.handleDetectionResults(request: request, error: error)
177
+ }
178
+
179
+ request.symbologies = supportedSymbologies
180
+
181
+ return request
182
+ }
183
+
184
+ /// Process detection results
185
+ /// - Parameters:
186
+ /// - request: The completed request
187
+ /// - error: Any error that occurred
188
+ private func handleDetectionResults(request: VNRequest, error: Error?) {
189
+ if let error = error {
190
+ print("[BarcodeDetectionManager] Detection error: \(error)")
191
+ DispatchQueue.main.async { [weak self] in
192
+ guard let self = self else { return }
193
+ self.delegate?.barcodeDetectionManager(self, didFailWith: error)
194
+ }
195
+ return
196
+ }
197
+
198
+ guard let results = request.results as? [VNBarcodeObservation] else {
199
+ return
200
+ }
201
+
202
+ // Filter out barcodes without valid payload
203
+ let validBarcodes = results.filter { $0.payloadStringValue != nil }
204
+
205
+ if !validBarcodes.isEmpty {
206
+ DispatchQueue.main.async { [weak self] in
207
+ guard let self = self else { return }
208
+ self.delegate?.barcodeDetectionManager(self, didDetect: validBarcodes)
209
+ }
210
+ }
211
+ }
212
+
213
+ /// Get pixel buffer from sample buffer
214
+ /// - Parameter sampleBuffer: The sample buffer
215
+ /// - Returns: Pixel buffer or nil
216
+ private func getPixelBuffer(from sampleBuffer: CMSampleBuffer) -> CVPixelBuffer? {
217
+ return CMSampleBufferGetImageBuffer(sampleBuffer)
218
+ }
219
+
220
+ /// Get image orientation from sample buffer
221
+ /// - Parameter sampleBuffer: The sample buffer
222
+ /// - Returns: CGImagePropertyOrientation
223
+ private func getImageOrientation(from sampleBuffer: CMSampleBuffer) -> CGImagePropertyOrientation {
224
+ // For most cases, .up works well
225
+ // In production, you might want to account for device orientation
226
+ return .up
227
+ }
228
+ }
229
+
@@ -0,0 +1,175 @@
1
+ //
2
+ // BarcodeFrameManager.swift
3
+ // react-native-scanner
4
+ //
5
+ // Manages barcode frame lifecycle with timeout
6
+ //
7
+
8
+ import Foundation
9
+ import CoreGraphics
10
+
11
+ /// Manages barcode frames with automatic timeout and cleanup
12
+ class BarcodeFrameManager {
13
+
14
+ // MARK: - Public Properties
15
+
16
+ /// Delegate for frame change notifications
17
+ weak var delegate: BarcodeFrameManagerDelegate?
18
+
19
+ /// Callback for frame changes
20
+ var onFramesChanged: (([CGRect]) -> Void)?
21
+
22
+ // MARK: - Private Properties
23
+
24
+ /// Active barcode frames mapped by barcode data
25
+ private var activeFrames: [String: BarcodeFrame] = [:]
26
+
27
+ /// Timeout duration for frames (seconds)
28
+ private let frameTimeout: TimeInterval
29
+
30
+ /// Timer for cleanup
31
+ private var cleanupTimer: Timer?
32
+
33
+ /// Queue for thread-safe access
34
+ private let queue: DispatchQueue
35
+
36
+ // MARK: - Initialization
37
+
38
+ init(frameTimeout: TimeInterval = 1.0) {
39
+ self.frameTimeout = frameTimeout
40
+ self.queue = DispatchQueue(label: "com.scanner.framemanager", attributes: .concurrent)
41
+ }
42
+
43
+ // MARK: - Public Methods
44
+
45
+ /// Update barcode frames with new detections
46
+ /// - Parameter frames: Dictionary mapping barcode data to rectangles
47
+ func updateBarcodeFrames(_ frames: [String: CGRect]) {
48
+ queue.async(flags: .barrier) { [weak self] in
49
+ guard let self = self else { return }
50
+
51
+ let now = Date()
52
+ let currentKeys = Set(frames.keys)
53
+
54
+ // Update or add frames with current timestamp
55
+ for (key, rect) in frames {
56
+ self.activeFrames[key] = BarcodeFrame(rect: rect, lastSeenTime: now)
57
+ }
58
+
59
+ // Remove frames not in current detection
60
+ self.activeFrames = self.activeFrames.filter { currentKeys.contains($0.key) }
61
+
62
+ // Schedule cleanup if we have frames
63
+ if !self.activeFrames.isEmpty {
64
+ DispatchQueue.main.async {
65
+ self.scheduleCleanup()
66
+ }
67
+ }
68
+
69
+ // Notify observers
70
+ self.notifyFramesChanged()
71
+ }
72
+ }
73
+
74
+ /// Get current active frames for display
75
+ /// - Returns: Array of active frame rectangles
76
+ func getActiveFrames() -> [CGRect] {
77
+ return queue.sync {
78
+ return activeFrames.values.map { $0.rect }
79
+ }
80
+ }
81
+
82
+ /// Clear all barcode frames immediately
83
+ func clearAllFrames() {
84
+ queue.async(flags: .barrier) { [weak self] in
85
+ guard let self = self else { return }
86
+
87
+ let hadFrames = !self.activeFrames.isEmpty
88
+ self.activeFrames.removeAll()
89
+
90
+ DispatchQueue.main.async {
91
+ self.cleanupTimer?.invalidate()
92
+ self.cleanupTimer = nil
93
+
94
+ if hadFrames {
95
+ self.notifyFramesChanged()
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ /// Shutdown the manager and cleanup resources
102
+ func shutdown() {
103
+ clearAllFrames()
104
+ print("[BarcodeFrameManager] Shutdown")
105
+ }
106
+
107
+ // MARK: - Private Methods
108
+
109
+ /// Schedule cleanup of stale frames
110
+ private func scheduleCleanup() {
111
+ // Cancel existing timer
112
+ cleanupTimer?.invalidate()
113
+
114
+ // Schedule new timer
115
+ cleanupTimer = Timer.scheduledTimer(
116
+ timeInterval: frameTimeout,
117
+ target: self,
118
+ selector: #selector(cleanupStaleFrames),
119
+ userInfo: nil,
120
+ repeats: false
121
+ )
122
+ }
123
+
124
+ /// Clean up stale frames
125
+ @objc private func cleanupStaleFrames() {
126
+ queue.async(flags: .barrier) { [weak self] in
127
+ guard let self = self else { return }
128
+
129
+ let now = Date()
130
+ let countBefore = self.activeFrames.count
131
+
132
+ // Remove frames older than timeout
133
+ self.activeFrames = self.activeFrames.filter { _, frame in
134
+ now.timeIntervalSince(frame.lastSeenTime) < self.frameTimeout
135
+ }
136
+
137
+ let framesRemoved = countBefore - self.activeFrames.count
138
+
139
+ if framesRemoved > 0 {
140
+ print("[BarcodeFrameManager] Removed \(framesRemoved) stale frame(s)")
141
+ self.notifyFramesChanged()
142
+ }
143
+
144
+ // Reschedule if frames remain
145
+ if !self.activeFrames.isEmpty {
146
+ DispatchQueue.main.async {
147
+ self.scheduleCleanup()
148
+ }
149
+ } else {
150
+ print("[BarcodeFrameManager] No more frames to monitor")
151
+ }
152
+ }
153
+ }
154
+
155
+ /// Notify observers of frame changes
156
+ private func notifyFramesChanged() {
157
+ let currentFrames = activeFrames.values.map { $0.rect }
158
+
159
+ DispatchQueue.main.async { [weak self] in
160
+ guard let self = self else { return }
161
+
162
+ // Call delegate
163
+ self.delegate?.barcodeFrameManager(self, didUpdateFrames: currentFrames)
164
+
165
+ // Call callback
166
+ self.onFramesChanged?(currentFrames)
167
+ }
168
+ }
169
+
170
+ deinit {
171
+ // Implementation: Cleanup timer
172
+ cleanupTimer?.invalidate()
173
+ }
174
+ }
175
+
@@ -0,0 +1,102 @@
1
+ //
2
+ // BarcodeFrameOverlayView.swift
3
+ // react-native-scanner
4
+ //
5
+ // Overlay view for barcode frame visualization
6
+ //
7
+
8
+ import UIKit
9
+
10
+ /// View that draws rectangles around detected barcodes
11
+ class BarcodeFrameOverlayView: UIView, BarcodeFrameDisplayProtocol {
12
+
13
+ // MARK: - Public Properties
14
+
15
+ /// Color for barcode frame borders
16
+ var frameColor: UIColor = .yellow {
17
+ didSet { setNeedsDisplay() }
18
+ }
19
+
20
+ /// Width of the frame stroke
21
+ var frameStrokeWidth: CGFloat = 3.0 {
22
+ didSet { setNeedsDisplay() }
23
+ }
24
+
25
+ // MARK: - Private Properties
26
+
27
+ /// Array of barcode rectangles to draw
28
+ private var barcodeBoxes: [CGRect] = [] {
29
+ didSet { setNeedsDisplay() }
30
+ }
31
+
32
+ // MARK: - Initialization
33
+
34
+ override init(frame: CGRect) {
35
+ super.init(frame: frame)
36
+ setupView()
37
+ }
38
+
39
+ required init?(coder: NSCoder) {
40
+ super.init(coder: coder)
41
+ setupView()
42
+ }
43
+
44
+ private func setupView() {
45
+ // Implementation: Configure view properties
46
+ backgroundColor = .clear
47
+ isUserInteractionEnabled = false
48
+ }
49
+
50
+ // MARK: - Drawing
51
+
52
+ override func draw(_ rect: CGRect) {
53
+ super.draw(rect)
54
+
55
+ guard let context = UIGraphicsGetCurrentContext() else { return }
56
+
57
+ // Implementation: Draw rectangles for each barcode
58
+ drawBarcodeFrames(in: context)
59
+ }
60
+
61
+ /// Draw barcode frame rectangles
62
+ /// - Parameter context: Graphics context
63
+ private func drawBarcodeFrames(in context: CGContext) {
64
+ guard !barcodeBoxes.isEmpty else { return }
65
+
66
+ // Set stroke color and width
67
+ context.setStrokeColor(frameColor.cgColor)
68
+ context.setLineWidth(frameStrokeWidth)
69
+ context.setLineCap(.round)
70
+ context.setLineJoin(.round)
71
+
72
+ // Draw rectangle for each barcode box
73
+ for box in barcodeBoxes {
74
+ context.stroke(box)
75
+ }
76
+ }
77
+
78
+ // MARK: - BarcodeFrameDisplayProtocol Methods
79
+
80
+ /// Update the barcode frames configuration
81
+ /// - Parameter config: The new configuration
82
+ func updateBarcodeFrames(config: BarcodeFramesConfig) {
83
+ frameColor = config.color
84
+ setNeedsDisplay()
85
+ }
86
+
87
+ /// Set the barcode boxes to display
88
+ /// - Parameter boxes: Array of rectangles to display
89
+ func setBarcodeBoxes(_ boxes: [CGRect]) {
90
+ // Implementation: Update boxes on main thread
91
+ DispatchQueue.main.async { [weak self] in
92
+ self?.barcodeBoxes = boxes
93
+ }
94
+ }
95
+
96
+ /// Clear all barcode boxes
97
+ func clearBarcodeBoxes() {
98
+ // Implementation: Clear boxes array
99
+ setBarcodeBoxes([])
100
+ }
101
+ }
102
+