capacitor-plugin-camera-forked 3.0.91 → 3.0.94

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.
@@ -328,9 +328,6 @@ public class CameraPreviewPlugin extends Plugin {
328
328
 
329
329
  camera.getCameraControl().startFocusAndMetering(initialFocus);
330
330
 
331
- // Enable continuous auto-focus by starting a background focus monitoring
332
- startContinuousAutoFocus();
333
-
334
331
  Log.d("Camera", "Initialized responsive auto-focus with continuous monitoring");
335
332
  } catch (Exception e) {
336
333
  Log.e("Camera", "Failed to initialize responsive auto-focus: " + e.getMessage());
@@ -338,65 +335,69 @@ public class CameraPreviewPlugin extends Plugin {
338
335
  }
339
336
  }
340
337
 
341
- /**
342
- * Start continuous auto-focus monitoring for better focus stability
343
- */
344
- private void startContinuousAutoFocus() {
345
- if (camera != null && previewView != null) {
346
- // Use a separate executor for continuous focus to avoid blocking
347
- ExecutorService focusExecutor = Executors.newSingleThreadExecutor();
348
-
349
- focusExecutor.execute(new Runnable() {
350
- @Override
351
- public void run() {
352
- try {
353
- while (camera != null && camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
354
- Thread.sleep(800); // Check every 800ms for faster transitions
355
-
356
- getActivity().runOnUiThread(new Runnable() {
357
- @Override
358
- public void run() {
359
- try {
360
- // Trigger auto-focus at center to maintain continuous focus
361
- MeteringPointFactory factory = previewView.getMeteringPointFactory();
362
- float centerX = previewView.getWidth() / 2.0f;
363
- float centerY = previewView.getHeight() / 2.0f;
364
- MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
365
-
366
- FocusMeteringAction continuousAction = new FocusMeteringAction.Builder(centerPoint)
367
- .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for responsive transitions
368
- .build();
369
-
370
- camera.getCameraControl().startFocusAndMetering(continuousAction);
371
- } catch (Exception e) {
372
- Log.d("Camera", "Continuous focus update failed: " + e.getMessage());
373
- }
374
- }
375
- });
376
- }
377
- } catch (InterruptedException e) {
378
- Log.d("Camera", "Continuous auto-focus stopped");
379
- }
380
- }
381
- });
382
- }
383
- }
384
-
385
338
  @PluginMethod
386
339
  public void startCamera(PluginCall call) {
387
340
  getActivity().runOnUiThread(new Runnable() {
388
341
  public void run() {
389
342
  try {
390
- camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
343
+ // Validate that all required components are ready
344
+ if (cameraProvider == null) {
345
+ call.reject("Camera provider not initialized");
346
+ return;
347
+ }
348
+ if (cameraSelector == null) {
349
+ call.reject("Camera selector not initialized");
350
+ return;
351
+ }
352
+ if (useCaseGroup == null) {
353
+ call.reject("Camera use cases not initialized");
354
+ return;
355
+ }
356
+ if (previewView == null) {
357
+ call.reject("Preview view not initialized");
358
+ return;
359
+ }
360
+
361
+ // Ensure preview surface is properly connected
362
+ if (preview != null) {
363
+ preview.setSurfaceProvider(previewView.getSurfaceProvider());
364
+ }
365
+
366
+ // Make UI changes first
391
367
  previewView.setVisibility(View.VISIBLE);
392
368
  previewView.setBackgroundColor(Color.BLACK);
393
369
  makeWebViewTransparent();
394
-
395
- // Initialize responsive auto-focus for better performance
396
- initializeResponsiveAutoFocus();
397
-
398
- triggerOnPlayed();
399
- call.resolve();
370
+
371
+ // Small delay to ensure preview surface is ready
372
+ exec.execute(new Runnable() {
373
+ @Override
374
+ public void run() {
375
+ try {
376
+ Thread.sleep(50); // Brief delay for surface initialization
377
+
378
+ getActivity().runOnUiThread(new Runnable() {
379
+ @Override
380
+ public void run() {
381
+ try {
382
+ // Bind camera to lifecycle
383
+ camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
384
+
385
+ // Initialize responsive auto-focus for better performance
386
+ initializeResponsiveAutoFocus();
387
+
388
+ triggerOnPlayed();
389
+ call.resolve();
390
+ } catch (Exception e) {
391
+ e.printStackTrace();
392
+ call.reject("Failed to bind camera: " + e.getMessage());
393
+ }
394
+ }
395
+ });
396
+ } catch (InterruptedException e) {
397
+ call.reject("Camera start interrupted: " + e.getMessage());
398
+ }
399
+ }
400
+ });
400
401
  } catch (Exception e) {
401
402
  e.printStackTrace();
402
403
  call.reject(e.getMessage());
@@ -508,24 +509,8 @@ public class CameraPreviewPlugin extends Plugin {
508
509
  .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for zoom focus
509
510
  .build();
510
511
 
511
- ListenableFuture<FocusMeteringResult> zoomFocusFuture =
512
- camera.getCameraControl().startFocusAndMetering(zoomFocusAction);
513
-
514
- zoomFocusFuture.addListener(new Runnable() {
515
- @Override
516
- public void run() {
517
- try {
518
- FocusMeteringResult result = zoomFocusFuture.get();
519
- if (result.isFocusSuccessful()) {
520
- Log.d("Camera", "Zoom-triggered focus successful for factor: " + factor);
521
- } else {
522
- Log.d("Camera", "Zoom-triggered focus failed, will rely on continuous AF");
523
- }
524
- } catch (Exception e) {
525
- Log.d("Camera", "Zoom focus result check failed: " + e.getMessage());
526
- }
527
- }
528
- }, ContextCompat.getMainExecutor(getContext()));
512
+ // Trigger focus after zoom change - simplified without result handling
513
+ camera.getCameraControl().startFocusAndMetering(zoomFocusAction);
529
514
  }
530
515
  } catch (Exception e) {
531
516
  Log.d("Camera", "Auto-focus after zoom failed: " + e.getMessage());
@@ -609,39 +594,24 @@ public class CameraPreviewPlugin extends Plugin {
609
594
  // Start focus and metering with result callback
610
595
  ListenableFuture<FocusMeteringResult> future = camera.getCameraControl().startFocusAndMetering(action);
611
596
 
612
- // Add callback to handle the result with enhanced focus maintenance
597
+ // Simple callback to return focus result
613
598
  future.addListener(new Runnable() {
614
599
  @Override
615
600
  public void run() {
616
- try {
617
- FocusMeteringResult result = future.get();
618
601
  JSObject response = new JSObject();
619
- response.put("success", true);
620
- response.put("autoFocusSuccessful", result.isFocusSuccessful());
621
-
622
- if (includeExposure) {
623
- // Note: CameraX FocusMeteringResult doesn't provide separate exposure success status
624
- // We'll indicate that exposure was attempted along with focus
625
- response.put("autoExposureSuccessful", result.isFocusSuccessful());
626
- }
627
-
628
- response.put("x", x);
629
- response.put("y", y);
630
-
631
- // If focus failed, try a backup focus attempt to reduce need for multiple taps
632
- if (!result.isFocusSuccessful()) {
633
- Log.d("Camera", "Initial focus failed, attempting backup focus");
634
- performBackupFocus(previewX, previewY);
635
- } else {
636
- // If manual focus was successful, maintain it with a follow-up action
637
- maintainFocusAtPoint(previewX, previewY);
602
+ try {
603
+ FocusMeteringResult result = future.get();
604
+ response.put("success", true);
605
+ response.put("autoFocusSuccessful", result.isFocusSuccessful());
606
+ response.put("x", x);
607
+ response.put("y", y);
608
+
609
+ call.resolve(response);
610
+ } catch (Exception e) {
611
+ response.put("success", false);
612
+ Log.e("Camera", "Focus operation failed", e);
613
+ call.resolve(response);
638
614
  }
639
-
640
- call.resolve(response);
641
- } catch (Exception e) {
642
- Log.e("Camera", "Focus operation failed", e);
643
- call.reject("Focus operation failed: " + e.getMessage());
644
- }
645
615
  }
646
616
  }, ContextCompat.getMainExecutor(getContext()));
647
617
 
@@ -653,112 +623,6 @@ public class CameraPreviewPlugin extends Plugin {
653
623
  });
654
624
  }
655
625
 
656
- /**
657
- * Perform backup focus attempt if initial focus fails
658
- * This reduces the need for users to tap multiple times
659
- */
660
- private void performBackupFocus(float previewX, float previewY) {
661
- if (camera == null || previewView == null) return;
662
-
663
- // Wait a moment for the camera to settle
664
- ExecutorService backupFocusExecutor = Executors.newSingleThreadExecutor();
665
- backupFocusExecutor.execute(new Runnable() {
666
- @Override
667
- public void run() {
668
- try {
669
- Thread.sleep(200); // Wait 200ms for faster backup focus
670
-
671
- getActivity().runOnUiThread(new Runnable() {
672
- @Override
673
- public void run() {
674
- try {
675
- if (camera != null && previewView != null) {
676
- MeteringPointFactory factory = previewView.getMeteringPointFactory();
677
- MeteringPoint backupPoint = factory.createPoint(previewX, previewY);
678
-
679
- // Try with fast duration for responsive backup focus
680
- FocusMeteringAction backupAction = new FocusMeteringAction.Builder(backupPoint,
681
- FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
682
- .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second for backup
683
- .build();
684
-
685
- ListenableFuture<FocusMeteringResult> backupFuture =
686
- camera.getCameraControl().startFocusAndMetering(backupAction);
687
-
688
- backupFuture.addListener(new Runnable() {
689
- @Override
690
- public void run() {
691
- try {
692
- FocusMeteringResult backupResult = backupFuture.get();
693
- if (backupResult.isFocusSuccessful()) {
694
- Log.d("Camera", "Backup focus successful");
695
- maintainFocusAtPoint(previewX, previewY);
696
- } else {
697
- Log.d("Camera", "Backup focus also failed");
698
- }
699
- } catch (Exception e) {
700
- Log.d("Camera", "Backup focus exception: " + e.getMessage());
701
- }
702
- }
703
- }, ContextCompat.getMainExecutor(getContext()));
704
- }
705
- } catch (Exception e) {
706
- Log.d("Camera", "Backup focus setup failed: " + e.getMessage());
707
- }
708
- }
709
- });
710
- } catch (InterruptedException e) {
711
- Log.d("Camera", "Backup focus interrupted");
712
- }
713
- }
714
- });
715
- }
716
-
717
- /**
718
- * Maintain focus at a specific point with repeated focus actions for stability
719
- */
720
- private void maintainFocusAtPoint(float previewX, float previewY) {
721
- if (camera == null || previewView == null) return;
722
-
723
- // Use a separate executor for focus maintenance
724
- ExecutorService focusMaintainExecutor = Executors.newSingleThreadExecutor();
725
-
726
- focusMaintainExecutor.execute(new Runnable() {
727
- @Override
728
- public void run() {
729
- try {
730
- // Maintain focus for 2 seconds with quick refocus for responsive transitions
731
- for (int i = 0; i < 2; i++) {
732
- Thread.sleep(1000); // Wait 1 second between focus actions
733
-
734
- getActivity().runOnUiThread(new Runnable() {
735
- @Override
736
- public void run() {
737
- try {
738
- if (camera != null && camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
739
- MeteringPointFactory factory = previewView.getMeteringPointFactory();
740
- MeteringPoint maintainPoint = factory.createPoint(previewX, previewY);
741
-
742
- FocusMeteringAction maintainAction = new FocusMeteringAction.Builder(maintainPoint)
743
- .setAutoCancelDuration(1, TimeUnit.SECONDS) // Fast 1 second maintenance
744
- .build();
745
-
746
- camera.getCameraControl().startFocusAndMetering(maintainAction);
747
- Log.d("Camera", "Maintaining focus at tapped point");
748
- }
749
- } catch (Exception e) {
750
- Log.d("Camera", "Focus maintenance failed: " + e.getMessage());
751
- }
752
- }
753
- });
754
- }
755
- } catch (InterruptedException e) {
756
- Log.d("Camera", "Focus maintenance interrupted");
757
- }
758
- }
759
- });
760
- }
761
-
762
626
  @PluginMethod
763
627
  public void setAutoFocusMode(PluginCall call) {
764
628
  if (camera == null) {
@@ -928,9 +792,7 @@ public class CameraPreviewPlugin extends Plugin {
928
792
  .build();
929
793
 
930
794
  camera.getCameraControl().startFocusAndMetering(restartAction);
931
-
932
- // Restart the continuous auto-focus monitoring
933
- startContinuousAutoFocus();
795
+
934
796
  }
935
797
 
936
798
  JSObject result = new JSObject();
@@ -1151,8 +1013,17 @@ public class CameraPreviewPlugin extends Plugin {
1151
1013
 
1152
1014
  @PluginMethod
1153
1015
  public void takeSnapshot(PluginCall call) {
1154
- call.setKeepAlive(true);
1155
- takeSnapshotCall = call;
1016
+ if (camera == null) {
1017
+ call.reject("Camera not initialized.");
1018
+ return;
1019
+ }
1020
+
1021
+ try {
1022
+ call.setKeepAlive(true);
1023
+ takeSnapshotCall = call;
1024
+ } catch (Exception e) {
1025
+ call.reject("Failed to take snapshot: " + e.getMessage());
1026
+ }
1156
1027
  }
1157
1028
 
1158
1029
  @PluginMethod
@@ -33,13 +33,16 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
33
33
  private let focusThrottleInterval: TimeInterval = 0.5
34
34
  var currentCameraDevice: AVCaptureDevice?
35
35
 
36
+ // Simple focus management with fixed delay
37
+ var continuousFocusReturnTimer: Timer?
38
+
36
39
  // Store the desired JPEG quality, set during initialization
37
- var desiredJpegQuality: Float = 0.95 // Default to high quality (0.0-1.0)
40
+ var desiredJpegQuality: CGFloat = 0.95 // Default to high quality (0.0-1.0)
38
41
 
39
42
  @objc func initialize(_ call: CAPPluginCall) {
40
43
  // Get quality parameter from initialization, default to 95% if not specified
41
44
  if let quality = call.getInt("quality") {
42
- desiredJpegQuality = Float(max(1, min(100, quality))) / 100.0
45
+ desiredJpegQuality = CGFloat(max(1, min(100, quality))) / 100.0
43
46
  print("Camera initialized with JPEG quality: \(desiredJpegQuality)")
44
47
  }
45
48
 
@@ -242,6 +245,13 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
242
245
  self.photoOutput.maxPhotoQualityPrioritization = .quality
243
246
  }
244
247
 
248
+ // Enable content aware distortion correction if available
249
+ if #available(iOS 14.1, *) {
250
+ if self.photoOutput.isContentAwareDistortionCorrectionSupported {
251
+ self.photoOutput.isContentAwareDistortionCorrectionEnabled = true
252
+ }
253
+ }
254
+
245
255
  if self.captureSession.canAddOutput(self.photoOutput) {
246
256
  self.captureSession.addOutput(photoOutput)
247
257
  }
@@ -335,7 +345,9 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
335
345
 
336
346
  // Enable auto-focus and auto-exposure for optimal capture
337
347
  if #available(iOS 14.1, *) {
338
- photoSettings.isAutoContentAwareDistortionCorrectionEnabled = true
348
+ if self.photoOutput.isContentAwareDistortionCorrectionEnabled {
349
+ photoSettings.isAutoContentAwareDistortionCorrectionEnabled = true
350
+ }
339
351
  }
340
352
 
341
353
  // Enhanced focus before capture for better close-up performance
@@ -865,51 +877,17 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
865
877
  x >= 0.0 && x <= 1.0, y >= 0.0 && y <= 1.0 {
866
878
  let point = CGPoint(x: CGFloat(x), y: CGFloat(y))
867
879
 
868
- // Check if focus is currently animating and reset if stuck
869
- if isFocusAnimating {
870
- resetFocusIfStuck()
871
- }
872
-
880
+ // Simplified focus without complex throttling or maintenance
873
881
  focusWithPoint(point: point)
874
882
 
875
- // Calculate the point in the preview layer's coordinate space
876
- let previewPoint = CGPoint(x: point.x * previewView.bounds.width,
877
- y: point.y * previewView.bounds.height)
878
- // showFocusView(at: previewPoint)
879
883
  call.resolve()
880
884
  } else {
881
885
  call.reject("Invalid coordinates. Provide normalized x,y values (0.0-1.0)")
882
886
  }
883
887
  }
884
888
 
885
- private func resetFocusIfStuck() {
886
- DispatchQueue.main.async { [weak self] in
887
- guard let self = self else { return }
888
-
889
- // Remove any existing focus indicator
890
- self.focusView?.removeFromSuperview()
891
- self.focusCompletionTimer?.invalidate()
892
- self.isFocusAnimating = false
893
-
894
- // Reset focus to continuous mode
895
- guard let videoInput = self.videoInput else { return }
896
- let device = videoInput.device
897
- do {
898
- try device.lockForConfiguration()
899
- if device.isFocusModeSupported(.continuousAutoFocus) {
900
- device.focusMode = .continuousAutoFocus
901
- }
902
- device.unlockForConfiguration()
903
- } catch {
904
- print("Could not reset focus: \(error)")
905
- }
906
- }
907
- }
908
-
909
889
  @objc func resetFocus(_ call: CAPPluginCall) {
910
- resetFocusIfStuck()
911
-
912
- // Reset to center focus
890
+ // Simple reset to center focus
913
891
  let centerPoint = CGPoint(x: 0.5, y: 0.5)
914
892
  focusWithPoint(point: centerPoint)
915
893
 
@@ -921,72 +899,40 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
921
899
  let convertedPoint = self.previewView.videoPreviewLayer.captureDevicePointConverted(fromLayerPoint: location)
922
900
 
923
901
  focusWithPoint(point: convertedPoint)
924
- // showFocusView(at: location)
925
902
  }
926
903
 
927
904
  func focusWithPoint(point: CGPoint) {
928
905
  guard let videoInput = self.videoInput else { return }
929
906
  let device = videoInput.device
930
907
 
931
- let now = Date()
932
- if now.timeIntervalSince(lastFocusTime) < focusThrottleInterval {
933
- return
934
- }
935
- lastFocusTime = now
936
-
937
908
  do {
938
909
  try device.lockForConfiguration()
939
910
 
940
- focusCompletionTimer?.invalidate()
941
-
942
- if device.isFocusPointOfInterestSupported {
911
+ // Cancel any existing focus/exposure operations
912
+ if device.isFocusModeSupported(.autoFocus) && device.isFocusPointOfInterestSupported {
943
913
  device.focusPointOfInterest = point
944
-
945
- // Use autoFocus for more aggressive focusing on specific points
946
- if device.isFocusModeSupported(.autoFocus) {
947
- device.focusMode = .autoFocus
948
-
949
- // Set up observer for focus completion
950
- NotificationCenter.default.addObserver(
951
- self,
952
- selector: #selector(subjectAreaDidChange),
953
- name: .AVCaptureDeviceSubjectAreaDidChange,
954
- object: device
955
- )
956
- } else if device.isFocusModeSupported(.continuousAutoFocus) {
957
- device.focusMode = .continuousAutoFocus
958
- }
914
+ device.focusMode = .autoFocus
959
915
  }
960
916
 
961
- if device.isExposurePointOfInterestSupported {
917
+ if device.isExposureModeSupported(.autoExpose) && device.isExposurePointOfInterestSupported {
962
918
  device.exposurePointOfInterest = point
963
-
964
- // Use autoExpose for specific point exposure
965
- if device.isExposureModeSupported(.autoExpose) {
966
- device.exposureMode = .autoExpose
967
- } else if device.isExposureModeSupported(.continuousAutoExposure) {
968
- device.exposureMode = .continuousAutoExposure
969
- }
919
+ device.exposureMode = .autoExpose
970
920
  }
971
921
 
972
- // Ensure full focus range is available for close objects
922
+ // Ensure full focus range is available
973
923
  if device.isAutoFocusRangeRestrictionSupported {
974
924
  device.autoFocusRangeRestriction = .none
975
925
  }
976
926
 
977
- device.isSubjectAreaChangeMonitoringEnabled = true
978
-
979
927
  device.unlockForConfiguration()
980
928
 
981
- // Switch back to continuous focus after a delay to maintain automatic focus
982
- DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
983
- self?.returnToContinuousFocus()
984
- }
929
+ // Cancel any existing timer
930
+ continuousFocusReturnTimer?.invalidate()
985
931
 
986
- focusCompletionTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { [weak self] _ in
987
- DispatchQueue.main.async {
988
- self?.hideFocusIndicatorWithCompletion()
989
- }
932
+ // Return to continuous focus after fixed delay
933
+ // This provides predictable behavior - user has 2 seconds to take photo after tap-to-focus
934
+ continuousFocusReturnTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { [weak self] _ in
935
+ self?.returnToContinuousFocus()
990
936
  }
991
937
 
992
938
  } catch {
@@ -994,26 +940,16 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
994
940
  }
995
941
  }
996
942
 
997
- @objc private func subjectAreaDidChange(notification: NSNotification) {
998
- DispatchQueue.main.async { [weak self] in
999
- self?.hideFocusIndicatorWithCompletion()
1000
- }
1001
-
1002
- NotificationCenter.default.removeObserver(self, name: .AVCaptureDeviceSubjectAreaDidChange, object: notification.object)
1003
- }
1004
-
1005
943
  private func returnToContinuousFocus() {
1006
944
  guard let videoInput = self.videoInput else { return }
1007
945
  let device = videoInput.device
1008
946
  do {
1009
947
  try device.lockForConfiguration()
1010
948
 
1011
- // Return to continuous auto focus for automatic operation
1012
949
  if device.isFocusModeSupported(.continuousAutoFocus) {
1013
950
  device.focusMode = .continuousAutoFocus
1014
951
  }
1015
952
 
1016
- // Return to continuous auto exposure
1017
953
  if device.isExposureModeSupported(.continuousAutoExposure) {
1018
954
  device.exposureMode = .continuousAutoExposure
1019
955
  }
@@ -1024,58 +960,6 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
1024
960
  }
1025
961
  }
1026
962
 
1027
-
1028
-
1029
- private func hideFocusIndicatorWithCompletion() {
1030
- DispatchQueue.main.async { [weak self] in
1031
- guard let self = self, let focusView = self.focusView else { return }
1032
-
1033
- UIView.animate(withDuration: 0.2, animations: {
1034
- focusView.alpha = 0.0
1035
- focusView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
1036
- }) { _ in
1037
- focusView.removeFromSuperview()
1038
- focusView.transform = CGAffineTransform.identity
1039
- self.isFocusAnimating = false
1040
- }
1041
- }
1042
- }
1043
-
1044
- // func showFocusView(at point: CGPoint) {
1045
- // DispatchQueue.main.async { [weak self] in
1046
- // guard let self = self else { return }
1047
-
1048
- // if self.isFocusAnimating {
1049
- // self.focusView?.removeFromSuperview()
1050
- // self.focusCompletionTimer?.invalidate()
1051
- // }
1052
-
1053
- // // Create focus view if needed - but make it invisible
1054
- // if self.focusView == nil {
1055
- // self.focusView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
1056
- // // Make the focus view completely transparent
1057
- // self.focusView?.layer.borderColor = UIColor.clear.cgColor
1058
- // self.focusView?.layer.borderWidth = 0.0
1059
- // self.focusView?.layer.cornerRadius = 40
1060
- // self.focusView?.backgroundColor = .clear
1061
- // self.focusView?.alpha = 0.0
1062
-
1063
- // // Remove the inner circle to make it completely invisible
1064
- // // No inner circle added
1065
- // }
1066
-
1067
- // self.focusView?.center = point
1068
- // self.focusView?.alpha = 0.0 // Keep invisible
1069
- // self.focusView?.transform = CGAffineTransform.identity
1070
- // self.previewView.addSubview(self.focusView!)
1071
-
1072
- // self.isFocusAnimating = true
1073
-
1074
- // // Skip the animation since the view is invisible
1075
- // // Focus functionality still works, just no visual feedback
1076
- // }
1077
- // }
1078
-
1079
963
  @objc func requestCameraPermission(_ call: CAPPluginCall) {
1080
964
  AVCaptureDevice.requestAccess(for: .video) { granted in
1081
965
  DispatchQueue.main.async {
@@ -1191,14 +1075,59 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
1191
1075
  }
1192
1076
 
1193
1077
  @objc func takePhoto(_ call: CAPPluginCall) {
1194
- call.keepAlive = true
1195
1078
  takePhotoCall = call
1196
- takePhotoWithAVFoundation()
1079
+
1080
+ // Configure photo settings
1081
+ let photoSettings = AVCapturePhotoSettings()
1082
+ photoSettings.isHighResolutionPhotoEnabled = true
1083
+
1084
+ // Use JPEG format with quality setting
1085
+ if #available(iOS 11.0, *) {
1086
+ photoSettings.photoQualityPrioritization = .quality
1087
+ }
1088
+
1089
+ // Configure for optimal photo capture without complex focus management
1090
+ guard let device = videoInput?.device else {
1091
+ self.photoOutput.capturePhoto(with: photoSettings, delegate: self)
1092
+ return
1093
+ }
1094
+
1095
+ if device.isFocusModeSupported(.autoFocus) {
1096
+ do {
1097
+ try device.lockForConfiguration()
1098
+
1099
+ // Set to auto focus for capture
1100
+ device.focusMode = .autoFocus
1101
+
1102
+ // Ensure full focus range
1103
+ if device.isAutoFocusRangeRestrictionSupported {
1104
+ device.autoFocusRangeRestriction = .none
1105
+ }
1106
+
1107
+ // Set exposure for photo
1108
+ if device.isExposureModeSupported(.autoExpose) {
1109
+ device.exposureMode = .autoExpose
1110
+ }
1111
+
1112
+ device.unlockForConfiguration()
1113
+
1114
+ // Wait briefly for focus, then capture
1115
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
1116
+ self.photoOutput.capturePhoto(with: photoSettings, delegate: self)
1117
+ }
1118
+ } catch {
1119
+ print("Could not configure focus for capture: \(error)")
1120
+ self.photoOutput.capturePhoto(with: photoSettings, delegate: self)
1121
+ }
1122
+ } else {
1123
+ // Capture immediately if auto focus isn't supported
1124
+ self.photoOutput.capturePhoto(with: photoSettings, delegate: self)
1125
+ }
1197
1126
  }
1198
1127
 
1199
1128
  deinit {
1200
- NotificationCenter.default.removeObserver(self)
1201
1129
  focusCompletionTimer?.invalidate()
1130
+ continuousFocusReturnTimer?.invalidate()
1202
1131
  }
1203
1132
 
1204
1133
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-plugin-camera-forked",
3
- "version": "3.0.91",
3
+ "version": "3.0.94",
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",