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
|
|
313
|
+
// Create responsive auto-focus action with fast duration for quick transitions
|
|
314
314
|
FocusMeteringAction initialFocus = new FocusMeteringAction.Builder(centerPoint)
|
|
315
|
-
.setAutoCancelDuration(
|
|
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(
|
|
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(
|
|
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
|
-
//
|
|
490
|
-
|
|
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
|
|
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
|
-
//
|
|
505
|
-
int autoCancelDuration = autoCancelDurationParam != null ? autoCancelDurationParam :
|
|
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
|
|
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
|
|
581
|
-
for (int i = 0; i <
|
|
582
|
-
Thread.sleep(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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