@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,358 @@
1
+ #import "ScannerView.h"
2
+
3
+ #import <react/renderer/components/ScannerViewSpec/ComponentDescriptors.h>
4
+ #import <react/renderer/components/ScannerViewSpec/EventEmitters.h>
5
+ #import <react/renderer/components/ScannerViewSpec/Props.h>
6
+ #import <react/renderer/components/ScannerViewSpec/RCTComponentViewHelpers.h>
7
+
8
+ #import "RCTFabricComponentsPlugins.h"
9
+
10
+ // Import of the generated Swift header is not required because we use dynamic lookup (NSClassFromString).
11
+ // If you later want to statically reference Swift symbols, import "<ProductModuleName>-Swift.h" instead,
12
+ // where ProductModuleName matches your target's Packaging > Product Module Name.
13
+
14
+ using namespace facebook::react;
15
+
16
+ // No need to forward-declare ScannerViewImpl now, since we will use dynamic lookup at runtime.
17
+
18
+ @interface ScannerView () <RCTScannerViewViewProtocol>
19
+ @property (nonatomic, strong) UIView *scannerImpl;
20
+ @end
21
+
22
+ @implementation ScannerView
23
+
24
+ + (ComponentDescriptorProvider)componentDescriptorProvider
25
+ {
26
+ return concreteComponentDescriptorProvider<ScannerViewComponentDescriptor>();
27
+ }
28
+
29
+ - (instancetype)initWithFrame:(CGRect)frame
30
+ {
31
+ if (self = [super initWithFrame:frame]) {
32
+ static const auto defaultProps = std::make_shared<const ScannerViewProps>();
33
+ _props = defaultProps;
34
+
35
+ NSLog(@"[ScannerView] 🚀 Starting initialization...");
36
+
37
+ // Initialize Swift implementation
38
+ // Try with module name first (Scanner is the module name from Scanner.podspec)
39
+ Class scannerImplClass = NSClassFromString(@"ScannerViewImpl");
40
+ if (!scannerImplClass) {
41
+ scannerImplClass = NSClassFromString(@"Scanner.ScannerViewImpl");
42
+ }
43
+ if (!scannerImplClass) {
44
+ scannerImplClass = NSClassFromString(@"react_native_scanner.ScannerViewImpl");
45
+ }
46
+
47
+ if (scannerImplClass) {
48
+ NSLog(@"[ScannerView] ✅ Found ScannerViewImpl class: %@", scannerImplClass);
49
+
50
+ // Create instance
51
+ _scannerImpl = [[scannerImplClass alloc] initWithFrame:CGRectZero];
52
+
53
+ if (_scannerImpl) {
54
+ NSLog(@"[ScannerView] ✅ ScannerViewImpl instance created successfully");
55
+
56
+ // Try to set delegate using the property directly
57
+ if ([_scannerImpl respondsToSelector:@selector(setDelegate:)]) {
58
+ [_scannerImpl performSelector:@selector(setDelegate:) withObject:self];
59
+ NSLog(@"[ScannerView] ✅ Delegate set successfully using setDelegate:");
60
+ } else {
61
+ NSLog(@"[ScannerView] ⚠️ setDelegate: selector not found");
62
+ }
63
+
64
+ // IMPORTANT (Fabric): set the component's contentView to our Swift implementation.
65
+ // Fabric will size `contentView` via `updateLayoutMetrics`, not necessarily via `layoutSubviews`.
66
+ self.contentView = _scannerImpl;
67
+ _scannerImpl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
68
+ _scannerImpl.frame = self.bounds;
69
+
70
+ NSLog(@"[ScannerView] ✅ View hierarchy setup complete (contentView bounds: %@, scannerImpl frame: %@)",
71
+ NSStringFromCGRect(self.contentView.bounds),
72
+ NSStringFromCGRect(_scannerImpl.frame));
73
+ } else {
74
+ NSLog(@"[ScannerView] ❌ Failed to create ScannerViewImpl instance");
75
+ }
76
+ } else {
77
+ NSLog(@"[ScannerView] ❌ ERROR: ScannerViewImpl class not found!");
78
+ NSLog(@"[ScannerView] Tried: ScannerViewImpl, Scanner.ScannerViewImpl, react_native_scanner.ScannerViewImpl");
79
+ }
80
+ }
81
+
82
+ return self;
83
+ }
84
+
85
+ - (void)updateLayoutMetrics:(LayoutMetrics const &)layoutMetrics oldLayoutMetrics:(LayoutMetrics const &)oldLayoutMetrics
86
+ {
87
+ [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
88
+
89
+ // Fabric layout updates flow through here. Ensure the Swift view always matches our bounds.
90
+ if (_scannerImpl) {
91
+ _scannerImpl.frame = self.bounds;
92
+ NSLog(@"[ScannerView] 📏 updateLayoutMetrics - bounds: %@, scannerImpl frame: %@",
93
+ NSStringFromCGRect(self.bounds),
94
+ NSStringFromCGRect(_scannerImpl.frame));
95
+ } else {
96
+ NSLog(@"[ScannerView] 📏 updateLayoutMetrics - bounds: %@ (no scannerImpl yet)", NSStringFromCGRect(self.bounds));
97
+ }
98
+ }
99
+
100
+ - (void)prepareForRecycle
101
+ {
102
+ [super prepareForRecycle];
103
+ NSLog(@"[ScannerView] prepareForRecycle called");
104
+ }
105
+
106
+ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
107
+ {
108
+ [super mountChildComponentView:childComponentView index:index];
109
+ NSLog(@"[ScannerView] mountChildComponentView called at index: %ld", (long)index);
110
+ }
111
+
112
+ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask
113
+ {
114
+ [super finalizeUpdates:updateMask];
115
+ NSLog(@"[ScannerView] 🔄 finalizeUpdates - frame: %@, bounds: %@", NSStringFromCGRect(self.frame), NSStringFromCGRect(self.bounds));
116
+
117
+ // Force layout when we have a valid frame
118
+ if (self.bounds.size.width > 0 && self.bounds.size.height > 0) {
119
+ if (_scannerImpl) {
120
+ _scannerImpl.frame = self.contentView.bounds;
121
+ NSLog(@"[ScannerView] 🔧 Force updated scannerImpl frame in finalizeUpdates: %@", NSStringFromCGRect(_scannerImpl.frame));
122
+ }
123
+ }
124
+ }
125
+
126
+ - (void)layoutSubviews
127
+ {
128
+ [super layoutSubviews];
129
+
130
+ NSLog(@"[ScannerView] 📐 layoutSubviews - contentView bounds: %@", NSStringFromCGRect(self.contentView.bounds));
131
+
132
+ // Update the scanner impl frame to match contentView bounds
133
+ if (_scannerImpl) {
134
+ _scannerImpl.frame = self.contentView.bounds;
135
+ NSLog(@"[ScannerView] Updated scannerImpl frame to: %@", NSStringFromCGRect(_scannerImpl.frame));
136
+ }
137
+ }
138
+
139
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
140
+ {
141
+ const auto &oldViewProps = *std::static_pointer_cast<ScannerViewProps const>(_props);
142
+ const auto &newViewProps = *std::static_pointer_cast<ScannerViewProps const>(props);
143
+
144
+ if (!_scannerImpl) {
145
+ [super updateProps:props oldProps:oldProps];
146
+ return;
147
+ }
148
+
149
+ // Barcode types
150
+ if (oldViewProps.barcodeTypes != newViewProps.barcodeTypes) {
151
+ NSMutableArray *formats = [NSMutableArray new];
152
+ for (const auto &format : newViewProps.barcodeTypes) {
153
+ [formats addObject:[NSString stringWithUTF8String:format.c_str()]];
154
+ }
155
+ [_scannerImpl performSelector:@selector(setBarcodeTypes:) withObject:formats];
156
+ }
157
+
158
+ // Focus area (overlay + optional filtering)
159
+ auto focusAreaChanged = (
160
+ oldViewProps.focusArea.enabled != newViewProps.focusArea.enabled ||
161
+ oldViewProps.focusArea.showOverlay != newViewProps.focusArea.showOverlay ||
162
+ oldViewProps.focusArea.borderColor != newViewProps.focusArea.borderColor ||
163
+ oldViewProps.focusArea.tintColor != newViewProps.focusArea.tintColor ||
164
+ oldViewProps.focusArea.size.width != newViewProps.focusArea.size.width ||
165
+ oldViewProps.focusArea.size.height != newViewProps.focusArea.size.height ||
166
+ oldViewProps.focusArea.position.x != newViewProps.focusArea.position.x ||
167
+ oldViewProps.focusArea.position.y != newViewProps.focusArea.position.y
168
+ );
169
+ if (focusAreaChanged) {
170
+ NSMutableDictionary *focusAreaDict = [NSMutableDictionary dictionary];
171
+
172
+ [focusAreaDict setObject:@(newViewProps.focusArea.enabled) forKey:@"enabled"];
173
+ [focusAreaDict setObject:@(newViewProps.focusArea.showOverlay) forKey:@"showOverlay"];
174
+
175
+ if (!newViewProps.focusArea.borderColor.empty()) {
176
+ [focusAreaDict setObject:[NSString stringWithUTF8String:newViewProps.focusArea.borderColor.c_str()] forKey:@"borderColor"];
177
+ }
178
+ if (!newViewProps.focusArea.tintColor.empty()) {
179
+ [focusAreaDict setObject:[NSString stringWithUTF8String:newViewProps.focusArea.tintColor.c_str()] forKey:@"tintColor"];
180
+ }
181
+
182
+ // size: either number or {width,height}
183
+ if (newViewProps.focusArea.size.width > 0 || newViewProps.focusArea.size.height > 0) {
184
+ if (newViewProps.focusArea.size.width == newViewProps.focusArea.size.height) {
185
+ [focusAreaDict setObject:@(newViewProps.focusArea.size.width) forKey:@"size"];
186
+ } else {
187
+ [focusAreaDict setObject:@{
188
+ @"width": @(newViewProps.focusArea.size.width),
189
+ @"height": @(newViewProps.focusArea.size.height)
190
+ } forKey:@"size"];
191
+ }
192
+ }
193
+
194
+ // position: {x,y} in 0-100
195
+ // Android defaults to 50/50 when position isn't provided.
196
+ // Codegen initializes missing nested fields to 0.0, so we only forward position when non-zero.
197
+ // Note: this means explicitly setting {x:0,y:0} from JS is not representable with this heuristic.
198
+ if (newViewProps.focusArea.position.x != 0.0 || newViewProps.focusArea.position.y != 0.0) {
199
+ [focusAreaDict setObject:@{
200
+ @"x": @(newViewProps.focusArea.position.x),
201
+ @"y": @(newViewProps.focusArea.position.y)
202
+ } forKey:@"position"];
203
+ }
204
+
205
+ if ([_scannerImpl respondsToSelector:@selector(configureFocusArea:)]) {
206
+ [_scannerImpl performSelector:@selector(configureFocusArea:) withObject:focusAreaDict];
207
+ }
208
+ }
209
+
210
+ // Barcode frames overlay
211
+ auto barcodeFramesChanged = (
212
+ oldViewProps.barcodeFrames.enabled != newViewProps.barcodeFrames.enabled ||
213
+ oldViewProps.barcodeFrames.onlyInFocusArea != newViewProps.barcodeFrames.onlyInFocusArea ||
214
+ oldViewProps.barcodeFrames.color != newViewProps.barcodeFrames.color
215
+ );
216
+ if (barcodeFramesChanged) {
217
+ NSMutableDictionary *barcodeFramesDict = [NSMutableDictionary dictionary];
218
+ [barcodeFramesDict setObject:@(newViewProps.barcodeFrames.enabled) forKey:@"enabled"];
219
+ [barcodeFramesDict setObject:@(newViewProps.barcodeFrames.onlyInFocusArea) forKey:@"onlyInFocusArea"];
220
+
221
+ if (!newViewProps.barcodeFrames.color.empty()) {
222
+ [barcodeFramesDict setObject:[NSString stringWithUTF8String:newViewProps.barcodeFrames.color.c_str()] forKey:@"color"];
223
+ }
224
+
225
+ if ([_scannerImpl respondsToSelector:@selector(configureBarcodeFrames:)]) {
226
+ [_scannerImpl performSelector:@selector(configureBarcodeFrames:) withObject:barcodeFramesDict];
227
+ }
228
+ }
229
+
230
+ // Torch
231
+ if (oldViewProps.torch != newViewProps.torch) {
232
+ NSNumber *torchValue = @(newViewProps.torch);
233
+ [_scannerImpl performSelector:@selector(setTorchEnabled:) withObject:torchValue];
234
+ }
235
+
236
+ // Zoom
237
+ if (oldViewProps.zoom != newViewProps.zoom) {
238
+ NSNumber *zoomValue = @(newViewProps.zoom);
239
+ [_scannerImpl performSelector:@selector(setZoomLevel:) withObject:zoomValue];
240
+ }
241
+
242
+ // Pause scanning
243
+ if (oldViewProps.pauseScanning != newViewProps.pauseScanning) {
244
+ NSNumber *pauseValue = @(newViewProps.pauseScanning);
245
+ [_scannerImpl performSelector:@selector(setPauseScanning:) withObject:pauseValue];
246
+ }
247
+
248
+ // Barcode scan strategy
249
+ if (oldViewProps.barcodeScanStrategy != newViewProps.barcodeScanStrategy) {
250
+ NSString *strategy = [NSString stringWithUTF8String:newViewProps.barcodeScanStrategy.c_str()];
251
+ if ([_scannerImpl respondsToSelector:@selector(setBarcodeScanStrategy:)]) {
252
+ [_scannerImpl performSelector:@selector(setBarcodeScanStrategy:) withObject:strategy];
253
+ }
254
+ }
255
+
256
+ // Keep screen on
257
+ if (oldViewProps.keepScreenOn != newViewProps.keepScreenOn) {
258
+ NSNumber *keepScreenOnValue = @(newViewProps.keepScreenOn);
259
+ if ([_scannerImpl respondsToSelector:@selector(setKeepScreenOn:)]) {
260
+ [_scannerImpl performSelector:@selector(setKeepScreenOn:) withObject:keepScreenOnValue];
261
+ }
262
+ }
263
+
264
+ // Barcode emission interval
265
+ if (oldViewProps.barcodeEmissionInterval != newViewProps.barcodeEmissionInterval) {
266
+ NSNumber *intervalValue = @(newViewProps.barcodeEmissionInterval);
267
+ if ([_scannerImpl respondsToSelector:@selector(setBarcodeEmissionInterval:)]) {
268
+ [_scannerImpl performSelector:@selector(setBarcodeEmissionInterval:) withObject:intervalValue];
269
+ }
270
+ }
271
+
272
+ [super updateProps:props oldProps:oldProps];
273
+ }
274
+
275
+ // MARK: - ScannerViewDelegate (implemented via performSelector to avoid Swift header dependency)
276
+
277
+ - (void)scannerDidDetectBarcodes:(NSArray<NSDictionary *> *)barcodes
278
+ {
279
+ if (_eventEmitter) {
280
+ auto scannerEventEmitter = std::static_pointer_cast<const ScannerViewEventEmitter>(_eventEmitter);
281
+
282
+ std::vector<ScannerViewEventEmitter::OnBarcodeScannedBarcodes> barcodesVector;
283
+ barcodesVector.reserve(barcodes.count);
284
+ for (NSDictionary *barcode in barcodes) {
285
+ ScannerViewEventEmitter::OnBarcodeScannedBarcodes item{};
286
+ NSString *dataStr = barcode[@"data"];
287
+ NSString *formatStr = barcode[@"format"];
288
+ NSNumber *timestampNum = barcode[@"timestamp"];
289
+
290
+ item.data = dataStr ? std::string([dataStr UTF8String]) : std::string();
291
+ item.format = formatStr ? std::string([formatStr UTF8String]) : std::string();
292
+ item.timestamp = timestampNum ? [timestampNum doubleValue] : 0.0;
293
+
294
+ // boundingBox is required by codegen; fill with zeros if missing
295
+ ScannerViewEventEmitter::OnBarcodeScannedBarcodesBoundingBox bbox{};
296
+ NSDictionary *box = barcode[@"boundingBox"];
297
+ if (box) {
298
+ bbox.left = [box[@"left"] doubleValue];
299
+ bbox.top = [box[@"top"] doubleValue];
300
+ bbox.right = [box[@"right"] doubleValue];
301
+ bbox.bottom = [box[@"bottom"] doubleValue];
302
+ } else {
303
+ bbox.left = 0.0;
304
+ bbox.top = 0.0;
305
+ bbox.right = 0.0;
306
+ bbox.bottom = 0.0;
307
+ }
308
+ item.boundingBox = bbox;
309
+
310
+ // area is required by codegen; default to 0 if absent
311
+ NSNumber *areaNum = barcode[@"area"];
312
+ item.area = areaNum ? [areaNum doubleValue] : 0.0;
313
+
314
+ barcodesVector.push_back(std::move(item));
315
+ }
316
+
317
+ ScannerViewEventEmitter::OnBarcodeScanned event = {
318
+ .barcodes = std::move(barcodesVector)
319
+ };
320
+
321
+ scannerEventEmitter->onBarcodeScanned(event);
322
+ }
323
+ }
324
+
325
+ - (void)scannerDidEncounterError:(NSDictionary<NSString *,id> *)error
326
+ {
327
+ if (_eventEmitter) {
328
+ auto scannerEventEmitter = std::static_pointer_cast<const ScannerViewEventEmitter>(_eventEmitter);
329
+
330
+ ScannerViewEventEmitter::OnScannerError event = {
331
+ .error = std::string([[error objectForKey:@"error"] UTF8String]),
332
+ .code = std::string([[error objectForKey:@"code"] UTF8String])
333
+ };
334
+
335
+ scannerEventEmitter->onScannerError(event);
336
+ }
337
+ }
338
+
339
+ - (void)scannerDidLoad:(NSDictionary<NSString *,id> *)info
340
+ {
341
+ if (_eventEmitter) {
342
+ auto scannerEventEmitter = std::static_pointer_cast<const ScannerViewEventEmitter>(_eventEmitter);
343
+
344
+ ScannerViewEventEmitter::OnLoad event = {
345
+ .success = [[info objectForKey:@"success"] boolValue],
346
+ .error = info[@"error"] ? std::string([[info objectForKey:@"error"] UTF8String]) : ""
347
+ };
348
+
349
+ scannerEventEmitter->onLoad(event);
350
+ }
351
+ }
352
+
353
+ Class<RCTComponentViewProtocol> ScannerViewCls(void)
354
+ {
355
+ return ScannerView.class;
356
+ }
357
+
358
+ @end