@scr2em/capacitor-scanner 6.0.29 → 6.0.31

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.
package/README.md CHANGED
@@ -262,7 +262,7 @@ Set the camera zoom level.
262
262
 
263
263
  #### StartOptions
264
264
 
265
- <code>{ /** * Barcode formats to detect when barcodeDetection is enabled. */ formats?: BarcodeFormat[]; /** * Camera direction to use. Defaults to 'BACK'. */ cameraDirection?: 'BACK' | 'FRONT'; /** * Enable barcode detection on start. Defaults to false. * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events. */ barcodeDetection?: boolean; /** * Whether to show the highlight overlay for detected barcodes. Defaults to false. * Only applies when barcodeDetection is true. */ barcodeHighlight?: boolean; /** * Enable object detection (business card) on start. Defaults to false. * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events. */ objectDetection?: boolean; /** * Whether to show the highlight overlay for detected business cards. Defaults to true. * Only applies when objectDetection is true. */ objectHighlight?: boolean; /** * Minimum interval in seconds between consecutive 'rectangleDetected' events. * Only applies when objectDetection is true. * Defaults to 0 (no throttling beyond initial stability check). */ rectangleEmitIntervalSeconds?: number; debounceTimeInMilli?: number; /** * Optional regex pattern to filter scanned barcodes. * If provided, only barcodes matching this pattern will be reported. */ regex?: string; /** * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline). */ regexFlags?: string; }</code>
265
+ <code>{ /** * Barcode formats to detect when barcodeDetection is enabled. */ formats?: BarcodeFormat[]; /** * Camera direction to use. Defaults to 'BACK'. */ cameraDirection?: 'BACK' | 'FRONT'; /** * Enable barcode detection on start. Defaults to false. * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events. */ barcodeDetection?: boolean; /** * Whether to show the highlight overlay for detected barcodes. Defaults to false. * Only applies when barcodeDetection is true. */ barcodeHighlight?: boolean; /** * Enable object detection (business card) on start. Defaults to false. * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events. */ objectDetection?: boolean; /** * Whether to show the highlight overlay for detected business cards. Defaults to true. * Only applies when objectDetection is true. */ objectHighlight?: boolean; /** * Minimum interval in seconds between consecutive 'rectangleDetected' events. * Only applies when objectDetection is true. * Defaults to 0 (no throttling beyond initial stability check). */ rectangleEmitIntervalSeconds?: number; /** * Optional regex pattern to filter scanned barcodes. * If provided, only barcodes matching this pattern will be reported. */ regex?: string; /** * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline). */ regexFlags?: string; /** * Duration in seconds to pause rectangle detection after a barcode is successfully scanned. * When both barcode and object detection are enabled, this gives barcode scanning priority * by temporarily suppressing rectangle detection after a barcode is emitted. * Defaults to 0 (no throttle, both detections run simultaneously). */ detectionThrottleSeconds?: number; }</code>
266
266
 
267
267
 
268
268
  #### CapturePhotoOptions
@@ -143,6 +143,10 @@ public class CapacitorScannerPlugin extends Plugin {
143
143
  // Configurable interval between rectangle detection event emissions (in milliseconds)
144
144
  private long rectangleEmitIntervalMs = 0; // Default: 0 (no throttling)
145
145
 
146
+ // Detection throttle: pause rectangle detection after a successful barcode scan
147
+ private long detectionThrottleMs = 0; // Default: 0 (no throttle)
148
+ private long lastSuccessfulBarcodeScanTime = 0;
149
+
146
150
  /**
147
151
  * Calculates the processing interval in milliseconds based on the desired
148
152
  * frequency per second.
@@ -230,6 +234,19 @@ public class CapacitorScannerPlugin extends Plugin {
230
234
  }
231
235
  }
232
236
 
237
+ // Read detection throttle
238
+ if (call.getData().has("detectionThrottleSeconds")) {
239
+ try {
240
+ double throttleSeconds = call.getData().getDouble("detectionThrottleSeconds");
241
+ detectionThrottleMs = Math.max(0, (long) (throttleSeconds * 1000));
242
+ } catch (Exception e) {
243
+ detectionThrottleMs = 0;
244
+ }
245
+ } else {
246
+ detectionThrottleMs = 0;
247
+ }
248
+ lastSuccessfulBarcodeScanTime = 0;
249
+
233
250
  try {
234
251
  String cameraDirectionStr = call.getString("cameraDirection", "BACK");
235
252
  int lensFacing;
@@ -449,152 +466,169 @@ public class CapacitorScannerPlugin extends Plugin {
449
466
 
450
467
  @Override
451
468
  public void analyze(@NonNull ImageProxy imageProxy) {
452
- try (imageProxy) {
453
- @ExperimentalGetImage
454
- android.media.Image mediaImage = imageProxy.getImage();
455
- if (mediaImage != null) {
456
- InputImage image = InputImage.fromMediaImage(mediaImage,
457
- imageProxy.getImageInfo().getRotationDegrees());
458
-
459
- // Barcode detection - only process if barcode detection is enabled
460
- if (scanner != null && enabledDetectionTypes.contains("barcode")) {
461
- scanner.process(image)
462
- .addOnSuccessListener(executor, barcodes -> {
463
- processBarcodes(barcodes);
464
- // Only display barcode overlay if highlight is enabled
465
- if (enabledHighlightTypes.contains("barcode")) {
466
- displayDetectedBarcodes(barcodes, imageProxy.getWidth(), imageProxy.getHeight(),
467
- imageProxy.getImageInfo().getRotationDegrees());
468
- }
469
- })
470
- .addOnFailureListener(executor, e -> {
471
- echo("Failed to process image for barcode: " + e.getMessage());
472
- });
473
- }
469
+ @ExperimentalGetImage
470
+ android.media.Image mediaImage = imageProxy.getImage();
471
+ if (mediaImage == null) {
472
+ imageProxy.close();
473
+ return;
474
+ }
474
475
 
475
- // Rectangle detection
476
- if (rectangleDetector != null && enabledDetectionTypes.contains("businessCard")) {
477
- DetectionResult detectionResult = null;
478
-
479
- // Only perform detection if enough time has passed since last detection
480
- // This throttles processing based on configured frequency
481
- long currentTime = System.currentTimeMillis();
482
- long processingIntervalMs = calculateProcessingInterval(processingFrequency);
483
- if (currentTime - lastRectangleProcessingTime >= processingIntervalMs) {
484
- // Perform new detection
485
- echo("Starting rectangle detection for businessCard");
486
- Bitmap bitmap = null;
487
- try {
488
- // Convert ImageProxy to Bitmap using the sample's toBitmap method
489
- bitmap = imageProxyToBitmap(imageProxy);
476
+ // Capture metadata upfront so it's available after imageProxy is closed
477
+ final int imageWidth = imageProxy.getWidth();
478
+ final int imageHeight = imageProxy.getHeight();
479
+ final int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
490
480
 
491
- echo("Converting image for rectangle detection - bitmap size: " + bitmap.getWidth()
492
- + "x" + bitmap.getHeight());
481
+ try {
482
+ InputImage image = InputImage.fromMediaImage(mediaImage, rotationDegrees);
483
+
484
+ boolean barcodeEnabled = scanner != null && enabledDetectionTypes.contains("barcode");
485
+ boolean isRectangleThrottled = detectionThrottleMs > 0
486
+ && lastSuccessfulBarcodeScanTime > 0
487
+ && (System.currentTimeMillis() - lastSuccessfulBarcodeScanTime) < detectionThrottleMs;
488
+ boolean rectangleEnabled = rectangleDetector != null
489
+ && enabledDetectionTypes.contains("businessCard")
490
+ && !isRectangleThrottled;
491
+
492
+ // Clear rectangle overlay when throttled
493
+ if (isRectangleThrottled && enabledDetectionTypes.contains("businessCard")) {
494
+ getActivity().runOnUiThread(() -> {
495
+ synchronized (overlayLock) {
496
+ currentRectangleRects.clear();
497
+ updateOverlayDetections(currentBarcodeRects, currentRectangleRects);
498
+ }
499
+ });
500
+ }
493
501
 
494
- long startTime = System.currentTimeMillis();
495
- DetectionResult rawResult = rectangleDetector.detectRectangles(bitmap);
496
- echo("Rectangle detection took " + (System.currentTimeMillis() - startTime) + "ms");
502
+ // Rectangle detection (synchronous) - runs first while imageProxy is still open
503
+ if (rectangleEnabled) {
504
+ DetectionResult detectionResult = null;
505
+
506
+ // Only perform detection if enough time has passed since last detection
507
+ // This throttles processing based on configured frequency
508
+ long currentTime = System.currentTimeMillis();
509
+ long processingIntervalMs = calculateProcessingInterval(processingFrequency);
510
+ if (currentTime - lastRectangleProcessingTime >= processingIntervalMs) {
511
+ // Perform new detection
512
+ Bitmap bitmap = null;
513
+ try {
514
+ // Convert ImageProxy to Bitmap using the sample's toBitmap method
515
+ bitmap = imageProxyToBitmap(imageProxy);
497
516
 
498
- // Apply geometric filtering to eliminate false positives
499
- List<Rectangle> filteredRectangles = filterRectangles(rawResult.getRectangles(),
500
- rawResult.getImageSize());
517
+ DetectionResult rawResult = rectangleDetector.detectRectangles(bitmap);
501
518
 
502
- // 2) record hit/miss in sliding window
503
- boolean isHit = !filteredRectangles.isEmpty();
504
- detectionHistory.add(isHit);
505
- if (detectionHistory.size() > DETECTION_WINDOW) {
506
- detectionHistory.poll();
507
- }
508
- if (isHit) {
509
- lastGoodRects = filteredRectangles; // update last known good
510
- echo("✅ Hit - saved " + filteredRectangles.size() + " good rectangles");
511
- } else {
512
- echo("❌ Miss - keeping " + lastGoodRects.size() + " previous rectangles");
513
- }
519
+ // Apply geometric filtering to eliminate false positives
520
+ List<Rectangle> filteredRectangles = filterRectangles(rawResult.getRectangles(),
521
+ rawResult.getImageSize());
514
522
 
515
- // 3) decide whether to show based on sliding window
516
- long hits = 0;
517
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
518
- hits = detectionHistory.stream().filter(b -> b).count();
519
- } else {
520
- // For older Android versions, count manually
521
- for (Boolean hit : detectionHistory) {
522
- if (hit)
523
- hits++;
524
- }
525
- }
526
- boolean shouldShow = hits >= Math.ceil(PRESENCE_THRESHOLD * detectionHistory.size());
527
- List<Rectangle> rectanglesToShow = shouldShow ? lastGoodRects : Collections.emptyList();
528
- echo("Window: " + hits + "/" + detectionHistory.size() + " hits - shouldShow: "
529
- + shouldShow);
530
-
531
- // Create detection result with the rectangles we decided to show
532
- detectionResult = new DetectionResult(rawResult.getImageSize(), rectanglesToShow);
533
-
534
- // Store the detection result for reuse on non-processing frames
535
- if (!rectanglesToShow.isEmpty()) {
536
- lastGoodDetectionResult = detectionResult;
537
- lastRectangleDetectionTime = currentTime;
538
- }
523
+ // 2) record hit/miss in sliding window
524
+ boolean isHit = !filteredRectangles.isEmpty();
525
+ detectionHistory.add(isHit);
526
+ if (detectionHistory.size() > DETECTION_WINDOW) {
527
+ detectionHistory.poll();
528
+ }
529
+ if (isHit) {
530
+ lastGoodRects = filteredRectangles; // update last known good
531
+ }
539
532
 
540
- // Process rectangle detection results to emit events if we're showing
541
- // rectangles
542
- if (!rectanglesToShow.isEmpty()) {
543
- processRectangles(detectionResult);
533
+ // 3) decide whether to show based on sliding window
534
+ long hits = 0;
535
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
536
+ hits = detectionHistory.stream().filter(b -> b).count();
537
+ } else {
538
+ // For older Android versions, count manually
539
+ for (Boolean hit : detectionHistory) {
540
+ if (hit)
541
+ hits++;
544
542
  }
543
+ }
544
+ boolean shouldShow = hits >= Math.ceil(PRESENCE_THRESHOLD * detectionHistory.size());
545
+ List<Rectangle> rectanglesToShow = shouldShow ? lastGoodRects : Collections.emptyList();
545
546
 
546
- // Update last processing time
547
- lastRectangleProcessingTime = currentTime;
548
- } catch (Exception e) {
549
- echo("Failed to process image for rectangle detection: " + e.getMessage());
550
- e.printStackTrace();
551
- detectionResult = new DetectionResult(
552
- new Size(imageProxy.getWidth(), imageProxy.getHeight()), new ArrayList<>());
553
- } finally {
554
- // Cleanup bitmap resource in all code paths
555
- if (bitmap != null) {
556
- bitmap.recycle();
557
- }
547
+ // Create detection result with the rectangles we decided to show
548
+ detectionResult = new DetectionResult(rawResult.getImageSize(), rectanglesToShow);
549
+
550
+ // Store the detection result for reuse on non-processing frames
551
+ if (!rectanglesToShow.isEmpty()) {
552
+ lastGoodDetectionResult = detectionResult;
553
+ lastRectangleDetectionTime = currentTime;
558
554
  }
559
- } else {
560
- // For frames we're not processing, reuse the last good detection result
561
- // This ensures the UI stays smooth even though we're only detecting 10
562
- // times/second
563
- if (lastGoodDetectionResult != null &&
564
- (currentTime - lastRectangleDetectionTime) < CACHE_MS) {
565
- detectionResult = lastGoodDetectionResult;
566
- echo("Reusing last good detection result (age: "
567
- + (currentTime - lastRectangleProcessingTime) + "ms)");
568
- } else {
569
- // Create empty result if we have no recent good detection
570
- detectionResult = new DetectionResult(
571
- new Size(imageProxy.getWidth(), imageProxy.getHeight()), new ArrayList<>());
555
+
556
+ // Process rectangle detection results to emit events if we're showing
557
+ // rectangles
558
+ if (!rectanglesToShow.isEmpty()) {
559
+ processRectangles(detectionResult);
572
560
  }
573
- }
574
561
 
575
- // Always display detection results for every frame to keep UI smooth
576
- // This runs for both freshly detected rectangles and reused detection results
577
- final DetectionResult finalResult = detectionResult;
578
- getActivity().runOnUiThread(() -> {
579
- // Only display rectangle overlay if highlight is enabled
580
- if (enabledHighlightTypes.contains("businessCard")) {
581
- // Show the rectangles
582
- displayDetectedRectangles(finalResult,
583
- imageProxy.getWidth(),
584
- imageProxy.getHeight(),
585
- imageProxy.getImageInfo().getRotationDegrees());
562
+ // Update last processing time
563
+ lastRectangleProcessingTime = currentTime;
564
+ } catch (Exception e) {
565
+ echo("Failed to process image for rectangle detection: " + e.getMessage());
566
+ e.printStackTrace();
567
+ detectionResult = new DetectionResult(
568
+ new Size(imageWidth, imageHeight), new ArrayList<>());
569
+ } finally {
570
+ // Cleanup bitmap resource in all code paths
571
+ if (bitmap != null) {
572
+ bitmap.recycle();
586
573
  }
587
- });
574
+ }
588
575
  } else {
589
- // When businessCard detection is disabled, we don't need to do anything here.
590
- // The overlay is cleared by the disableObjectDetection method.
576
+ // For frames we're not processing, reuse the last good detection result
577
+ // This ensures the UI stays smooth even though we're only detecting 10
578
+ // times/second
579
+ if (lastGoodDetectionResult != null &&
580
+ (currentTime - lastRectangleDetectionTime) < CACHE_MS) {
581
+ detectionResult = lastGoodDetectionResult;
582
+ } else {
583
+ // Create empty result if we have no recent good detection
584
+ detectionResult = new DetectionResult(
585
+ new Size(imageWidth, imageHeight), new ArrayList<>());
586
+ }
591
587
  }
588
+
589
+ // Always display detection results for every frame to keep UI smooth
590
+ // This runs for both freshly detected rectangles and reused detection results
591
+ final DetectionResult finalResult = detectionResult;
592
+ getActivity().runOnUiThread(() -> {
593
+ // Only display rectangle overlay if highlight is enabled
594
+ if (enabledHighlightTypes.contains("businessCard")) {
595
+ // Show the rectangles
596
+ displayDetectedRectangles(finalResult,
597
+ imageWidth,
598
+ imageHeight,
599
+ rotationDegrees);
600
+ }
601
+ });
602
+ }
603
+
604
+ // Barcode detection (async) - close imageProxy only after ML Kit finishes
605
+ // processing. The imageProxy must stay open so ML Kit can read the underlying
606
+ // image buffer.
607
+ if (barcodeEnabled) {
608
+ scanner.process(image)
609
+ .addOnSuccessListener(executor, barcodes -> {
610
+ processBarcodes(barcodes);
611
+ // Only display barcode overlay if highlight is enabled
612
+ if (enabledHighlightTypes.contains("barcode")) {
613
+ displayDetectedBarcodes(barcodes, imageWidth, imageHeight,
614
+ rotationDegrees);
615
+ }
616
+ })
617
+ .addOnFailureListener(executor, e -> {
618
+ echo("Failed to process image for barcode: " + e.getMessage());
619
+ })
620
+ .addOnCompleteListener(executor, task -> {
621
+ imageProxy.close();
622
+ });
623
+ } else {
624
+ // No async work pending — safe to close now
625
+ imageProxy.close();
592
626
  }
593
627
  } catch (Exception e) {
594
628
  echo("Unexpected error in image analysis: " + e.getMessage());
595
629
  e.printStackTrace();
630
+ imageProxy.close();
596
631
  }
597
- // Always close the ImageProxy, in all code paths
598
632
  }
599
633
  }
600
634
 
@@ -874,6 +908,10 @@ public class CapacitorScannerPlugin extends Plugin {
874
908
  data.put("scannedCode", rawValue);
875
909
  data.put("format", BarcodeFormatHelper.barcodeFormatToString(format));
876
910
  notifyListeners("barcodeScanned", data, true);
911
+
912
+ // Record successful scan time for detection throttle
913
+ lastSuccessfulBarcodeScanTime = System.currentTimeMillis();
914
+
877
915
  echo("Barcode " + rawValue + " scanned with format: "
878
916
  + BarcodeFormatHelper.barcodeFormatToString(format));
879
917
  }
@@ -1148,6 +1186,8 @@ public class CapacitorScannerPlugin extends Plugin {
1148
1186
  lastRectangleProcessingTime = 0;
1149
1187
  lastRectangleDetectionTime = 0;
1150
1188
  rectangleEmitIntervalMs = 0;
1189
+ detectionThrottleMs = 0;
1190
+ lastSuccessfulBarcodeScanTime = 0;
1151
1191
 
1152
1192
  // Clear detection histories and caches
1153
1193
  enabledDetectionTypes.clear();
package/dist/docs.json CHANGED
@@ -344,7 +344,7 @@
344
344
  "docs": "",
345
345
  "types": [
346
346
  {
347
- "text": "{\n /**\n * Barcode formats to detect when barcodeDetection is enabled.\n */\n formats?: BarcodeFormat[];\n /**\n * Camera direction to use. Defaults to 'BACK'.\n */\n cameraDirection?: 'BACK' | 'FRONT';\n /**\n * Enable barcode detection on start. Defaults to false.\n * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events.\n */\n barcodeDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected barcodes. Defaults to false.\n * Only applies when barcodeDetection is true.\n */\n barcodeHighlight?: boolean;\n /**\n * Enable object detection (business card) on start. Defaults to false.\n * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events.\n */\n objectDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected business cards. Defaults to true.\n * Only applies when objectDetection is true.\n */\n objectHighlight?: boolean;\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * Only applies when objectDetection is true.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n rectangleEmitIntervalSeconds?: number;\n debounceTimeInMilli?: number;\n /**\n * Optional regex pattern to filter scanned barcodes.\n * If provided, only barcodes matching this pattern will be reported.\n */\n regex?: string;\n /**\n * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline).\n */\n regexFlags?: string;\n}",
347
+ "text": "{\n /**\n * Barcode formats to detect when barcodeDetection is enabled.\n */\n formats?: BarcodeFormat[];\n /**\n * Camera direction to use. Defaults to 'BACK'.\n */\n cameraDirection?: 'BACK' | 'FRONT';\n /**\n * Enable barcode detection on start. Defaults to false.\n * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events.\n */\n barcodeDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected barcodes. Defaults to false.\n * Only applies when barcodeDetection is true.\n */\n barcodeHighlight?: boolean;\n /**\n * Enable object detection (business card) on start. Defaults to false.\n * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events.\n */\n objectDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected business cards. Defaults to true.\n * Only applies when objectDetection is true.\n */\n objectHighlight?: boolean;\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * Only applies when objectDetection is true.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n rectangleEmitIntervalSeconds?: number;\n /**\n * Optional regex pattern to filter scanned barcodes.\n * If provided, only barcodes matching this pattern will be reported.\n */\n regex?: string;\n /**\n * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline).\n */\n regexFlags?: string;\n /**\n * Duration in seconds to pause rectangle detection after a barcode is successfully scanned.\n * When both barcode and object detection are enabled, this gives barcode scanning priority\n * by temporarily suppressing rectangle detection after a barcode is emitted.\n * Defaults to 0 (no throttle, both detections run simultaneously).\n */\n detectionThrottleSeconds?: number;\n}",
348
348
  "complexTypes": [
349
349
  "BarcodeFormat"
350
350
  ]
@@ -105,7 +105,6 @@ export declare type StartOptions = {
105
105
  * Defaults to 0 (no throttling beyond initial stability check).
106
106
  */
107
107
  rectangleEmitIntervalSeconds?: number;
108
- debounceTimeInMilli?: number;
109
108
  /**
110
109
  * Optional regex pattern to filter scanned barcodes.
111
110
  * If provided, only barcodes matching this pattern will be reported.
@@ -115,6 +114,13 @@ export declare type StartOptions = {
115
114
  * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline).
116
115
  */
117
116
  regexFlags?: string;
117
+ /**
118
+ * Duration in seconds to pause rectangle detection after a barcode is successfully scanned.
119
+ * When both barcode and object detection are enabled, this gives barcode scanning priority
120
+ * by temporarily suppressing rectangle detection after a barcode is emitted.
121
+ * Defaults to 0 (no throttle, both detections run simultaneously).
122
+ */
123
+ detectionThrottleSeconds?: number;
118
124
  };
119
125
  export declare type BarcodeScannedEvent = {
120
126
  scannedCode: string;
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAmKA,MAAM,CAAN,IAAY,aAYX;AAZD,WAAY,aAAa;IACvB,gCAAe,CAAA;IACf,mCAAkB,CAAA;IAClB,mCAAkB,CAAA;IAClB,qCAAoB,CAAA;IACpB,2CAA0B,CAAA;IAC1B,+BAAc,CAAA;IACd,iCAAgB,CAAA;IAChB,gCAAe,CAAA;IACf,mCAAkB,CAAA;IAClB,mCAAkB,CAAA;IAClB,+BAAc,CAAA;AAChB,CAAC,EAZW,aAAa,KAAb,aAAa,QAYxB","sourcesContent":["export interface CapacitorScannerPlugin {\n /**\n * Start the camera preview with optional barcode and object detection.\n * @param options - Configuration options for the camera and detection\n */\n start(options?: StartOptions): Promise<void>;\n\n /**\n * Stop the camera preview and all detection.\n */\n stop(): Promise<void>;\n\n openSettings(): Promise<void>;\n\n capturePhoto(options?: CapturePhotoOptions): Promise<CapturePhotoResult>;\n\n checkPermissions(): Promise<PermissionsResult>;\n\n requestPermissions(): Promise<PermissionsResult>;\n\n flipCamera(): Promise<void>;\n\n toggleFlash(): Promise<FlashResult>;\n\n addListener(event: 'barcodeScanned', listenerFunc: (result: BarcodeScannedEvent) => void): Promise<void>;\n\n addListener(event: 'rectangleDetected', listenerFunc: (result: RectangleDetectedEvent) => void): Promise<void>;\n\n removeAllListeners(): Promise<void>;\n\n /**\n * Enable object detection (business card). This will start detecting business cards.\n * @param options - Optional configuration for object detection\n */\n enableObjectDetection(options?: ObjectDetectionOptions): Promise<{ enabled: true }>;\n\n /**\n * Disable object detection. This will stop detecting business cards.\n */\n disableObjectDetection(): Promise<{ enabled: false }>;\n\n /**\n * Enable barcode detection. This will start detecting barcodes.\n * @param options - Optional configuration for barcode detection\n */\n enableBarcodeDetection(options?: BarcodeDetectionOptions): Promise<{ enabled: true }>;\n\n /**\n * Disable barcode detection. This will stop detecting barcodes and clear cached barcodes.\n */\n disableBarcodeDetection(): Promise<{ enabled: false }>;\n\n /**\n * Set the camera zoom level.\n * @param options - The zoom options containing the level (1, 2, or 3)\n */\n zoom(options: ZoomOptions): Promise<ZoomResult>;\n}\n\nexport type BarcodeDetectionOptions = {\n /**\n * Whether to show the highlight overlay for detected barcodes.\n * Defaults to false.\n */\n showHighlight?: boolean;\n}\n\nexport type ObjectDetectionOptions = {\n /**\n * Whether to show the highlight overlay for detected business cards.\n * Defaults to true.\n */\n showHighlight?: boolean,\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * After an event is emitted, no new events will be emitted until this interval passes.\n * Set to 0 to emit events as soon as stability is detected.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n emitIntervalSeconds?: number\n}\n\nexport type StartOptions = {\n /**\n * Barcode formats to detect when barcodeDetection is enabled.\n */\n formats?: BarcodeFormat[];\n /**\n * Camera direction to use. Defaults to 'BACK'.\n */\n cameraDirection?: 'BACK' | 'FRONT';\n /**\n * Enable barcode detection on start. Defaults to false.\n * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events.\n */\n barcodeDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected barcodes. Defaults to false.\n * Only applies when barcodeDetection is true.\n */\n barcodeHighlight?: boolean;\n /**\n * Enable object detection (business card) on start. Defaults to false.\n * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events.\n */\n objectDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected business cards. Defaults to true.\n * Only applies when objectDetection is true.\n */\n objectHighlight?: boolean;\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * Only applies when objectDetection is true.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n rectangleEmitIntervalSeconds?: number;\n debounceTimeInMilli?: number;\n /**\n * Optional regex pattern to filter scanned barcodes.\n * If provided, only barcodes matching this pattern will be reported.\n */\n regex?: string;\n /**\n * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline).\n */\n regexFlags?: string;\n};\n\nexport type BarcodeScannedEvent = { scannedCode: string; format: string };\n\n\nexport type RectangleDetectedEvent = {\n detected: true\n};\n\nexport type CapturePhotoOptions = {\n /**\n * The desired quality of the captured image, expressed as a value between 0.0 (lowest quality, smallest file size)\n * and 1.0 (highest quality, largest file size). Defaults to 1.0.\n * This parameter directly influences the compression level of the resulting JPEG image.\n */\n qualityRatio?: number;\n};\n\nexport type PermissionsResult = { camera: 'prompt' | 'denied' | 'granted' };\n\nexport type CapturePhotoResult = { imageBase64: string };\n\nexport type FlashResult = { enabled: boolean };\n\nexport type ZoomOptions = {\n /**\n * The zoom level to set. Must be 1, 2, or 3.\n * - 1 = 1.0x (no zoom)\n * - 2 = 2.0x\n * - 3 = 3.0x\n */\n level: 1 | 2 | 3;\n};\n\nexport type ZoomResult = { level: number };\n\nexport enum BarcodeFormat {\n Aztec = 'AZTEC',\n Code39 = 'CODE_39',\n Code93 = 'CODE_93',\n Code128 = 'CODE_128',\n DataMatrix = 'DATA_MATRIX',\n Ean8 = 'EAN_8',\n Ean13 = 'EAN_13',\n Itf14 = 'ITF14',\n Pdf417 = 'PDF_417',\n QrCode = 'QR_CODE',\n UpcE = 'UPC_E',\n}\n\ndeclare global {\n interface PluginRegistry {\n QRScanner: CapacitorScannerPlugin;\n }\n}"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAyKA,MAAM,CAAN,IAAY,aAYX;AAZD,WAAY,aAAa;IACvB,gCAAe,CAAA;IACf,mCAAkB,CAAA;IAClB,mCAAkB,CAAA;IAClB,qCAAoB,CAAA;IACpB,2CAA0B,CAAA;IAC1B,+BAAc,CAAA;IACd,iCAAgB,CAAA;IAChB,gCAAe,CAAA;IACf,mCAAkB,CAAA;IAClB,mCAAkB,CAAA;IAClB,+BAAc,CAAA;AAChB,CAAC,EAZW,aAAa,KAAb,aAAa,QAYxB","sourcesContent":["export interface CapacitorScannerPlugin {\n /**\n * Start the camera preview with optional barcode and object detection.\n * @param options - Configuration options for the camera and detection\n */\n start(options?: StartOptions): Promise<void>;\n\n /**\n * Stop the camera preview and all detection.\n */\n stop(): Promise<void>;\n\n openSettings(): Promise<void>;\n\n capturePhoto(options?: CapturePhotoOptions): Promise<CapturePhotoResult>;\n\n checkPermissions(): Promise<PermissionsResult>;\n\n requestPermissions(): Promise<PermissionsResult>;\n\n flipCamera(): Promise<void>;\n\n toggleFlash(): Promise<FlashResult>;\n\n addListener(event: 'barcodeScanned', listenerFunc: (result: BarcodeScannedEvent) => void): Promise<void>;\n\n addListener(event: 'rectangleDetected', listenerFunc: (result: RectangleDetectedEvent) => void): Promise<void>;\n\n removeAllListeners(): Promise<void>;\n\n /**\n * Enable object detection (business card). This will start detecting business cards.\n * @param options - Optional configuration for object detection\n */\n enableObjectDetection(options?: ObjectDetectionOptions): Promise<{ enabled: true }>;\n\n /**\n * Disable object detection. This will stop detecting business cards.\n */\n disableObjectDetection(): Promise<{ enabled: false }>;\n\n /**\n * Enable barcode detection. This will start detecting barcodes.\n * @param options - Optional configuration for barcode detection\n */\n enableBarcodeDetection(options?: BarcodeDetectionOptions): Promise<{ enabled: true }>;\n\n /**\n * Disable barcode detection. This will stop detecting barcodes and clear cached barcodes.\n */\n disableBarcodeDetection(): Promise<{ enabled: false }>;\n\n /**\n * Set the camera zoom level.\n * @param options - The zoom options containing the level (1, 2, or 3)\n */\n zoom(options: ZoomOptions): Promise<ZoomResult>;\n}\n\nexport type BarcodeDetectionOptions = {\n /**\n * Whether to show the highlight overlay for detected barcodes.\n * Defaults to false.\n */\n showHighlight?: boolean;\n}\n\nexport type ObjectDetectionOptions = {\n /**\n * Whether to show the highlight overlay for detected business cards.\n * Defaults to true.\n */\n showHighlight?: boolean,\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * After an event is emitted, no new events will be emitted until this interval passes.\n * Set to 0 to emit events as soon as stability is detected.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n emitIntervalSeconds?: number\n}\n\nexport type StartOptions = {\n /**\n * Barcode formats to detect when barcodeDetection is enabled.\n */\n formats?: BarcodeFormat[];\n /**\n * Camera direction to use. Defaults to 'BACK'.\n */\n cameraDirection?: 'BACK' | 'FRONT';\n /**\n * Enable barcode detection on start. Defaults to false.\n * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events.\n */\n barcodeDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected barcodes. Defaults to false.\n * Only applies when barcodeDetection is true.\n */\n barcodeHighlight?: boolean;\n /**\n * Enable object detection (business card) on start. Defaults to false.\n * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events.\n */\n objectDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected business cards. Defaults to true.\n * Only applies when objectDetection is true.\n */\n objectHighlight?: boolean;\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * Only applies when objectDetection is true.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n rectangleEmitIntervalSeconds?: number;\n /**\n * Optional regex pattern to filter scanned barcodes.\n * If provided, only barcodes matching this pattern will be reported.\n */\n regex?: string;\n /**\n * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline).\n */\n regexFlags?: string;\n /**\n * Duration in seconds to pause rectangle detection after a barcode is successfully scanned.\n * When both barcode and object detection are enabled, this gives barcode scanning priority\n * by temporarily suppressing rectangle detection after a barcode is emitted.\n * Defaults to 0 (no throttle, both detections run simultaneously).\n */\n detectionThrottleSeconds?: number;\n};\n\nexport type BarcodeScannedEvent = { scannedCode: string; format: string };\n\n\nexport type RectangleDetectedEvent = {\n detected: true\n};\n\nexport type CapturePhotoOptions = {\n /**\n * The desired quality of the captured image, expressed as a value between 0.0 (lowest quality, smallest file size)\n * and 1.0 (highest quality, largest file size). Defaults to 1.0.\n * This parameter directly influences the compression level of the resulting JPEG image.\n */\n qualityRatio?: number;\n};\n\nexport type PermissionsResult = { camera: 'prompt' | 'denied' | 'granted' };\n\nexport type CapturePhotoResult = { imageBase64: string };\n\nexport type FlashResult = { enabled: boolean };\n\nexport type ZoomOptions = {\n /**\n * The zoom level to set. Must be 1, 2, or 3.\n * - 1 = 1.0x (no zoom)\n * - 2 = 2.0x\n * - 3 = 3.0x\n */\n level: 1 | 2 | 3;\n};\n\nexport type ZoomResult = { level: number };\n\nexport enum BarcodeFormat {\n Aztec = 'AZTEC',\n Code39 = 'CODE_39',\n Code93 = 'CODE_93',\n Code128 = 'CODE_128',\n DataMatrix = 'DATA_MATRIX',\n Ean8 = 'EAN_8',\n Ean13 = 'EAN_13',\n Itf14 = 'ITF14',\n Pdf417 = 'PDF_417',\n QrCode = 'QR_CODE',\n UpcE = 'UPC_E',\n}\n\ndeclare global {\n interface PluginRegistry {\n QRScanner: CapacitorScannerPlugin;\n }\n}"]}
@@ -51,6 +51,10 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
51
51
  private var lastBarcodeDetectionTime: Date?
52
52
  private let barcodeVisibilityTimeout: TimeInterval = 1.0
53
53
 
54
+ // Detection throttle: pause rectangle detection after a successful barcode scan
55
+ private var detectionThrottleSeconds: TimeInterval = 0
56
+ private var lastSuccessfulBarcodeScanTime: Date?
57
+
54
58
  // Detection state (controls what detection runs)
55
59
  private var enabledDetectionTypes: Set<String> = []
56
60
  // Highlight state (controls what overlay is shown)
@@ -129,10 +133,8 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
129
133
  private func setupOverlayView() {
130
134
  guard let cameraView = self.cameraView,
131
135
  let previewLayer = self.previewLayer else {
132
- print("[CapacitorScanner] setupOverlayView: cameraView or previewLayer is nil")
133
136
  return
134
137
  }
135
- print("[CapacitorScanner] setupOverlayView: setting up overlay")
136
138
 
137
139
  // Remove existing overlay if present
138
140
  self.overlayView?.removeFromSuperview()
@@ -466,6 +468,10 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
466
468
  self.rectangleDetector?.setEmitInterval(emitInterval)
467
469
  }
468
470
 
471
+ // Read detection throttle
472
+ self.detectionThrottleSeconds = call.getDouble("detectionThrottleSeconds") ?? 0
473
+ self.lastSuccessfulBarcodeScanTime = nil
474
+
469
475
  // Handle Regex Filter
470
476
  if let pattern = call.getString("regex") {
471
477
  let flags = call.getString("regexFlags") ?? ""
@@ -598,6 +604,8 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
598
604
  self.enabledDetectionTypes.removeAll()
599
605
  self.enabledHighlightTypes.removeAll()
600
606
  self.lastBarcodeDetectionTime = nil
607
+ self.lastSuccessfulBarcodeScanTime = nil
608
+ self.detectionThrottleSeconds = 0
601
609
  self.removeOrientationChangeObserver()
602
610
  }
603
611
 
@@ -813,6 +821,9 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
813
821
  "format": CapacitorScannerHelpers.convertBarcodeScannerFormatToString(bestObservation.symbology),
814
822
  ])
815
823
 
824
+ // Record successful scan time for detection throttle
825
+ self.lastSuccessfulBarcodeScanTime = Date()
826
+
816
827
  // Reset votes after successful scan
817
828
  self.scannedCodesVotes.clear()
818
829
  }
@@ -825,7 +836,6 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
825
836
  // MARK: - RectangleDetectorDelegate
826
837
 
827
838
  func rectangleDetector(_ detector: RectangleDetector, didDetect corners: RectangleCorners) {
828
- print("[CapacitorScanner] rectangleDetector delegate called - emitting rectangleDetected event")
829
839
  self.notifyListeners("rectangleDetected", data: [
830
840
  "topLeft": ["x": corners.topLeft.x, "y": corners.topLeft.y],
831
841
  "topRight": ["x": corners.topRight.x, "y": corners.topRight.y],
@@ -1027,9 +1037,15 @@ extension CapacitorScannerPlugin: AVCaptureVideoDataOutputSampleBufferDelegate {
1027
1037
  requests.append(self.barcodeDetectionRequest)
1028
1038
  }
1029
1039
 
1030
- if self.enabledDetectionTypes.contains("businessCard"),
1040
+ let isRectangleThrottled: Bool = {
1041
+ guard self.detectionThrottleSeconds > 0,
1042
+ let lastScan = self.lastSuccessfulBarcodeScanTime else { return false }
1043
+ return Date().timeIntervalSince(lastScan) < self.detectionThrottleSeconds
1044
+ }()
1045
+
1046
+ if !isRectangleThrottled,
1047
+ self.enabledDetectionTypes.contains("businessCard"),
1031
1048
  let detector = self.rectangleDetector {
1032
- print("[CapacitorScanner] Processing frame for businessCard detection")
1033
1049
  detector.updatePixelBuffer(pixelBuffer)
1034
1050
  requests.append(detector.visionRequest)
1035
1051
  }
@@ -1041,15 +1057,19 @@ extension CapacitorScannerPlugin: AVCaptureVideoDataOutputSampleBufferDelegate {
1041
1057
  print("Failed to perform Vision request: \(error)")
1042
1058
  }
1043
1059
 
1044
- // Check overlay timeouts
1045
- DispatchQueue.main.async {
1046
- let now = Date()
1047
- if let t = self.lastBarcodeDetectionTime, now.timeIntervalSince(t) > self.barcodeVisibilityTimeout {
1048
- self.barcodeLayer?.removeFromSuperlayer()
1049
- self.barcodeLayer = nil
1050
- self.prevBarcodeRect = nil
1060
+ // Check overlay timeouts only when there's something to clean up
1061
+ let needsBarcodeCheck = self.barcodeLayer != nil
1062
+ let needsRectCheck = self.rectangleDetector != nil
1063
+ if needsBarcodeCheck || needsRectCheck {
1064
+ DispatchQueue.main.async {
1065
+ let now = Date()
1066
+ if let t = self.lastBarcodeDetectionTime, now.timeIntervalSince(t) > self.barcodeVisibilityTimeout {
1067
+ self.barcodeLayer?.removeFromSuperlayer()
1068
+ self.barcodeLayer = nil
1069
+ self.prevBarcodeRect = nil
1070
+ }
1071
+ self.rectangleDetector?.checkTimeout()
1051
1072
  }
1052
- self.rectangleDetector?.checkTimeout()
1053
1073
  }
1054
1074
  }
1055
1075
  }
@@ -41,6 +41,7 @@ class RectangleDetector {
41
41
  private(set) var lastDetectedRectangle: DetectedRectangle?
42
42
  private(set) var lastDetectionTime: Date?
43
43
  private var currentPixelBuffer: CVPixelBuffer?
44
+ private let ciContext = CIContext(options: nil)
44
45
 
45
46
  // Stability tracking
46
47
  private var previousCorners: RectangleCorners?
@@ -73,7 +74,6 @@ class RectangleDetector {
73
74
 
74
75
  /// Configure the detector with overlay support
75
76
  func configure(overlayLayer: CALayer, previewLayer: AVCaptureVideoPreviewLayer) {
76
- print("[RectangleDetector] configure called, isHighlightEnabled: \(isHighlightEnabled)")
77
77
  self.overlayParentLayer = overlayLayer
78
78
  self.previewLayer = previewLayer
79
79
 
@@ -132,41 +132,31 @@ class RectangleDetector {
132
132
 
133
133
  private func setupSmoother() {
134
134
  guard let parentLayer = overlayParentLayer, smoother == nil else {
135
- print("[RectangleDetector] setupSmoother: skipped (parentLayer nil: \(overlayParentLayer == nil), smoother exists: \(smoother != nil))")
136
135
  return
137
136
  }
138
- print("[RectangleDetector] setupSmoother: creating smoother")
139
137
  smoother = RectangleSmoother()
140
138
  smoother?.setup(in: parentLayer)
141
139
  }
142
140
 
143
141
  private func processDetection(_ request: VNRequest, error: Error?) {
144
- print("[RectangleDetector] processDetection called")
145
-
146
142
  if let error = error {
147
- print("[RectangleDetector] Detection error: \(error)")
148
143
  // Don't reset stability here - let checkTimeout() handle the grace period
149
144
  return
150
145
  }
151
146
 
152
147
  guard let observations = request.results as? [VNRectangleObservation] else {
153
- print("[RectangleDetector] No observations in results")
154
148
  // Don't reset stability here - let checkTimeout() handle the grace period
155
149
  return
156
150
  }
157
151
 
158
152
  // Find the rectangle with highest confidence
159
153
  guard let bestCandidate = observations.max(by: { $0.confidence < $1.confidence }) else {
160
- print("[RectangleDetector] No rectangles found (empty observations)")
161
154
  // Don't reset stability here - let checkTimeout() handle the grace period
162
155
  return
163
156
  }
164
157
 
165
- print("[RectangleDetector] Found rectangle with confidence: \(bestCandidate.confidence)")
166
-
167
158
  // Check if all corners are inside the screen with buffer
168
159
  guard isInsideScreen(bestCandidate) else {
169
- print("[RectangleDetector] Rectangle outside screen bounds")
170
160
  // Don't reset stability here - let checkTimeout() handle the grace period
171
161
  return
172
162
  }
@@ -204,11 +194,8 @@ class RectangleDetector {
204
194
  // MARK: - Stability Tracking
205
195
 
206
196
  private func updateStability(with corners: RectangleCorners) {
207
- print("[RectangleDetector] updateStability - hasPrevious: \(previousCorners != nil), timerActive: \(stabilityStartTime != nil)")
208
-
209
197
  // First detection - just store corners, don't start timer yet
210
198
  guard let previous = previousCorners else {
211
- print("[RectangleDetector] First detection - storing corners, no timer yet")
212
199
  previousCorners = corners
213
200
  stabilityStartTime = nil
214
201
  return
@@ -219,11 +206,9 @@ class RectangleDetector {
219
206
 
220
207
  // Check how much the rectangle moved
221
208
  let movement = maxCornerMovement(from: previous, to: corners)
222
- print("[RectangleDetector] Movement: \(movement), threshold: \(stabilityThreshold)")
223
209
 
224
210
  // Too much movement - reset timer completely
225
211
  if movement >= stabilityThreshold {
226
- print("[RectangleDetector] Too much movement (\(movement) >= \(stabilityThreshold)) - resetting timer")
227
212
  stabilityStartTime = nil
228
213
  return
229
214
  }
@@ -234,13 +219,11 @@ class RectangleDetector {
234
219
  if stabilityStartTime == nil {
235
220
  // Start the stability timer - but DON'T check duration yet
236
221
  stabilityStartTime = now
237
- print("[RectangleDetector] Stability timer STARTED")
238
222
  return
239
223
  }
240
224
 
241
225
  // Timer was already running - check elapsed time
242
226
  let elapsed = now.timeIntervalSince(stabilityStartTime!)
243
- print("[RectangleDetector] Stability elapsed: \(String(format: "%.2f", elapsed))s / \(stabilityDuration)s required")
244
227
 
245
228
  // Need to be stable for the required duration before any emission
246
229
  guard elapsed >= stabilityDuration else { return }
@@ -250,13 +233,11 @@ class RectangleDetector {
250
233
  if let lastEmit = lastEmitTime {
251
234
  let timeSinceLastEmit = now.timeIntervalSince(lastEmit)
252
235
  canEmit = timeSinceLastEmit >= emitIntervalSeconds
253
- print("[RectangleDetector] Time since last emit: \(String(format: "%.2f", timeSinceLastEmit))s, interval: \(emitIntervalSeconds)s")
254
236
  } else {
255
237
  canEmit = true
256
238
  }
257
239
 
258
240
  if canEmit {
259
- print("[RectangleDetector] STABLE for \(elapsed)s - emitting event")
260
241
  lastEmitTime = now
261
242
  delegate?.rectangleDetector(self, didDetect: corners)
262
243
  }
@@ -264,9 +245,6 @@ class RectangleDetector {
264
245
 
265
246
  /// Called when no valid rectangle is detected - resets all tracking state
266
247
  private func handleNoDetection() {
267
- if stabilityStartTime != nil {
268
- print("[RectangleDetector] No detection - resetting stability timer")
269
- }
270
248
  stabilityStartTime = nil
271
249
  previousCorners = nil
272
250
  }
@@ -311,9 +289,8 @@ class RectangleDetector {
311
289
  }
312
290
 
313
291
  let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
314
- let context = CIContext(options: nil)
315
292
 
316
- guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else {
293
+ guard let cgImage = ciContext.createCGImage(ciImage, from: ciImage.extent) else {
317
294
  lastDetectedRectangle = DetectedRectangle(observation: observation, snapshot: nil)
318
295
  return
319
296
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scr2em/capacitor-scanner",
3
- "version": "6.0.29",
3
+ "version": "6.0.31",
4
4
  "description": "scan codes",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",