capacitor-plugin-camera-forked 3.0.95 → 3.0.97
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.
|
@@ -542,7 +542,7 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
542
542
|
|
|
543
543
|
} catch (Exception e) {
|
|
544
544
|
e.printStackTrace();
|
|
545
|
-
call.
|
|
545
|
+
call.resolve();
|
|
546
546
|
}
|
|
547
547
|
}
|
|
548
548
|
call.resolve();
|
|
@@ -550,22 +550,34 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
550
550
|
|
|
551
551
|
@PluginMethod
|
|
552
552
|
public void setFocus(PluginCall call) {
|
|
553
|
+
|
|
554
|
+
JSObject response = new JSObject();
|
|
553
555
|
if (!call.hasOption("x") || !call.hasOption("y")) {
|
|
554
|
-
|
|
556
|
+
response.put("success", false);
|
|
557
|
+
call.resolve(response);
|
|
555
558
|
return;
|
|
556
559
|
}
|
|
557
560
|
|
|
558
561
|
Float x = call.getFloat("x");
|
|
559
562
|
Float y = call.getFloat("y");
|
|
560
563
|
|
|
564
|
+
// Check for null values
|
|
565
|
+
if (x == null || y == null) {
|
|
566
|
+
response.put("success", false);
|
|
567
|
+
call.resolve(response);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
|
|
561
571
|
// Validate coordinate ranges (should be 0-1 for normalized coordinates)
|
|
562
572
|
if (x < 0.0f || x > 1.0f || y < 0.0f || y > 1.0f) {
|
|
563
|
-
|
|
573
|
+
response.put("success", false);
|
|
574
|
+
call.resolve(response);
|
|
564
575
|
return;
|
|
565
576
|
}
|
|
566
577
|
|
|
567
578
|
if (previewView == null || camera == null) {
|
|
568
|
-
|
|
579
|
+
response.put("success", false);
|
|
580
|
+
call.resolve(response);
|
|
569
581
|
return;
|
|
570
582
|
}
|
|
571
583
|
|
|
@@ -634,7 +646,9 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
634
646
|
|
|
635
647
|
} catch (Exception e) {
|
|
636
648
|
Log.e("Camera", "Error setting focus", e);
|
|
637
|
-
|
|
649
|
+
response.put("success", false);
|
|
650
|
+
response.put("error", e.getMessage());
|
|
651
|
+
call.resolve(response);
|
|
638
652
|
}
|
|
639
653
|
}
|
|
640
654
|
});
|
|
@@ -33,9 +33,6 @@ 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
|
-
|
|
39
36
|
// Store the desired JPEG quality, set during initialization
|
|
40
37
|
var desiredJpegQuality: CGFloat = 0.95 // Default to high quality (0.0-1.0)
|
|
41
38
|
|
|
@@ -379,21 +376,26 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
|
|
|
379
376
|
|
|
380
377
|
device.unlockForConfiguration()
|
|
381
378
|
|
|
379
|
+
if device.isAdjustingFocus {
|
|
382
380
|
// Wait longer for focus to settle, especially for close objects
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
381
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
382
|
+
self.photoOutput.capturePhoto(with: photoSettings, delegate: self)
|
|
383
|
+
|
|
384
|
+
// Restore previous settings after capture
|
|
385
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
|
|
386
|
+
do {
|
|
387
|
+
try device.lockForConfiguration()
|
|
388
|
+
device.focusMode = previousFocusMode
|
|
389
|
+
device.exposureMode = previousExposureMode
|
|
390
|
+
device.unlockForConfiguration()
|
|
391
|
+
} catch {
|
|
392
|
+
print("Could not restore camera settings: \(error)")
|
|
393
|
+
}
|
|
395
394
|
}
|
|
396
395
|
}
|
|
396
|
+
} else {
|
|
397
|
+
self.photoOutput.capturePhoto(with: photoSettings, delegate: self)
|
|
398
|
+
|
|
397
399
|
}
|
|
398
400
|
} catch {
|
|
399
401
|
// If focus configuration fails, capture anyway
|
|
@@ -877,17 +879,51 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
|
|
|
877
879
|
x >= 0.0 && x <= 1.0, y >= 0.0 && y <= 1.0 {
|
|
878
880
|
let point = CGPoint(x: CGFloat(x), y: CGFloat(y))
|
|
879
881
|
|
|
880
|
-
//
|
|
882
|
+
// Check if focus is currently animating and reset if stuck
|
|
883
|
+
if isFocusAnimating {
|
|
884
|
+
resetFocusIfStuck()
|
|
885
|
+
}
|
|
886
|
+
|
|
881
887
|
focusWithPoint(point: point)
|
|
882
888
|
|
|
889
|
+
// Calculate the point in the preview layer's coordinate space
|
|
890
|
+
//let previewPoint = CGPoint(x: point.x * previewView.bounds.width,
|
|
891
|
+
// y: point.y * previewView.bounds.height)
|
|
892
|
+
// showFocusView(at: previewPoint)
|
|
883
893
|
call.resolve()
|
|
884
894
|
} else {
|
|
885
895
|
call.reject("Invalid coordinates. Provide normalized x,y values (0.0-1.0)")
|
|
886
896
|
}
|
|
887
897
|
}
|
|
888
898
|
|
|
899
|
+
private func resetFocusIfStuck() {
|
|
900
|
+
DispatchQueue.main.async { [weak self] in
|
|
901
|
+
guard let self = self else { return }
|
|
902
|
+
|
|
903
|
+
// Remove any existing focus indicator
|
|
904
|
+
self.focusView?.removeFromSuperview()
|
|
905
|
+
self.focusCompletionTimer?.invalidate()
|
|
906
|
+
self.isFocusAnimating = false
|
|
907
|
+
|
|
908
|
+
// Reset focus to continuous mode
|
|
909
|
+
guard let videoInput = self.videoInput else { return }
|
|
910
|
+
let device = videoInput.device
|
|
911
|
+
do {
|
|
912
|
+
try device.lockForConfiguration()
|
|
913
|
+
if device.isFocusModeSupported(.continuousAutoFocus) {
|
|
914
|
+
device.focusMode = .continuousAutoFocus
|
|
915
|
+
}
|
|
916
|
+
device.unlockForConfiguration()
|
|
917
|
+
} catch {
|
|
918
|
+
print("Could not reset focus: \(error)")
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
889
923
|
@objc func resetFocus(_ call: CAPPluginCall) {
|
|
890
|
-
|
|
924
|
+
resetFocusIfStuck()
|
|
925
|
+
|
|
926
|
+
// Reset to center focus
|
|
891
927
|
let centerPoint = CGPoint(x: 0.5, y: 0.5)
|
|
892
928
|
focusWithPoint(point: centerPoint)
|
|
893
929
|
|
|
@@ -899,57 +935,99 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
|
|
|
899
935
|
let convertedPoint = self.previewView.videoPreviewLayer.captureDevicePointConverted(fromLayerPoint: location)
|
|
900
936
|
|
|
901
937
|
focusWithPoint(point: convertedPoint)
|
|
938
|
+
// showFocusView(at: location)
|
|
902
939
|
}
|
|
903
940
|
|
|
904
941
|
func focusWithPoint(point: CGPoint) {
|
|
905
942
|
guard let videoInput = self.videoInput else { return }
|
|
906
943
|
let device = videoInput.device
|
|
907
944
|
|
|
945
|
+
let now = Date()
|
|
946
|
+
if now.timeIntervalSince(lastFocusTime) < focusThrottleInterval {
|
|
947
|
+
return
|
|
948
|
+
}
|
|
949
|
+
lastFocusTime = now
|
|
950
|
+
|
|
908
951
|
do {
|
|
909
952
|
try device.lockForConfiguration()
|
|
910
953
|
|
|
911
|
-
|
|
912
|
-
|
|
954
|
+
focusCompletionTimer?.invalidate()
|
|
955
|
+
|
|
956
|
+
if device.isFocusPointOfInterestSupported {
|
|
913
957
|
device.focusPointOfInterest = point
|
|
914
|
-
|
|
958
|
+
|
|
959
|
+
// Use autoFocus for more aggressive focusing on specific points
|
|
960
|
+
if device.isFocusModeSupported(.autoFocus) {
|
|
961
|
+
device.focusMode = .autoFocus
|
|
962
|
+
|
|
963
|
+
// Set up observer for focus completion
|
|
964
|
+
NotificationCenter.default.addObserver(
|
|
965
|
+
self,
|
|
966
|
+
selector: #selector(subjectAreaDidChange),
|
|
967
|
+
name: .AVCaptureDeviceSubjectAreaDidChange,
|
|
968
|
+
object: device
|
|
969
|
+
)
|
|
970
|
+
} else if device.isFocusModeSupported(.continuousAutoFocus) {
|
|
971
|
+
device.focusMode = .continuousAutoFocus
|
|
972
|
+
}
|
|
915
973
|
}
|
|
916
974
|
|
|
917
|
-
if device.
|
|
975
|
+
if device.isExposurePointOfInterestSupported {
|
|
918
976
|
device.exposurePointOfInterest = point
|
|
919
|
-
|
|
977
|
+
|
|
978
|
+
// Use autoExpose for specific point exposure
|
|
979
|
+
if device.isExposureModeSupported(.autoExpose) {
|
|
980
|
+
device.exposureMode = .autoExpose
|
|
981
|
+
} else if device.isExposureModeSupported(.continuousAutoExposure) {
|
|
982
|
+
device.exposureMode = .continuousAutoExposure
|
|
983
|
+
}
|
|
920
984
|
}
|
|
921
985
|
|
|
922
|
-
// Ensure full focus range is available
|
|
986
|
+
// Ensure full focus range is available for close objects
|
|
923
987
|
if device.isAutoFocusRangeRestrictionSupported {
|
|
924
988
|
device.autoFocusRangeRestriction = .none
|
|
925
989
|
}
|
|
926
990
|
|
|
927
|
-
device.
|
|
991
|
+
device.isSubjectAreaChangeMonitoringEnabled = true
|
|
928
992
|
|
|
929
|
-
|
|
930
|
-
continuousFocusReturnTimer?.invalidate()
|
|
993
|
+
device.unlockForConfiguration()
|
|
931
994
|
|
|
932
|
-
//
|
|
933
|
-
|
|
934
|
-
continuousFocusReturnTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { [weak self] _ in
|
|
995
|
+
// Switch back to continuous focus after a delay to maintain automatic focus
|
|
996
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
|
|
935
997
|
self?.returnToContinuousFocus()
|
|
936
998
|
}
|
|
937
999
|
|
|
1000
|
+
// focusCompletionTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { [weak self] _ in
|
|
1001
|
+
// DispatchQueue.main.async {
|
|
1002
|
+
// self?.hideFocusIndicatorWithCompletion()
|
|
1003
|
+
// }
|
|
1004
|
+
// }
|
|
1005
|
+
|
|
938
1006
|
} catch {
|
|
939
1007
|
print("Could not focus: \(error.localizedDescription)")
|
|
940
1008
|
}
|
|
941
1009
|
}
|
|
942
1010
|
|
|
1011
|
+
// @objc private func subjectAreaDidChange(notification: NSNotification) {
|
|
1012
|
+
// DispatchQueue.main.async { [weak self] in
|
|
1013
|
+
// self?.hideFocusIndicatorWithCompletion()
|
|
1014
|
+
// }
|
|
1015
|
+
|
|
1016
|
+
// NotificationCenter.default.removeObserver(self, name: .AVCaptureDeviceSubjectAreaDidChange, object: notification.object)
|
|
1017
|
+
// }
|
|
1018
|
+
|
|
943
1019
|
private func returnToContinuousFocus() {
|
|
944
1020
|
guard let videoInput = self.videoInput else { return }
|
|
945
1021
|
let device = videoInput.device
|
|
946
1022
|
do {
|
|
947
1023
|
try device.lockForConfiguration()
|
|
948
1024
|
|
|
1025
|
+
// Return to continuous auto focus for automatic operation
|
|
949
1026
|
if device.isFocusModeSupported(.continuousAutoFocus) {
|
|
950
1027
|
device.focusMode = .continuousAutoFocus
|
|
951
1028
|
}
|
|
952
1029
|
|
|
1030
|
+
// Return to continuous auto exposure
|
|
953
1031
|
if device.isExposureModeSupported(.continuousAutoExposure) {
|
|
954
1032
|
device.exposureMode = .continuousAutoExposure
|
|
955
1033
|
}
|
|
@@ -960,6 +1038,22 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
|
|
|
960
1038
|
}
|
|
961
1039
|
}
|
|
962
1040
|
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
// private func hideFocusIndicatorWithCompletion() {
|
|
1044
|
+
// DispatchQueue.main.async { [weak self] in
|
|
1045
|
+
// guard let self = self, let focusView = self.focusView else { return }
|
|
1046
|
+
|
|
1047
|
+
// UIView.animate(withDuration: 0.2, animations: {
|
|
1048
|
+
// focusView.alpha = 0.0
|
|
1049
|
+
// focusView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
|
|
1050
|
+
// }) { _ in
|
|
1051
|
+
// focusView.removeFromSuperview()
|
|
1052
|
+
// focusView.transform = CGAffineTransform.identity
|
|
1053
|
+
// self.isFocusAnimating = false
|
|
1054
|
+
// }
|
|
1055
|
+
// }
|
|
1056
|
+
// }
|
|
963
1057
|
@objc func requestCameraPermission(_ call: CAPPluginCall) {
|
|
964
1058
|
AVCaptureDevice.requestAccess(for: .video) { granted in
|
|
965
1059
|
DispatchQueue.main.async {
|
|
@@ -1075,59 +1169,14 @@ public class CameraPreviewPlugin: CAPPlugin, AVCaptureVideoDataOutputSampleBuffe
|
|
|
1075
1169
|
}
|
|
1076
1170
|
|
|
1077
1171
|
@objc func takePhoto(_ call: CAPPluginCall) {
|
|
1172
|
+
call.keepAlive = true
|
|
1078
1173
|
takePhotoCall = call
|
|
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
|
-
}
|
|
1174
|
+
takePhotoWithAVFoundation()
|
|
1126
1175
|
}
|
|
1127
1176
|
|
|
1128
1177
|
deinit {
|
|
1178
|
+
NotificationCenter.default.removeObserver(self)
|
|
1129
1179
|
focusCompletionTimer?.invalidate()
|
|
1130
|
-
continuousFocusReturnTimer?.invalidate()
|
|
1131
1180
|
}
|
|
1132
1181
|
|
|
1133
1182
|
}
|
package/package.json
CHANGED