@scr2em/capacitor-scanner 6.0.18 → 6.0.20

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 (17) hide show
  1. package/android/build.gradle +4 -2
  2. package/android/src/main/java/com/leadliaion/capacitorscanner/CapacitorScannerPlugin.java +226 -194
  3. package/package.json +2 -1
  4. package/android/src/main/libs/rectangle-detector/build.gradle +0 -73
  5. package/android/src/main/libs/rectangle-detector/consumer-rules.pro +0 -0
  6. package/android/src/main/libs/rectangle-detector/proguard-rules.pro +0 -21
  7. package/android/src/main/libs/rectangle-detector/src/main/AndroidManifest.xml +0 -2
  8. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/DetectionAccuracy.kt +0 -0
  9. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/RectangleDetector.kt +0 -0
  10. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/RectangleDetectorImpl.kt +0 -0
  11. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/dto/DetectionResult.kt +0 -0
  12. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/dto/Rectangle.kt +0 -0
  13. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/extension/BitmapExt.kt +0 -0
  14. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/strategy/AdaptiveThresholdStrategy.kt +0 -0
  15. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/strategy/CannyAlgorithmStrategy.kt +0 -0
  16. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/strategy/CompositeContourDetectionStrategy.kt +0 -0
  17. /package/android/src/main/{libs/rectangle-detector/src/main/java → java}/jp/co/smartbank/rectangledetector/strategy/ContourDetectionStrategy.kt +0 -0
@@ -26,7 +26,7 @@ buildscript {
26
26
  }
27
27
 
28
28
  apply plugin: 'com.android.library'
29
-
29
+ apply plugin: 'kotlin-android'
30
30
  android {
31
31
  namespace "com.leadliaion.capacitorscanner"
32
32
  compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
@@ -46,6 +46,9 @@ android {
46
46
  lintOptions {
47
47
  abortOnError false
48
48
  }
49
+ kotlinOptions {
50
+ jvmTarget = '17'
51
+ }
49
52
  compileOptions {
50
53
  sourceCompatibility JavaVersion.VERSION_17
51
54
  targetCompatibility JavaVersion.VERSION_17
@@ -76,7 +79,6 @@ dependencies {
76
79
 
77
80
  // Exposed as 'api' so consuming apps get them transitively
78
81
  api 'org.opencv:opencv:4.12.0'
79
- implementation files('libs/rectangle-detector-release.aar')
80
82
  api "androidx.camera:camera-core:1.5.1"
81
83
  api "androidx.camera:camera-camera2:1.5.1"
82
84
  api "androidx.camera:camera-lifecycle:1.5.1"
@@ -137,9 +137,11 @@ public class CapacitorScannerPlugin extends Plugin {
137
137
  private long lastRectangleProcessingTime = 0;
138
138
  private long lastRectangleDetectionTime = 0; // Tracks when we last had a valid rectangle detection
139
139
  private int processingFrequency = 10; // Default: 10 rectangle detections per second
140
-
140
+
141
141
  /**
142
- * Calculates the processing interval in milliseconds based on the desired frequency per second.
142
+ * Calculates the processing interval in milliseconds based on the desired
143
+ * frequency per second.
144
+ *
143
145
  * @param frequency Number of processing operations per second
144
146
  * @return Processing interval in milliseconds
145
147
  */
@@ -159,7 +161,8 @@ public class CapacitorScannerPlugin extends Plugin {
159
161
  .setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
160
162
  .build());
161
163
 
162
- // Initialize rectangle detector like the sample (use default constructor, not DetectionAccuracy.Passive)
164
+ // Initialize rectangle detector like the sample (use default constructor, not
165
+ // DetectionAccuracy.Passive)
163
166
  try {
164
167
  rectangleDetector = RectangleDetector.Companion.getInstance(DetectionAccuracy.Aggressive);
165
168
  echo("Rectangle detector initialized successfully");
@@ -298,7 +301,8 @@ public class CapacitorScannerPlugin extends Plugin {
298
301
 
299
302
  private Camera camera;
300
303
 
301
- private void bindCamera(@NonNull ProcessCameraProvider cameraProvider, PreviewView previewView, int lensFacing, PluginCall call) {
304
+ private void bindCamera(@NonNull ProcessCameraProvider cameraProvider, PreviewView previewView, int lensFacing,
305
+ PluginCall call) {
302
306
  cameraProvider.unbindAll();
303
307
 
304
308
  // Get preview view dimensions (like sample)
@@ -362,7 +366,8 @@ public class CapacitorScannerPlugin extends Plugin {
362
366
 
363
367
  try {
364
368
  // Store the Camera instance for later use
365
- camera = cameraProvider.bindToLifecycle(getActivity(), cameraSelector, preview, imageCapture, imageAnalysis);
369
+ camera = cameraProvider.bindToLifecycle(getActivity(), cameraSelector, preview, imageCapture,
370
+ imageAnalysis);
366
371
  preview.setSurfaceProvider(previewView.getSurfaceProvider());
367
372
  } catch (Exception e) {
368
373
  echo("Failed to bind camera to lifecycle: " + e.getMessage());
@@ -387,7 +392,8 @@ public class CapacitorScannerPlugin extends Plugin {
387
392
  @ExperimentalGetImage
388
393
  android.media.Image mediaImage = imageProxy.getImage();
389
394
  if (mediaImage != null) {
390
- InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
395
+ InputImage image = InputImage.fromMediaImage(mediaImage,
396
+ imageProxy.getImageInfo().getRotationDegrees());
391
397
 
392
398
  // Barcode detection
393
399
  if (scanner != null) {
@@ -395,7 +401,8 @@ public class CapacitorScannerPlugin extends Plugin {
395
401
  .addOnSuccessListener(executor, barcodes -> {
396
402
  processBarcodes(barcodes);
397
403
  if (enabledDetectionTypes.contains("barcode")) {
398
- displayDetectedBarcodes(barcodes, imageProxy.getWidth(), imageProxy.getHeight(), imageProxy.getImageInfo().getRotationDegrees());
404
+ displayDetectedBarcodes(barcodes, imageProxy.getWidth(), imageProxy.getHeight(),
405
+ imageProxy.getImageInfo().getRotationDegrees());
399
406
  }
400
407
  })
401
408
  .addOnFailureListener(executor, e -> {
@@ -419,14 +426,16 @@ public class CapacitorScannerPlugin extends Plugin {
419
426
  // Convert ImageProxy to Bitmap using the sample's toBitmap method
420
427
  bitmap = imageProxyToBitmap(imageProxy);
421
428
 
422
- echo("Converting image for rectangle detection - bitmap size: " + bitmap.getWidth() + "x" + bitmap.getHeight());
429
+ echo("Converting image for rectangle detection - bitmap size: " + bitmap.getWidth()
430
+ + "x" + bitmap.getHeight());
423
431
 
424
432
  long startTime = System.currentTimeMillis();
425
433
  DetectionResult rawResult = rectangleDetector.detectRectangles(bitmap);
426
434
  echo("Rectangle detection took " + (System.currentTimeMillis() - startTime) + "ms");
427
435
 
428
436
  // Apply geometric filtering to eliminate false positives
429
- List<Rectangle> filteredRectangles = filterRectangles(rawResult.getRectangles(), rawResult.getImageSize());
437
+ List<Rectangle> filteredRectangles = filterRectangles(rawResult.getRectangles(),
438
+ rawResult.getImageSize());
430
439
 
431
440
  // 2) record hit/miss in sliding window
432
441
  boolean isHit = !filteredRectangles.isEmpty();
@@ -435,7 +444,7 @@ public class CapacitorScannerPlugin extends Plugin {
435
444
  detectionHistory.poll();
436
445
  }
437
446
  if (isHit) {
438
- lastGoodRects = filteredRectangles; // update last known good
447
+ lastGoodRects = filteredRectangles; // update last known good
439
448
  echo("✅ Hit - saved " + filteredRectangles.size() + " good rectangles");
440
449
  } else {
441
450
  echo("❌ Miss - keeping " + lastGoodRects.size() + " previous rectangles");
@@ -448,12 +457,14 @@ public class CapacitorScannerPlugin extends Plugin {
448
457
  } else {
449
458
  // For older Android versions, count manually
450
459
  for (Boolean hit : detectionHistory) {
451
- if (hit) hits++;
460
+ if (hit)
461
+ hits++;
452
462
  }
453
463
  }
454
464
  boolean shouldShow = hits >= Math.ceil(PRESENCE_THRESHOLD * detectionHistory.size());
455
465
  List<Rectangle> rectanglesToShow = shouldShow ? lastGoodRects : Collections.emptyList();
456
- echo("Window: " + hits + "/" + detectionHistory.size() + " hits - shouldShow: " + shouldShow);
466
+ echo("Window: " + hits + "/" + detectionHistory.size() + " hits - shouldShow: "
467
+ + shouldShow);
457
468
 
458
469
  // Create detection result with the rectangles we decided to show
459
470
  detectionResult = new DetectionResult(rawResult.getImageSize(), rectanglesToShow);
@@ -464,7 +475,8 @@ public class CapacitorScannerPlugin extends Plugin {
464
475
  lastRectangleDetectionTime = currentTime;
465
476
  }
466
477
 
467
- // Process rectangle detection results to emit events if we're showing rectangles
478
+ // Process rectangle detection results to emit events if we're showing
479
+ // rectangles
468
480
  if (!rectanglesToShow.isEmpty()) {
469
481
  processRectangles(detectionResult);
470
482
  }
@@ -474,7 +486,8 @@ public class CapacitorScannerPlugin extends Plugin {
474
486
  } catch (Exception e) {
475
487
  echo("Failed to process image for rectangle detection: " + e.getMessage());
476
488
  e.printStackTrace();
477
- detectionResult = new DetectionResult(new Size(imageProxy.getWidth(), imageProxy.getHeight()), new ArrayList<>());
489
+ detectionResult = new DetectionResult(
490
+ new Size(imageProxy.getWidth(), imageProxy.getHeight()), new ArrayList<>());
478
491
  } finally {
479
492
  // Cleanup bitmap resource in all code paths
480
493
  if (bitmap != null) {
@@ -483,14 +496,17 @@ public class CapacitorScannerPlugin extends Plugin {
483
496
  }
484
497
  } else {
485
498
  // For frames we're not processing, reuse the last good detection result
486
- // This ensures the UI stays smooth even though we're only detecting 10 times/second
499
+ // This ensures the UI stays smooth even though we're only detecting 10
500
+ // times/second
487
501
  if (lastGoodDetectionResult != null &&
488
502
  (currentTime - lastRectangleDetectionTime) < CACHE_MS) {
489
503
  detectionResult = lastGoodDetectionResult;
490
- echo("Reusing last good detection result (age: " + (currentTime - lastRectangleProcessingTime) + "ms)");
504
+ echo("Reusing last good detection result (age: "
505
+ + (currentTime - lastRectangleProcessingTime) + "ms)");
491
506
  } else {
492
507
  // Create empty result if we have no recent good detection
493
- detectionResult = new DetectionResult(new Size(imageProxy.getWidth(), imageProxy.getHeight()), new ArrayList<>());
508
+ detectionResult = new DetectionResult(
509
+ new Size(imageProxy.getWidth(), imageProxy.getHeight()), new ArrayList<>());
494
510
  }
495
511
  }
496
512
 
@@ -524,8 +540,9 @@ public class CapacitorScannerPlugin extends Plugin {
524
540
  float imgWidth = imageSize.getWidth();
525
541
  float imgHeight = imageSize.getHeight();
526
542
  float imageArea = imgWidth * imgHeight;
527
-
528
- // Sort rectangles by quality score (highest first) to prioritize better detections
543
+
544
+ // Sort rectangles by quality score (highest first) to prioritize better
545
+ // detections
529
546
  List<Rectangle> sortedRectangles = new ArrayList<>(rectangles);
530
547
  Collections.sort(sortedRectangles, (r1, r2) -> {
531
548
  float score1 = calculateRectangleQualityScore(r1, imageSize);
@@ -540,31 +557,31 @@ public class CapacitorScannerPlugin extends Plugin {
540
557
  float areaRatio = rectArea / imageArea;
541
558
  float rectangularity = calculateRectangularity(rect);
542
559
  float aspectRatio = calculateAspectRatio(rect);
543
-
560
+
544
561
  // Skip if too small relative to image (increased from 0.01 to 0.03)
545
562
  if (areaRatio < 0.03f) {
546
563
  echo("Filtered out tiny rectangle: " + areaRatio);
547
564
  continue;
548
565
  }
549
-
566
+
550
567
  // Skip if too large (likely a false positive like the whole screen)
551
568
  if (areaRatio > 0.8f) {
552
569
  echo("Filtered out too large rectangle: " + areaRatio);
553
570
  continue;
554
571
  }
555
-
572
+
556
573
  // Filter based on rectangularity (how close to being a perfect rectangle)
557
574
  if (rectangularity < 0.8f) {
558
575
  echo("Filtered out non-rectangular shape: " + rectangularity);
559
576
  continue;
560
577
  }
561
-
578
+
562
579
  // Filter extreme aspect ratios (too narrow or too wide)
563
580
  if (aspectRatio < 0.2f || aspectRatio > 5.0f) {
564
581
  echo("Filtered out bad aspect ratio: " + aspectRatio);
565
582
  continue;
566
583
  }
567
-
584
+
568
585
  // Check if this rectangle is a duplicate of one we already kept
569
586
  boolean isDuplicate = false;
570
587
  for (Rectangle existingRect : filteredRectangles) {
@@ -574,147 +591,153 @@ public class CapacitorScannerPlugin extends Plugin {
574
591
  break;
575
592
  }
576
593
  }
577
-
594
+
578
595
  if (!isDuplicate) {
579
596
  filteredRectangles.add(rect);
580
597
  }
581
598
  }
582
-
599
+
583
600
  echo("Rectangle filtering: " + rectangles.size() + " → " + filteredRectangles.size() + " rectangles");
584
601
  return filteredRectangles;
585
602
  }
586
-
603
+
587
604
  // Calculate area of rectangle
588
605
  private float getRectangleArea(Rectangle rect) {
589
606
  // Use shoelace formula to calculate area of quadrilateral
590
607
  return 0.5f * Math.abs(
591
- (rect.getTopLeft().x * rect.getTopRight().y - rect.getTopRight().x * rect.getTopLeft().y) +
592
- (rect.getTopRight().x * rect.getBottomRight().y - rect.getBottomRight().x * rect.getTopRight().y) +
593
- (rect.getBottomRight().x * rect.getBottomLeft().y - rect.getBottomLeft().x * rect.getBottomRight().y) +
594
- (rect.getBottomLeft().x * rect.getTopLeft().y - rect.getTopLeft().x * rect.getBottomLeft().y)
595
- );
608
+ (rect.getTopLeft().x * rect.getTopRight().y - rect.getTopRight().x * rect.getTopLeft().y) +
609
+ (rect.getTopRight().x * rect.getBottomRight().y
610
+ - rect.getBottomRight().x * rect.getTopRight().y)
611
+ +
612
+ (rect.getBottomRight().x * rect.getBottomLeft().y
613
+ - rect.getBottomLeft().x * rect.getBottomRight().y)
614
+ +
615
+ (rect.getBottomLeft().x * rect.getTopLeft().y - rect.getTopLeft().x * rect.getBottomLeft().y));
596
616
  }
597
-
617
+
598
618
  // Check if two rectangles are similar enough to be considered duplicates
599
619
  private boolean isSimilarRectangle(Rectangle rect1, Rectangle rect2) {
600
620
  // Calculate center points
601
- float cx1 = (float) (rect1.getTopLeft().x + rect1.getTopRight().x + rect1.getBottomLeft().x + rect1.getBottomRight().x) / 4;
602
- float cy1 = (float) (rect1.getTopLeft().y + rect1.getTopRight().y + rect1.getBottomLeft().y + rect1.getBottomRight().y) / 4;
603
-
604
- float cx2 = (float) (rect2.getTopLeft().x + rect2.getTopRight().x + rect2.getBottomLeft().x + rect2.getBottomRight().x) / 4;
605
- float cy2 = (float) (rect2.getTopLeft().y + rect2.getTopRight().y + rect2.getBottomLeft().y + rect2.getBottomRight().y) / 4;
606
-
621
+ float cx1 = (float) (rect1.getTopLeft().x + rect1.getTopRight().x + rect1.getBottomLeft().x
622
+ + rect1.getBottomRight().x) / 4;
623
+ float cy1 = (float) (rect1.getTopLeft().y + rect1.getTopRight().y + rect1.getBottomLeft().y
624
+ + rect1.getBottomRight().y) / 4;
625
+
626
+ float cx2 = (float) (rect2.getTopLeft().x + rect2.getTopRight().x + rect2.getBottomLeft().x
627
+ + rect2.getBottomRight().x) / 4;
628
+ float cy2 = (float) (rect2.getTopLeft().y + rect2.getTopRight().y + rect2.getBottomLeft().y
629
+ + rect2.getBottomRight().y) / 4;
630
+
607
631
  // Calculate distances between centers
608
632
  float distanceBetweenCenters = (float) Math.sqrt(Math.pow(cx2 - cx1, 2) + Math.pow(cy2 - cy1, 2));
609
-
633
+
610
634
  // Calculate diagonal lengths as reference
611
635
  float diag1 = (float) Math.sqrt(
612
- Math.pow(rect1.getTopLeft().x - rect1.getBottomRight().x, 2) +
613
- Math.pow(rect1.getTopLeft().y - rect1.getBottomRight().y, 2)
614
- );
636
+ Math.pow(rect1.getTopLeft().x - rect1.getBottomRight().x, 2) +
637
+ Math.pow(rect1.getTopLeft().y - rect1.getBottomRight().y, 2));
615
638
  float diag2 = (float) Math.sqrt(
616
- Math.pow(rect2.getTopLeft().x - rect2.getBottomRight().x, 2) +
617
- Math.pow(rect2.getTopLeft().y - rect2.getBottomRight().y, 2)
618
- );
619
-
639
+ Math.pow(rect2.getTopLeft().x - rect2.getBottomRight().x, 2) +
640
+ Math.pow(rect2.getTopLeft().y - rect2.getBottomRight().y, 2));
641
+
620
642
  float avgDiag = (diag1 + diag2) / 2;
621
-
643
+
622
644
  // Make duplicate detection more strict (reduced from 0.15 to 0.1)
623
- // If centers are close relative to the rectangle size, they're likely duplicates
645
+ // If centers are close relative to the rectangle size, they're likely
646
+ // duplicates
624
647
  return distanceBetweenCenters < (0.1f * avgDiag);
625
648
  }
626
-
627
- // Calculate how rectangular a shape is (1.0 = perfect rectangle, lower = less rectangular)
649
+
650
+ // Calculate how rectangular a shape is (1.0 = perfect rectangle, lower = less
651
+ // rectangular)
628
652
  private float calculateRectangularity(Rectangle rect) {
629
- // Check angles between adjacent sides - should be close to 90 degrees in a rectangle
653
+ // Check angles between adjacent sides - should be close to 90 degrees in a
654
+ // rectangle
630
655
  android.graphics.Point topLeft = rect.getTopLeft();
631
656
  android.graphics.Point topRight = rect.getTopRight();
632
657
  android.graphics.Point bottomRight = rect.getBottomRight();
633
658
  android.graphics.Point bottomLeft = rect.getBottomLeft();
634
-
659
+
635
660
  // Calculate vectors for the four sides
636
- float[] side1x = {topRight.x - topLeft.x, topRight.y - topLeft.y};
637
- float[] side2x = {bottomRight.x - topRight.x, bottomRight.y - topRight.y};
638
- float[] side3x = {bottomLeft.x - bottomRight.x, bottomLeft.y - bottomRight.y};
639
- float[] side4x = {topLeft.x - bottomLeft.x, topLeft.y - bottomLeft.y};
640
-
661
+ float[] side1x = { topRight.x - topLeft.x, topRight.y - topLeft.y };
662
+ float[] side2x = { bottomRight.x - topRight.x, bottomRight.y - topRight.y };
663
+ float[] side3x = { bottomLeft.x - bottomRight.x, bottomLeft.y - bottomRight.y };
664
+ float[] side4x = { topLeft.x - bottomLeft.x, topLeft.y - bottomLeft.y };
665
+
641
666
  // Calculate angles between sides (dot product)
642
667
  float angle1 = calculateAngle(side1x, side2x);
643
668
  float angle2 = calculateAngle(side2x, side3x);
644
669
  float angle3 = calculateAngle(side3x, side4x);
645
670
  float angle4 = calculateAngle(side4x, side1x);
646
-
671
+
647
672
  // For a perfect rectangle, all angles would be 90 degrees (or PI/2 radians)
648
673
  // Calculate deviation from 90 degrees for each angle
649
- float dev1 = Math.abs(angle1 - (float)(Math.PI/2));
650
- float dev2 = Math.abs(angle2 - (float)(Math.PI/2));
651
- float dev3 = Math.abs(angle3 - (float)(Math.PI/2));
652
- float dev4 = Math.abs(angle4 - (float)(Math.PI/2));
653
-
674
+ float dev1 = Math.abs(angle1 - (float) (Math.PI / 2));
675
+ float dev2 = Math.abs(angle2 - (float) (Math.PI / 2));
676
+ float dev3 = Math.abs(angle3 - (float) (Math.PI / 2));
677
+ float dev4 = Math.abs(angle4 - (float) (Math.PI / 2));
678
+
654
679
  // Calculate average deviation, normalized to 0-1 range
655
680
  float avgDev = (dev1 + dev2 + dev3 + dev4) / 4;
656
-
681
+
657
682
  // 1.0 = perfect rectangle, 0 = not rectangular at all
658
- return Math.max(0, 1.0f - (avgDev / (float)(Math.PI/4)));
683
+ return Math.max(0, 1.0f - (avgDev / (float) (Math.PI / 4)));
659
684
  }
660
-
685
+
661
686
  // Calculate angle between two vectors in radians
662
687
  private float calculateAngle(float[] v1, float[] v2) {
663
688
  // Calculate dot product
664
689
  float dotProduct = v1[0] * v2[0] + v1[1] * v2[1];
665
-
690
+
666
691
  // Calculate magnitudes
667
- float mag1 = (float)Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1]);
668
- float mag2 = (float)Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1]);
669
-
692
+ float mag1 = (float) Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1]);
693
+ float mag2 = (float) Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1]);
694
+
670
695
  // Avoid division by zero
671
- if (mag1 == 0 || mag2 == 0) return 0;
672
-
696
+ if (mag1 == 0 || mag2 == 0)
697
+ return 0;
698
+
673
699
  // Calculate angle
674
700
  float cosAngle = dotProduct / (mag1 * mag2);
675
-
701
+
676
702
  // Ensure value is in [-1,1] range to avoid domain errors with acos
677
703
  cosAngle = Math.max(-1.0f, Math.min(1.0f, cosAngle));
678
-
679
- return (float)Math.acos(cosAngle);
704
+
705
+ return (float) Math.acos(cosAngle);
680
706
  }
681
-
707
+
682
708
  // Calculate aspect ratio (width/height) using the average of opposite sides
683
709
  private float calculateAspectRatio(Rectangle rect) {
684
710
  android.graphics.Point topLeft = rect.getTopLeft();
685
711
  android.graphics.Point topRight = rect.getTopRight();
686
712
  android.graphics.Point bottomRight = rect.getBottomRight();
687
713
  android.graphics.Point bottomLeft = rect.getBottomLeft();
688
-
689
- float topWidth = (float)Math.sqrt(
690
- Math.pow(topRight.x - topLeft.x, 2) +
691
- Math.pow(topRight.y - topLeft.y, 2)
692
- );
693
-
694
- float bottomWidth = (float)Math.sqrt(
695
- Math.pow(bottomRight.x - bottomLeft.x, 2) +
696
- Math.pow(bottomRight.y - bottomLeft.y, 2)
697
- );
698
-
699
- float leftHeight = (float)Math.sqrt(
700
- Math.pow(bottomLeft.x - topLeft.x, 2) +
701
- Math.pow(bottomLeft.y - topLeft.y, 2)
702
- );
703
-
704
- float rightHeight = (float)Math.sqrt(
705
- Math.pow(bottomRight.x - topRight.x, 2) +
706
- Math.pow(bottomRight.y - topRight.y, 2)
707
- );
708
-
714
+
715
+ float topWidth = (float) Math.sqrt(
716
+ Math.pow(topRight.x - topLeft.x, 2) +
717
+ Math.pow(topRight.y - topLeft.y, 2));
718
+
719
+ float bottomWidth = (float) Math.sqrt(
720
+ Math.pow(bottomRight.x - bottomLeft.x, 2) +
721
+ Math.pow(bottomRight.y - bottomLeft.y, 2));
722
+
723
+ float leftHeight = (float) Math.sqrt(
724
+ Math.pow(bottomLeft.x - topLeft.x, 2) +
725
+ Math.pow(bottomLeft.y - topLeft.y, 2));
726
+
727
+ float rightHeight = (float) Math.sqrt(
728
+ Math.pow(bottomRight.x - topRight.x, 2) +
729
+ Math.pow(bottomRight.y - topRight.y, 2));
730
+
709
731
  float avgWidth = (topWidth + bottomWidth) / 2;
710
732
  float avgHeight = (leftHeight + rightHeight) / 2;
711
-
733
+
712
734
  // Avoid division by zero
713
- if (avgHeight == 0) return 0;
714
-
735
+ if (avgHeight == 0)
736
+ return 0;
737
+
715
738
  return avgWidth / avgHeight;
716
739
  }
717
-
740
+
718
741
  // Calculate a quality score for the rectangle (higher = better)
719
742
  private float calculateRectangleQualityScore(Rectangle rect, Size imageSize) {
720
743
  float rectangularity = calculateRectangularity(rect);
@@ -722,19 +745,21 @@ public class CapacitorScannerPlugin extends Plugin {
722
745
  float area = getRectangleArea(rect);
723
746
  float imageArea = imageSize.getWidth() * imageSize.getHeight();
724
747
  float areaRatio = area / imageArea;
725
-
726
- // Ideal aspect ratio score (1.0 = golden ratio, decreases as we move away from it)
748
+
749
+ // Ideal aspect ratio score (1.0 = golden ratio, decreases as we move away from
750
+ // it)
727
751
  float idealAspect = 1.6f; // Close to golden ratio
728
752
  float aspectScore = 1.0f - Math.min(1.0f, Math.abs(aspectRatio - idealAspect) / 2.0f);
729
-
730
- // Ideal area score (0 for tiny or huge rectangles, 1.0 for rectangles that take up reasonable portion)
753
+
754
+ // Ideal area score (0 for tiny or huge rectangles, 1.0 for rectangles that take
755
+ // up reasonable portion)
731
756
  float areaScore = 0;
732
757
  if (areaRatio > 0.05f && areaRatio < 0.7f) {
733
758
  // Parabolic function that peaks at around 0.3 of the image area
734
759
  areaScore = 1.0f - 4.0f * (areaRatio - 0.3f) * (areaRatio - 0.3f);
735
760
  areaScore = Math.max(0, areaScore);
736
761
  }
737
-
762
+
738
763
  // Weighted combination of factors
739
764
  return (0.5f * rectangularity) + (0.3f * aspectScore) + (0.2f * areaScore);
740
765
  }
@@ -771,7 +796,8 @@ public class CapacitorScannerPlugin extends Plugin {
771
796
  data.put("scannedCode", rawValue);
772
797
  data.put("format", BarcodeFormatHelper.barcodeFormatToString(format));
773
798
  notifyListeners("barcodeScanned", data, true);
774
- echo("Barcode " + rawValue + " scanned with format: " + BarcodeFormatHelper.barcodeFormatToString(format));
799
+ echo("Barcode " + rawValue + " scanned with format: "
800
+ + BarcodeFormatHelper.barcodeFormatToString(format));
775
801
  }
776
802
  }
777
803
  }
@@ -792,7 +818,7 @@ public class CapacitorScannerPlugin extends Plugin {
792
818
  // Find the largest rectangle if multiple are detected
793
819
  Rectangle largestRectangle = null;
794
820
  float largestArea = 0;
795
-
821
+
796
822
  for (Rectangle rectangle : rectangles) {
797
823
  float area = getRectangleArea(rectangle);
798
824
  if (area > largestArea) {
@@ -800,13 +826,13 @@ public class CapacitorScannerPlugin extends Plugin {
800
826
  largestRectangle = rectangle;
801
827
  }
802
828
  }
803
-
829
+
804
830
  // Emit event with detected: true for the largest rectangle
805
831
  JSObject data = new JSObject();
806
832
  data.put("detected", true);
807
833
  echo("Emitting rectangleDetected event with {detected: true} at " + currentTime);
808
834
  notifyListeners("rectangleDetected", data, true);
809
-
835
+
810
836
  // Update the last notification timestamp
811
837
  lastRectangleNotificationTime = currentTime;
812
838
  } else {
@@ -852,13 +878,13 @@ public class CapacitorScannerPlugin extends Plugin {
852
878
  for (DetectionResult result : recentDetectionResults) {
853
879
  if (result != null && !result.getRectangles().isEmpty()) {
854
880
  resultsWithRectangles++;
855
-
881
+
856
882
  // Calculate quality score for this detection result
857
883
  float qualityScore = 0;
858
884
  for (Rectangle rect : result.getRectangles()) {
859
885
  qualityScore += calculateRectangleQualityScore(rect, result.getImageSize());
860
886
  }
861
-
887
+
862
888
  // Factor in both number of rectangles and their quality
863
889
  if (qualityScore > bestQualityScore) {
864
890
  bestQualityScore = qualityScore;
@@ -945,8 +971,10 @@ public class CapacitorScannerPlugin extends Plugin {
945
971
  RectF srcRect = new RectF(0, 0, imageAnalysisWidth, imageAnalysisHeight);
946
972
  RectF dstRect = new RectF(0, 0, viewWidth, viewHeight);
947
973
 
948
- // Configure the matrix to scale and translate the source rectangle to fill the destination,
949
- // preserving aspect ratio. This is the equivalent of PreviewView's ScaleType.FILL_CENTER.
974
+ // Configure the matrix to scale and translate the source rectangle to fill the
975
+ // destination,
976
+ // preserving aspect ratio. This is the equivalent of PreviewView's
977
+ // ScaleType.FILL_CENTER.
950
978
  matrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.FILL);
951
979
 
952
980
  // Apply the transformation to the barcode's bounding box
@@ -1028,21 +1056,21 @@ public class CapacitorScannerPlugin extends Plugin {
1028
1056
  lastRectangleNotificationTime = 0;
1029
1057
  lastRectangleProcessingTime = 0;
1030
1058
  lastRectangleDetectionTime = 0;
1031
-
1059
+
1032
1060
  // Clear detection histories and caches
1033
1061
  enabledDetectionTypes.clear();
1034
1062
  recentDetectionResults.clear();
1035
1063
  detectionHistory.clear();
1036
1064
  lastGoodRects = Collections.emptyList();
1037
-
1065
+
1038
1066
  // Clear overlay rectangles
1039
1067
  synchronized (overlayLock) {
1040
1068
  currentBarcodeRects.clear();
1041
1069
  currentRectangleRects.clear();
1042
1070
  }
1043
-
1071
+
1044
1072
  echo("All detection state variables have been reset");
1045
-
1073
+
1046
1074
  call.resolve();
1047
1075
  });
1048
1076
  }
@@ -1062,7 +1090,7 @@ public class CapacitorScannerPlugin extends Plugin {
1062
1090
  lastGoodRects = Collections.emptyList();
1063
1091
  lastGoodRectangleTime = 0;
1064
1092
  lastGoodDetectionResult = null;
1065
-
1093
+
1066
1094
  // Clear previous detection overlays
1067
1095
  if (detectionOverlay != null) {
1068
1096
  getActivity().runOnUiThread(() -> {
@@ -1092,10 +1120,11 @@ public class CapacitorScannerPlugin extends Plugin {
1092
1120
  // Get value as double and convert to float
1093
1121
  double paddingValue = call.getData().getDouble("paddingRatio");
1094
1122
  float paddingRatioFloat = (float) paddingValue;
1095
-
1123
+
1096
1124
  // Validate that paddingRatio is between 0 and 1
1097
1125
  if (paddingRatioFloat < 0 || paddingRatioFloat > 1) {
1098
- echo("Invalid paddingRatio value: " + paddingRatioFloat + " - must be between 0 and 1. Using default 0.01f");
1126
+ echo("Invalid paddingRatio value: " + paddingRatioFloat
1127
+ + " - must be between 0 and 1. Using default 0.01f");
1099
1128
  paddingRatio = 0.01f;
1100
1129
  } else {
1101
1130
  paddingRatio = paddingRatioFloat;
@@ -1119,7 +1148,9 @@ public class CapacitorScannerPlugin extends Plugin {
1119
1148
  }
1120
1149
 
1121
1150
  if (enabledDetectionTypes.contains("barcode") || enabledDetectionTypes.contains("businessCard")) {
1122
- setupDetectionOverlay();
1151
+ getActivity().runOnUiThread(() -> {
1152
+ setupDetectionOverlay();
1153
+ });
1123
1154
  }
1124
1155
 
1125
1156
  call.resolve(result);
@@ -1173,7 +1204,8 @@ public class CapacitorScannerPlugin extends Plugin {
1173
1204
 
1174
1205
  // Decode the image into a Bitmap
1175
1206
  options.inJustDecodeBounds = false;
1176
- Bitmap originalBitmap = BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length, options);
1207
+ Bitmap originalBitmap = BitmapFactory.decodeByteArray(originalBytes, 0,
1208
+ originalBytes.length, options);
1177
1209
  if (originalBitmap == null) {
1178
1210
  call.reject("Failed to decode captured image.");
1179
1211
  return;
@@ -1189,13 +1221,12 @@ public class CapacitorScannerPlugin extends Plugin {
1189
1221
  // Scale the overlay rect from preview coordinates to bitmap coordinates
1190
1222
  float scaleX = (float) originalBitmap.getWidth() / previewView.getWidth();
1191
1223
  float scaleY = (float) originalBitmap.getHeight() / previewView.getHeight();
1192
-
1224
+
1193
1225
  RectF scaledRectF = new RectF(
1194
- largestRectF.left * scaleX,
1195
- largestRectF.top * scaleY,
1196
- largestRectF.right * scaleX,
1197
- largestRectF.bottom * scaleY
1198
- );
1226
+ largestRectF.left * scaleX,
1227
+ largestRectF.top * scaleY,
1228
+ largestRectF.right * scaleX,
1229
+ largestRectF.bottom * scaleY);
1199
1230
 
1200
1231
  // **FIX**: Un-mirror the coordinates for cropping if using the front camera
1201
1232
  if (currentLensFacing == CameraSelector.LENS_FACING_FRONT) {
@@ -1204,35 +1235,34 @@ public class CapacitorScannerPlugin extends Plugin {
1204
1235
  scaledRectF.left = bitmapWidth - scaledRectF.right;
1205
1236
  scaledRectF.right = bitmapWidth - left;
1206
1237
  }
1207
-
1238
+
1208
1239
  // Calculate 1% padding based on the full image dimensions
1209
1240
  float paddingX = originalBitmap.getWidth() * paddingRatio;
1210
1241
  float paddingY = originalBitmap.getHeight() * paddingRatio;
1211
-
1242
+
1212
1243
  // Apply 2% padding to the rectangle while keeping it within image bounds
1213
1244
  RectF paddedRectF = new RectF(
1214
- Math.max(0, scaledRectF.left - paddingX),
1215
- Math.max(0, scaledRectF.top - paddingY),
1216
- Math.min(originalBitmap.getWidth(), scaledRectF.right + paddingX),
1217
- Math.min(originalBitmap.getHeight(), scaledRectF.bottom + paddingY)
1218
- );
1219
-
1245
+ Math.max(0, scaledRectF.left - paddingX),
1246
+ Math.max(0, scaledRectF.top - paddingY),
1247
+ Math.min(originalBitmap.getWidth(), scaledRectF.right + paddingX),
1248
+ Math.min(originalBitmap.getHeight(), scaledRectF.bottom + paddingY));
1249
+
1220
1250
  // Convert scaled RectF to Rect (for cropping)
1221
1251
  Rect largestRect = new Rect(
1222
1252
  Math.round(paddedRectF.left),
1223
1253
  Math.round(paddedRectF.top),
1224
1254
  Math.round(paddedRectF.right),
1225
- Math.round(paddedRectF.bottom)
1226
- );
1255
+ Math.round(paddedRectF.bottom));
1227
1256
 
1228
1257
  // Log dimensions for debugging
1229
- echo("📏 Cropping - Preview: " + previewView.getWidth() + "x" + previewView.getHeight() +
1230
- ", Bitmap: " + originalBitmap.getWidth() + "x" + originalBitmap.getHeight() +
1231
- ", Scale: " + scaleX + "x" + scaleY);
1232
- echo("🔍 Original Rect: " + largestRectF.toString() +
1233
- " Scaled Rect: " + scaledRectF.toString() +
1234
- " → Padded Rect: " + paddedRectF.toString());
1235
-
1258
+ echo("📏 Cropping - Preview: " + previewView.getWidth() + "x" + previewView.getHeight()
1259
+ +
1260
+ ", Bitmap: " + originalBitmap.getWidth() + "x" + originalBitmap.getHeight() +
1261
+ ", Scale: " + scaleX + "x" + scaleY);
1262
+ echo("🔍 Original Rect: " + largestRectF.toString() +
1263
+ " → Scaled Rect: " + scaledRectF.toString() +
1264
+ " → Padded Rect: " + paddedRectF.toString());
1265
+
1236
1266
  // Crop the image based on the largest rectangle
1237
1267
  processedBitmap = cropBitmap(originalBitmap, largestRect);
1238
1268
  } else {
@@ -1303,12 +1333,13 @@ public class CapacitorScannerPlugin extends Plugin {
1303
1333
  }
1304
1334
 
1305
1335
  // **SAMPLE METHOD**: Rotate bitmap if needed (exact sample logic)
1306
- // Adjust the image's rotation based on its EXIF orientation
1336
+ // Adjust the image's rotation based on its EXIF orientation
1307
1337
  private Bitmap rotateBitmap(Bitmap bitmap, int rotationDegrees) {
1308
1338
  if (rotationDegrees != 0) {
1309
1339
  Matrix matrix = new Matrix();
1310
1340
  matrix.postRotate(rotationDegrees);
1311
- Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
1341
+ Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix,
1342
+ true);
1312
1343
  if (rotatedBitmap != bitmap) {
1313
1344
  bitmap.recycle(); // Recycle original bitmap if a new one was created
1314
1345
  }
@@ -1495,7 +1526,8 @@ public class CapacitorScannerPlugin extends Plugin {
1495
1526
  case ImageFormat.JPEG -> toBitmapFromJpegFormatImage(imageProxy);
1496
1527
  case ImageFormat.YUV_420_888 -> toBitmapFromYUVFormatImage(imageProxy);
1497
1528
  default ->
1498
- throw new UnsupportedOperationException("ImageProxy has unsupported image format: " + imageProxy.getFormat());
1529
+ throw new UnsupportedOperationException(
1530
+ "ImageProxy has unsupported image format: " + imageProxy.getFormat());
1499
1531
  };
1500
1532
  }
1501
1533
 
@@ -1506,13 +1538,14 @@ public class CapacitorScannerPlugin extends Plugin {
1506
1538
  byte[] bytes = new byte[buffer.remaining()];
1507
1539
  buffer.get(bytes);
1508
1540
  Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
1509
- return rotateBitmap(cropBitmap(bitmap, imageProxy.getCropRect()), imageProxy.getImageInfo().getRotationDegrees());
1541
+ return rotateBitmap(cropBitmap(bitmap, imageProxy.getCropRect()),
1542
+ imageProxy.getImageInfo().getRotationDegrees());
1510
1543
  }
1511
1544
 
1512
1545
  // **SAMPLE METHOD**: Handle YUV format images (exact sample logic)
1513
1546
  private Bitmap toBitmapFromYUVFormatImage(ImageProxy imageProxy) {
1514
1547
  byte[] nv21 = toNV21ByteArray(imageProxy);
1515
- YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21,
1548
+ YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21,
1516
1549
  imageProxy.getCropRect().width(), imageProxy.getCropRect().height(), null);
1517
1550
  ByteArrayOutputStream out = new ByteArrayOutputStream();
1518
1551
  yuvImage.compressToJpeg(new Rect(0, 0, yuvImage.getWidth(), yuvImage.getHeight()), 80, out);
@@ -1528,13 +1561,13 @@ public class CapacitorScannerPlugin extends Plugin {
1528
1561
  int ySize = cropRect.width() * cropRect.height();
1529
1562
  int uvSize = cropRect.width() * cropRect.height() / 4;
1530
1563
  byte[] nv21 = new byte[ySize + uvSize * 2];
1531
-
1564
+
1532
1565
  ImageProxy.PlaneProxy[] planes = imageProxy.getPlanes();
1533
1566
  for (int planeIndex = 0; planeIndex < planes.length; planeIndex++) {
1534
1567
  ImageProxy.PlaneProxy plane = planes[planeIndex];
1535
1568
  int outputStride;
1536
1569
  int outputOffset;
1537
-
1570
+
1538
1571
  switch (planeIndex) {
1539
1572
  case 0:
1540
1573
  outputStride = 1;
@@ -1551,24 +1584,23 @@ public class CapacitorScannerPlugin extends Plugin {
1551
1584
  default:
1552
1585
  continue;
1553
1586
  }
1554
-
1587
+
1555
1588
  ByteBuffer planeBuffer = plane.getBuffer();
1556
1589
  int rowStride = plane.getRowStride();
1557
1590
  int pixelStride = plane.getPixelStride();
1558
-
1559
- Rect planeCrop = (planeIndex == 0) ? cropRect :
1560
- new Rect(cropRect.left / 2, cropRect.top / 2, cropRect.right / 2, cropRect.bottom / 2);
1561
-
1591
+
1592
+ Rect planeCrop = (planeIndex == 0) ? cropRect
1593
+ : new Rect(cropRect.left / 2, cropRect.top / 2, cropRect.right / 2, cropRect.bottom / 2);
1594
+
1562
1595
  int planeWidth = planeCrop.width();
1563
1596
  int planeHeight = planeCrop.height();
1564
1597
  byte[] rowBuffer = new byte[plane.getRowStride()];
1565
-
1566
- int rowLength = (pixelStride == 1 && outputStride == 1) ? planeWidth :
1567
- (planeWidth - 1) * pixelStride + 1;
1568
-
1598
+
1599
+ int rowLength = (pixelStride == 1 && outputStride == 1) ? planeWidth : (planeWidth - 1) * pixelStride + 1;
1600
+
1569
1601
  for (int row = 0; row < planeHeight; row++) {
1570
1602
  planeBuffer.position((row + planeCrop.top) * rowStride + planeCrop.left * pixelStride);
1571
-
1603
+
1572
1604
  if (pixelStride == 1 && outputStride == 1) {
1573
1605
  planeBuffer.get(nv21, outputOffset, rowLength);
1574
1606
  outputOffset += rowLength;
@@ -1581,7 +1613,7 @@ public class CapacitorScannerPlugin extends Plugin {
1581
1613
  }
1582
1614
  }
1583
1615
  }
1584
-
1616
+
1585
1617
  return nv21;
1586
1618
  }
1587
1619
 
@@ -1599,7 +1631,7 @@ public class CapacitorScannerPlugin extends Plugin {
1599
1631
  case ExifInterface.ORIENTATION_ROTATE_270:
1600
1632
  return 270;
1601
1633
  default:
1602
- return 0; // No rotation
1634
+ return 0; // No rotation
1603
1635
  }
1604
1636
  } catch (IOException e) {
1605
1637
  e.printStackTrace();
@@ -1607,7 +1639,8 @@ public class CapacitorScannerPlugin extends Plugin {
1607
1639
  return 0;
1608
1640
  }
1609
1641
 
1610
- private void displayDetectedRectangles(DetectionResult detectionResult, int imageWidth, int imageHeight, int rotationDegrees) {
1642
+ private void displayDetectedRectangles(DetectionResult detectionResult, int imageWidth, int imageHeight,
1643
+ int rotationDegrees) {
1611
1644
  if (detectionOverlay == null || previewView == null) {
1612
1645
  return;
1613
1646
  }
@@ -1623,50 +1656,47 @@ public class CapacitorScannerPlugin extends Plugin {
1623
1656
 
1624
1657
  echo("🔄 Sample coordinate transformation - Preview: " + previewWidth + "x" + previewHeight +
1625
1658
  ", Image: " + imageWidth + "x" + imageHeight + ", Rotation: " + rotationDegrees);
1626
-
1659
+
1627
1660
  // Create preview size from actual preview dimensions
1628
1661
  Size previewSize = new Size(previewWidth, previewHeight);
1629
-
1662
+
1630
1663
  // Transform rectangles using sample's exact logic
1631
1664
  RectF previewRectInImage = previewRectInImage(detectionResult.getImageSize(), previewSize);
1632
1665
  List<Rectangle> transformedRectangles = new ArrayList<>();
1633
-
1666
+
1634
1667
  for (Rectangle rectangle : detectionResult.getRectangles()) {
1635
1668
  // Use separate scale ratios for width and height to fix vertical shifting
1636
1669
  float scaleRatioWidth = previewSize.getWidth() / previewRectInImage.width();
1637
1670
  float scaleRatioHeight = previewSize.getHeight() / previewRectInImage.height();
1638
-
1671
+
1639
1672
  // Use the correct scale ratio for each dimension
1640
1673
  android.graphics.Point transformedTopLeft = new android.graphics.Point(
1641
1674
  Math.round((rectangle.getTopLeft().x - previewRectInImage.left) * scaleRatioWidth),
1642
- Math.round((rectangle.getTopLeft().y - previewRectInImage.top) * scaleRatioHeight)
1643
- );
1675
+ Math.round((rectangle.getTopLeft().y - previewRectInImage.top) * scaleRatioHeight));
1644
1676
  android.graphics.Point transformedTopRight = new android.graphics.Point(
1645
1677
  Math.round((rectangle.getTopRight().x - previewRectInImage.left) * scaleRatioWidth),
1646
- Math.round((rectangle.getTopRight().y - previewRectInImage.top) * scaleRatioHeight)
1647
- );
1678
+ Math.round((rectangle.getTopRight().y - previewRectInImage.top) * scaleRatioHeight));
1648
1679
  android.graphics.Point transformedBottomLeft = new android.graphics.Point(
1649
1680
  Math.round((rectangle.getBottomLeft().x - previewRectInImage.left) * scaleRatioWidth),
1650
- Math.round((rectangle.getBottomLeft().y - previewRectInImage.top) * scaleRatioHeight)
1651
- );
1681
+ Math.round((rectangle.getBottomLeft().y - previewRectInImage.top) * scaleRatioHeight));
1652
1682
  android.graphics.Point transformedBottomRight = new android.graphics.Point(
1653
1683
  Math.round((rectangle.getBottomRight().x - previewRectInImage.left) * scaleRatioWidth),
1654
- Math.round((rectangle.getBottomRight().y - previewRectInImage.top) * scaleRatioHeight)
1655
- );
1656
-
1657
- transformedRectangles.add(new Rectangle(transformedTopLeft, transformedTopRight,
1684
+ Math.round((rectangle.getBottomRight().y - previewRectInImage.top) * scaleRatioHeight));
1685
+
1686
+ transformedRectangles.add(new Rectangle(transformedTopLeft, transformedTopRight,
1658
1687
  transformedBottomLeft, transformedBottomRight));
1659
1688
  }
1660
-
1689
+
1661
1690
  final List<RectF> overlayRects = new ArrayList<>();
1662
1691
  for (Rectangle rect : transformedRectangles) {
1663
1692
  // Create bounding box from transformed corner points
1664
1693
  RectF transformedRect = getRectF(rect);
1665
1694
  overlayRects.add(transformedRect);
1666
-
1667
- echo("✅ Sample transformation: " +
1668
- "Original corners: (" + detectionResult.getRectangles().get(overlayRects.size()-1).getTopLeft().x + "," +
1669
- detectionResult.getRectangles().get(overlayRects.size()-1).getTopLeft().y + ") → " +
1695
+
1696
+ echo("✅ Sample transformation: " +
1697
+ "Original corners: (" + detectionResult.getRectangles().get(overlayRects.size() - 1).getTopLeft().x
1698
+ + "," +
1699
+ detectionResult.getRectangles().get(overlayRects.size() - 1).getTopLeft().y + ") → " +
1670
1700
  "Preview rect: " + transformedRect.toString());
1671
1701
  }
1672
1702
 
@@ -1679,17 +1709,18 @@ public class CapacitorScannerPlugin extends Plugin {
1679
1709
  @NonNull
1680
1710
  private RectF getRectF(Rectangle rect) {
1681
1711
  float minX = Math.min(Math.min(rect.getTopLeft().x, rect.getTopRight().x),
1682
- Math.min(rect.getBottomLeft().x, rect.getBottomRight().x));
1712
+ Math.min(rect.getBottomLeft().x, rect.getBottomRight().x));
1683
1713
  float minY = Math.min(Math.min(rect.getTopLeft().y, rect.getTopRight().y),
1684
- Math.min(rect.getBottomLeft().y, rect.getBottomRight().y));
1714
+ Math.min(rect.getBottomLeft().y, rect.getBottomRight().y));
1685
1715
  float maxX = Math.max(Math.max(rect.getTopLeft().x, rect.getTopRight().x),
1686
- Math.max(rect.getBottomLeft().x, rect.getBottomRight().x));
1716
+ Math.max(rect.getBottomLeft().x, rect.getBottomRight().x));
1687
1717
  float maxY = Math.max(Math.max(rect.getTopLeft().y, rect.getTopRight().y),
1688
- Math.max(rect.getBottomLeft().y, rect.getBottomRight().y));
1718
+ Math.max(rect.getBottomLeft().y, rect.getBottomRight().y));
1689
1719
 
1690
1720
  RectF boundingBox = new RectF(minX, minY, maxX, maxY);
1691
1721
 
1692
- // **FIX**: Horizontally flip the coordinates if the front camera is active to match the mirrored preview
1722
+ // **FIX**: Horizontally flip the coordinates if the front camera is active to
1723
+ // match the mirrored preview
1693
1724
  if (currentLensFacing == CameraSelector.LENS_FACING_FRONT) {
1694
1725
  float left = boundingBox.left;
1695
1726
  boundingBox.left = previewView.getWidth() - boundingBox.right;
@@ -1699,18 +1730,19 @@ public class CapacitorScannerPlugin extends Plugin {
1699
1730
  return boundingBox;
1700
1731
  }
1701
1732
 
1702
- // **SAMPLE METHOD**: Calculate preview rect in image coordinates (exact sample logic)
1733
+ // **SAMPLE METHOD**: Calculate preview rect in image coordinates (exact sample
1734
+ // logic)
1703
1735
  private RectF previewRectInImage(Size imageSize, Size previewSize) {
1704
1736
  float widthRatio = (float) previewSize.getWidth() / imageSize.getWidth();
1705
1737
  float heightRatio = (float) previewSize.getHeight() / imageSize.getHeight();
1706
-
1738
+
1707
1739
  if (widthRatio > heightRatio) {
1708
1740
  // Preview wider - image cropped top/bottom
1709
1741
  float previewHeightInImage = previewSize.getHeight() / widthRatio;
1710
1742
  float topOffset = (imageSize.getHeight() - previewHeightInImage) / 2;
1711
1743
  return new RectF(0f, topOffset, imageSize.getWidth(), topOffset + previewHeightInImage);
1712
1744
  } else {
1713
- // Preview taller - image cropped left/right
1745
+ // Preview taller - image cropped left/right
1714
1746
  float previewWidthInImage = previewSize.getWidth() / heightRatio;
1715
1747
  float leftOffset = (imageSize.getWidth() - previewWidthInImage) / 2;
1716
1748
  return new RectF(leftOffset, 0f, leftOffset + previewWidthInImage, imageSize.getHeight());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scr2em/capacitor-scanner",
3
- "version": "6.0.18",
3
+ "version": "6.0.20",
4
4
  "description": "scan codes",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -8,6 +8,7 @@
8
8
  "unpkg": "dist/plugin.js",
9
9
  "files": [
10
10
  "android/src/main/",
11
+ "android/libs/rectangle-detector-release.aar",
11
12
  "android/build.gradle",
12
13
  "dist/",
13
14
  "ios/Sources",
@@ -1,73 +0,0 @@
1
- plugins {
2
- id 'com.android.library'
3
- id 'kotlin-android'
4
- id 'maven-publish'
5
- id 'org.jetbrains.dokka'
6
- }
7
-
8
- group = 'com.github.smartbank-inc'
9
-
10
- android {
11
- namespace "com.smartbank.rectangledetector"
12
- compileSdkVersion compile_sdk_version
13
-
14
- defaultConfig {
15
- minSdkVersion 23
16
- targetSdkVersion target_sdk_version
17
- versionCode version_code
18
- versionName version_name
19
-
20
- consumerProguardFiles "consumer-rules.pro"
21
- }
22
-
23
- buildTypes {
24
- release {
25
- minifyEnabled false
26
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27
- }
28
- }
29
- compileOptions {
30
- sourceCompatibility JavaVersion.VERSION_17
31
- targetCompatibility JavaVersion.VERSION_17
32
- }
33
- kotlinOptions {
34
- jvmTarget = '17'
35
- }
36
- }
37
-
38
- repositories {
39
- google()
40
- mavenCentral()
41
- }
42
-
43
- dependencies {
44
- implementation 'org.opencv:opencv:4.12.0'
45
- }
46
-
47
- afterEvaluate {
48
- tasks.register('androidSourcesJar', Jar) {
49
- archiveClassifier.convention('sources')
50
- archiveClassifier.set('sources')
51
- from android.sourceSets.main.java.source
52
- }
53
-
54
- tasks.register('androidJavadocsJar', Jar) {
55
- dependsOn dokkaJavadoc
56
- archiveClassifier.convention('javadoc')
57
- archiveClassifier.set('javadoc')
58
- from dokkaJavadoc.outputDirectory
59
- }
60
-
61
- publishing {
62
- publications {
63
- release(MavenPublication) {
64
- from components.release
65
- groupId = 'com.github.smartbank-inc'
66
- artifactId = 'android-rectangle-detector'
67
- version = version_name
68
- artifact androidSourcesJar
69
- artifact androidJavadocsJar
70
- }
71
- }
72
- }
73
- }
@@ -1,21 +0,0 @@
1
- # Add project specific ProGuard rules here.
2
- # You can control the set of applied configuration files using the
3
- # proguardFiles setting in build.gradle.
4
- #
5
- # For more details, see
6
- # http://developer.android.com/guide/developing/tools/proguard.html
7
-
8
- # If your project uses WebView with JS, uncomment the following
9
- # and specify the fully qualified class name to the JavaScript interface
10
- # class:
11
- #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12
- # public *;
13
- #}
14
-
15
- # Uncomment this to preserve the line number information for
16
- # debugging stack traces.
17
- #-keepattributes SourceFile,LineNumberTable
18
-
19
- # If you keep the line number information, uncomment this to
20
- # hide the original source file name.
21
- #-renamesourcefileattribute SourceFile
@@ -1,2 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <manifest />