capacitor-plugin-camera-forked 3.0.77 → 3.0.90

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.
@@ -310,9 +310,9 @@ public class CameraPreviewPlugin extends Plugin {
310
310
  float centerY = previewView.getHeight() / 2.0f;
311
311
  MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
312
312
 
313
- // Create responsive auto-focus action with longer duration for better stability
313
+ // Create responsive auto-focus action with fast duration for quick transitions
314
314
  FocusMeteringAction initialFocus = new FocusMeteringAction.Builder(centerPoint)
315
- .setAutoCancelDuration(5, TimeUnit.SECONDS) // Increased from 1 to 5 seconds
315
+ .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for responsive focus
316
316
  .build();
317
317
 
318
318
  camera.getCameraControl().startFocusAndMetering(initialFocus);
@@ -340,7 +340,7 @@ public class CameraPreviewPlugin extends Plugin {
340
340
  public void run() {
341
341
  try {
342
342
  while (camera != null && camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
343
- Thread.sleep(2000); // Check every 2 seconds
343
+ Thread.sleep(800); // Check every 800ms for faster transitions
344
344
 
345
345
  getActivity().runOnUiThread(new Runnable() {
346
346
  @Override
@@ -353,7 +353,7 @@ public class CameraPreviewPlugin extends Plugin {
353
353
  MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
354
354
 
355
355
  FocusMeteringAction continuousAction = new FocusMeteringAction.Builder(centerPoint)
356
- .setAutoCancelDuration(3, TimeUnit.SECONDS)
356
+ .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for responsive transitions
357
357
  .build();
358
358
 
359
359
  camera.getCameraControl().startFocusAndMetering(continuousAction);
@@ -453,6 +453,63 @@ public class CameraPreviewPlugin extends Plugin {
453
453
  Float factor = call.getFloat("factor");
454
454
  try {
455
455
  camera.getCameraControl().setZoomRatio(factor);
456
+
457
+ // Automatically trigger focus after zoom change for better UX
458
+ ExecutorService zoomFocusExecutor = Executors.newSingleThreadExecutor();
459
+ zoomFocusExecutor.execute(new Runnable() {
460
+ @Override
461
+ public void run() {
462
+ try {
463
+ // Wait briefly for zoom to settle - faster for responsive focus
464
+ Thread.sleep(150);
465
+
466
+ getActivity().runOnUiThread(new Runnable() {
467
+ @Override
468
+ public void run() {
469
+ try {
470
+ // Trigger auto-focus at center after zoom change
471
+ if (previewView != null && camera != null) {
472
+ MeteringPointFactory factory = previewView.getMeteringPointFactory();
473
+ float centerX = previewView.getWidth() / 2.0f;
474
+ float centerY = previewView.getHeight() / 2.0f;
475
+ MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
476
+
477
+ // Use fast focus settings for responsive zoom-triggered focus
478
+ FocusMeteringAction zoomFocusAction = new FocusMeteringAction.Builder(centerPoint,
479
+ FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
480
+ .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for zoom focus
481
+ .build();
482
+
483
+ ListenableFuture<FocusMeteringResult> zoomFocusFuture =
484
+ camera.getCameraControl().startFocusAndMetering(zoomFocusAction);
485
+
486
+ zoomFocusFuture.addListener(new Runnable() {
487
+ @Override
488
+ public void run() {
489
+ try {
490
+ FocusMeteringResult result = zoomFocusFuture.get();
491
+ if (result.isFocusSuccessful()) {
492
+ Log.d("Camera", "Zoom-triggered focus successful for factor: " + factor);
493
+ } else {
494
+ Log.d("Camera", "Zoom-triggered focus failed, will rely on continuous AF");
495
+ }
496
+ } catch (Exception e) {
497
+ Log.d("Camera", "Zoom focus result check failed: " + e.getMessage());
498
+ }
499
+ }
500
+ }, ContextCompat.getMainExecutor(getContext()));
501
+ }
502
+ } catch (Exception e) {
503
+ Log.d("Camera", "Auto-focus after zoom failed: " + e.getMessage());
504
+ }
505
+ }
506
+ });
507
+ } catch (InterruptedException e) {
508
+ Log.d("Camera", "Zoom focus interrupted");
509
+ }
510
+ }
511
+ });
512
+
456
513
  } catch (Exception e) {
457
514
  e.printStackTrace();
458
515
  call.reject(e.getMessage());
@@ -486,9 +543,9 @@ public class CameraPreviewPlugin extends Plugin {
486
543
  @Override
487
544
  public void run() {
488
545
  try {
489
- // Cancel any existing focus operations for faster transitions
490
- camera.getCameraControl().cancelFocusAndMetering();
491
-
546
+ // Only cancel if focus has been stable for a while to reduce multiple tap issues
547
+ // This prevents interrupting legitimate focus operations
548
+
492
549
  // Use PreviewView's built-in MeteringPointFactory for proper coordinate transformation
493
550
  MeteringPointFactory factory = previewView.getMeteringPointFactory();
494
551
 
@@ -498,11 +555,11 @@ public class CameraPreviewPlugin extends Plugin {
498
555
 
499
556
  MeteringPoint focusPoint = factory.createPoint(previewX, previewY);
500
557
 
501
- // Get configurable options with improved defaults for better focus stability
558
+ // Get configurable options with fast defaults for responsive focus
502
559
  boolean includeExposure = Boolean.TRUE.equals(call.getBoolean("includeExposure", true));
503
560
  Integer autoCancelDurationParam = call.getInt("autoCancelDurationSeconds");
504
- // Increased to 4 seconds for better focus stability and manual control
505
- int autoCancelDuration = autoCancelDurationParam != null ? autoCancelDurationParam : 4;
561
+ // Reduced to 1 second for fast responsive focus during near/far transitions
562
+ int autoCancelDuration = autoCancelDurationParam != null ? autoCancelDurationParam : 1;
506
563
 
507
564
  // Build the focus and metering action with optimized settings
508
565
  FocusMeteringAction.Builder builder;
@@ -543,8 +600,12 @@ public class CameraPreviewPlugin extends Plugin {
543
600
  response.put("x", x);
544
601
  response.put("y", y);
545
602
 
546
- // If manual focus was successful, maintain it with a follow-up action
547
- if (result.isFocusSuccessful()) {
603
+ // If focus failed, try a backup focus attempt to reduce need for multiple taps
604
+ if (!result.isFocusSuccessful()) {
605
+ Log.d("Camera", "Initial focus failed, attempting backup focus");
606
+ performBackupFocus(previewX, previewY);
607
+ } else {
608
+ // If manual focus was successful, maintain it with a follow-up action
548
609
  maintainFocusAtPoint(previewX, previewY);
549
610
  }
550
611
 
@@ -564,6 +625,67 @@ public class CameraPreviewPlugin extends Plugin {
564
625
  });
565
626
  }
566
627
 
628
+ /**
629
+ * Perform backup focus attempt if initial focus fails
630
+ * This reduces the need for users to tap multiple times
631
+ */
632
+ private void performBackupFocus(float previewX, float previewY) {
633
+ if (camera == null || previewView == null) return;
634
+
635
+ // Wait a moment for the camera to settle
636
+ ExecutorService backupFocusExecutor = Executors.newSingleThreadExecutor();
637
+ backupFocusExecutor.execute(new Runnable() {
638
+ @Override
639
+ public void run() {
640
+ try {
641
+ Thread.sleep(200); // Wait 200ms for faster backup focus
642
+
643
+ getActivity().runOnUiThread(new Runnable() {
644
+ @Override
645
+ public void run() {
646
+ try {
647
+ if (camera != null && previewView != null) {
648
+ MeteringPointFactory factory = previewView.getMeteringPointFactory();
649
+ MeteringPoint backupPoint = factory.createPoint(previewX, previewY);
650
+
651
+ // Try with fast duration for responsive backup focus
652
+ FocusMeteringAction backupAction = new FocusMeteringAction.Builder(backupPoint,
653
+ FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
654
+ .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for backup
655
+ .build();
656
+
657
+ ListenableFuture<FocusMeteringResult> backupFuture =
658
+ camera.getCameraControl().startFocusAndMetering(backupAction);
659
+
660
+ backupFuture.addListener(new Runnable() {
661
+ @Override
662
+ public void run() {
663
+ try {
664
+ FocusMeteringResult backupResult = backupFuture.get();
665
+ if (backupResult.isFocusSuccessful()) {
666
+ Log.d("Camera", "Backup focus successful");
667
+ maintainFocusAtPoint(previewX, previewY);
668
+ } else {
669
+ Log.d("Camera", "Backup focus also failed");
670
+ }
671
+ } catch (Exception e) {
672
+ Log.d("Camera", "Backup focus exception: " + e.getMessage());
673
+ }
674
+ }
675
+ }, ContextCompat.getMainExecutor(getContext()));
676
+ }
677
+ } catch (Exception e) {
678
+ Log.d("Camera", "Backup focus setup failed: " + e.getMessage());
679
+ }
680
+ }
681
+ });
682
+ } catch (InterruptedException e) {
683
+ Log.d("Camera", "Backup focus interrupted");
684
+ }
685
+ }
686
+ });
687
+ }
688
+
567
689
  /**
568
690
  * Maintain focus at a specific point with repeated focus actions for stability
569
691
  */
@@ -577,9 +699,9 @@ public class CameraPreviewPlugin extends Plugin {
577
699
  @Override
578
700
  public void run() {
579
701
  try {
580
- // Maintain focus for 10 seconds with periodic refocus
581
- for (int i = 0; i < 5; i++) {
582
- Thread.sleep(2000); // Wait 2 seconds between focus actions
702
+ // Maintain focus for 2 seconds with quick refocus for responsive transitions
703
+ for (int i = 0; i < 2; i++) {
704
+ Thread.sleep(1000); // Wait 1 second between focus actions
583
705
 
584
706
  getActivity().runOnUiThread(new Runnable() {
585
707
  @Override
@@ -590,7 +712,7 @@ public class CameraPreviewPlugin extends Plugin {
590
712
  MeteringPoint maintainPoint = factory.createPoint(previewX, previewY);
591
713
 
592
714
  FocusMeteringAction maintainAction = new FocusMeteringAction.Builder(maintainPoint)
593
- .setAutoCancelDuration(3, TimeUnit.SECONDS)
715
+ .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second maintenance
594
716
  .build();
595
717
 
596
718
  camera.getCameraControl().startFocusAndMetering(maintainAction);
@@ -710,7 +832,7 @@ public class CameraPreviewPlugin extends Plugin {
710
832
  int pointIndex = 0;
711
833
 
712
834
  while (camera != null && camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
713
- Thread.sleep(1500); // Focus check every 1.5 seconds
835
+ Thread.sleep(600); // Focus check every 600ms for faster near/far transitions
714
836
 
715
837
  final int currentPointIndex = pointIndex;
716
838
 
@@ -728,7 +850,7 @@ public class CameraPreviewPlugin extends Plugin {
728
850
  MeteringPoint adaptivePoint = factory.createPoint(x, y);
729
851
 
730
852
  FocusMeteringAction adaptiveAction = new FocusMeteringAction.Builder(adaptivePoint)
731
- .setAutoCancelDuration(2, TimeUnit.SECONDS)
853
+ .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for adaptive focus
732
854
  .build();
733
855
 
734
856
  camera.getCameraControl().startFocusAndMetering(adaptiveAction);
@@ -774,7 +896,7 @@ public class CameraPreviewPlugin extends Plugin {
774
896
  MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
775
897
 
776
898
  FocusMeteringAction restartAction = new FocusMeteringAction.Builder(centerPoint)
777
- .setAutoCancelDuration(5, TimeUnit.SECONDS) // Increased duration for stability
899
+ .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for responsive restart
778
900
  .build();
779
901
 
780
902
  camera.getCameraControl().startFocusAndMetering(restartAction);
@@ -48,8 +48,22 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
48
48
  return
49
49
  }
50
50
 
51
- self.previewView = PreviewView.init(frame: (self.bridge?.viewController?.view.bounds)!)
52
- self.webView!.superview!.insertSubview(self.previewView, belowSubview: self.webView!)
51
+ // Safely get the view bounds
52
+ guard let viewController = self.bridge?.viewController,
53
+ let viewBounds = viewController.view?.bounds else {
54
+ call.reject("Unable to access view controller or view bounds")
55
+ return
56
+ }
57
+
58
+ // Safely get the webView and its superview
59
+ guard let webView = self.webView,
60
+ let webViewSuperview = webView.superview else {
61
+ call.reject("Unable to access webView or its superview")
62
+ return
63
+ }
64
+
65
+ self.previewView = PreviewView.init(frame: viewBounds)
66
+ webViewSuperview.insertSubview(self.previewView, belowSubview: webView)
53
67
 
54
68
  // Only initialize capture session if permission is granted
55
69
  if authStatus == .authorized {
@@ -65,20 +79,23 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
65
79
  }
66
80
 
67
81
  @objc func rotated() {
68
- let bounds = self.webView?.bounds
69
- if bounds != nil {
70
- self.previewView.frame = bounds!
71
- if UIDevice.current.orientation == UIDeviceOrientation.portrait {
72
- self.previewView.videoPreviewLayer.connection?.videoOrientation = .portrait
73
- lastValidOrientation = "portrait"
74
- }else if UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft {
75
- self.previewView.videoPreviewLayer.connection?.videoOrientation = .landscapeRight
76
- lastValidOrientation = "landscapeRight"
77
- }else if UIDevice.current.orientation == UIDeviceOrientation.landscapeRight {
78
- self.previewView.videoPreviewLayer.connection?.videoOrientation = .landscapeLeft
79
- lastValidOrientation = "landscapeLeft"
80
- }
82
+ guard let bounds = self.webView?.bounds else {
83
+ notifyListeners("onOrientationChanged",data: nil)
84
+ return
85
+ }
86
+
87
+ self.previewView.frame = bounds
88
+ if UIDevice.current.orientation == UIDeviceOrientation.portrait {
89
+ self.previewView.videoPreviewLayer.connection?.videoOrientation = .portrait
90
+ lastValidOrientation = "portrait"
91
+ }else if UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft {
92
+ self.previewView.videoPreviewLayer.connection?.videoOrientation = .landscapeRight
93
+ lastValidOrientation = "landscapeRight"
94
+ }else if UIDevice.current.orientation == UIDeviceOrientation.landscapeRight {
95
+ self.previewView.videoPreviewLayer.connection?.videoOrientation = .landscapeLeft
96
+ lastValidOrientation = "landscapeLeft"
81
97
  }
98
+
82
99
  notifyListeners("onOrientationChanged",data: nil)
83
100
  }
84
101
 
@@ -160,11 +177,10 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
160
177
  guard let videoDevice = getBestAvailableCameraDevice() else { return }
161
178
  if enableVideoRecording {
162
179
  let microphone = AVCaptureDevice.default(for: AVMediaType.audio)
163
- if microphone != nil {
164
- let micInput = try? AVCaptureDeviceInput(device: microphone!)
165
- if captureSession.canAddInput(micInput!) {
166
- captureSession.addInput(micInput!)
167
- }
180
+ if let microphone = microphone,
181
+ let micInput = try? AVCaptureDeviceInput(device: microphone),
182
+ captureSession.canAddInput(micInput) {
183
+ captureSession.addInput(micInput)
168
184
  }
169
185
  }
170
186
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-plugin-camera-forked",
3
- "version": "3.0.77",
3
+ "version": "3.0.90",
4
4
  "description": "A capacitor camera plugin - A custom Capacitor camera plugin with additional features.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",