@capgo/camera-preview 7.21.10 → 7.22.0
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.
- package/README.md +74 -0
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +220 -2
- package/dist/docs.json +181 -0
- package/dist/esm/definitions.d.ts +31 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +6 -1
- package/dist/esm/web.js +71 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +71 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +71 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +205 -21
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import Capacitor
|
|
|
5
5
|
import CoreImage
|
|
6
6
|
import CoreLocation
|
|
7
7
|
import MobileCoreServices
|
|
8
|
+
import UIKit
|
|
8
9
|
|
|
9
10
|
extension UIWindow {
|
|
10
11
|
static var isLandscape: Bool {
|
|
@@ -66,6 +67,8 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
66
67
|
CAPPluginMethod(name: "deleteFile", returnType: CAPPluginReturnPromise),
|
|
67
68
|
CAPPluginMethod(name: "getOrientation", returnType: CAPPluginReturnPromise),
|
|
68
69
|
CAPPluginMethod(name: "getSafeAreaInsets", returnType: CAPPluginReturnPromise),
|
|
70
|
+
CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
|
|
71
|
+
CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
|
|
69
72
|
// Exposure control methods
|
|
70
73
|
CAPPluginMethod(name: "getExposureModes", returnType: CAPPluginReturnPromise),
|
|
71
74
|
CAPPluginMethod(name: "getExposureMode", returnType: CAPPluginReturnPromise),
|
|
@@ -100,6 +103,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
100
103
|
private var positioning: String = "center"
|
|
101
104
|
private var permissionCallID: String?
|
|
102
105
|
private var waitingForLocation: Bool = false
|
|
106
|
+
private var isPresentingPermissionAlert: Bool = false
|
|
103
107
|
|
|
104
108
|
// MARK: - Helper Methods for Aspect Ratio
|
|
105
109
|
|
|
@@ -165,6 +169,71 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
165
169
|
}
|
|
166
170
|
}
|
|
167
171
|
|
|
172
|
+
private func presentCameraPermissionAlert(title: String,
|
|
173
|
+
message: String,
|
|
174
|
+
openSettingsText: String,
|
|
175
|
+
cancelText: String,
|
|
176
|
+
completion: (() -> Void)? = nil) {
|
|
177
|
+
DispatchQueue.main.async {
|
|
178
|
+
guard let viewController = self.bridge?.viewController else {
|
|
179
|
+
completion?()
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if self.isPresentingPermissionAlert {
|
|
184
|
+
completion?()
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let alert = UIAlertController(title: title,
|
|
189
|
+
message: message,
|
|
190
|
+
preferredStyle: .alert)
|
|
191
|
+
|
|
192
|
+
let cancelAction = UIAlertAction(title: cancelText, style: .cancel) { _ in
|
|
193
|
+
self.isPresentingPermissionAlert = false
|
|
194
|
+
}
|
|
195
|
+
alert.addAction(cancelAction)
|
|
196
|
+
|
|
197
|
+
let openSettingsAction = UIAlertAction(title: openSettingsText, style: .default) { _ in
|
|
198
|
+
self.isPresentingPermissionAlert = false
|
|
199
|
+
guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
|
|
200
|
+
UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)
|
|
201
|
+
}
|
|
202
|
+
alert.addAction(openSettingsAction)
|
|
203
|
+
|
|
204
|
+
self.isPresentingPermissionAlert = true
|
|
205
|
+
viewController.present(alert, animated: true) {
|
|
206
|
+
completion?()
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private func mapAuthorizationStatus(_ status: AVAuthorizationStatus) -> String {
|
|
212
|
+
switch status {
|
|
213
|
+
case .authorized:
|
|
214
|
+
return "granted"
|
|
215
|
+
case .denied, .restricted:
|
|
216
|
+
return "denied"
|
|
217
|
+
case .notDetermined:
|
|
218
|
+
fallthrough
|
|
219
|
+
@unknown default:
|
|
220
|
+
return "prompt"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private func mapAudioPermission(_ permission: AVAudioSession.RecordPermission) -> String {
|
|
225
|
+
switch permission {
|
|
226
|
+
case .granted:
|
|
227
|
+
return "granted"
|
|
228
|
+
case .denied:
|
|
229
|
+
return "denied"
|
|
230
|
+
case .undetermined:
|
|
231
|
+
fallthrough
|
|
232
|
+
@unknown default:
|
|
233
|
+
return "prompt"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
168
237
|
@objc func getZoomButtonValues(_ call: CAPPluginCall) {
|
|
169
238
|
guard isInitialized else {
|
|
170
239
|
call.reject("Camera not initialized")
|
|
@@ -582,35 +651,62 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
582
651
|
call.reject("Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start.")
|
|
583
652
|
return
|
|
584
653
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
654
|
+
let beginStart: () -> Void = {
|
|
655
|
+
if self.cameraController.captureSession?.isRunning ?? false {
|
|
656
|
+
DispatchQueue.main.async {
|
|
657
|
+
self.isInitializing = false
|
|
658
|
+
call.reject("camera already started")
|
|
659
|
+
}
|
|
590
660
|
return
|
|
591
661
|
}
|
|
592
662
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
print(error)
|
|
663
|
+
self.cameraController.prepare(cameraPosition: self.cameraPosition, deviceId: deviceId, disableAudio: self.disableAudio, cameraMode: cameraMode, aspectRatio: self.aspectRatio, initialZoomLevel: initialZoomLevel, disableFocusIndicator: self.disableFocusIndicator) { error in
|
|
664
|
+
if let error = error {
|
|
665
|
+
print(error)
|
|
666
|
+
DispatchQueue.main.async {
|
|
667
|
+
self.isInitializing = false
|
|
599
668
|
call.reject(error.localizedDescription)
|
|
600
|
-
return
|
|
601
669
|
}
|
|
670
|
+
return
|
|
671
|
+
}
|
|
602
672
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
673
|
+
DispatchQueue.main.async {
|
|
674
|
+
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
|
|
675
|
+
NotificationCenter.default.addObserver(self,
|
|
676
|
+
selector: #selector(self.handleOrientationChange),
|
|
677
|
+
name: UIDevice.orientationDidChangeNotification,
|
|
678
|
+
object: nil)
|
|
679
|
+
self.completeStartCamera(call: call)
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
let handleDenied: (AVAuthorizationStatus) -> Void = { _ in
|
|
685
|
+
DispatchQueue.main.async {
|
|
686
|
+
self.isInitializing = false
|
|
687
|
+
call.reject("camera permission denied. enable camera access in Settings.", "cameraPermissionDenied")
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
let authorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
|
|
692
|
+
|
|
693
|
+
switch authorizationStatus {
|
|
694
|
+
case .authorized:
|
|
695
|
+
beginStart()
|
|
696
|
+
case .notDetermined:
|
|
697
|
+
AVCaptureDevice.requestAccess(for: .video) { granted in
|
|
698
|
+
if granted {
|
|
699
|
+
beginStart()
|
|
700
|
+
} else {
|
|
701
|
+
let currentStatus = AVCaptureDevice.authorizationStatus(for: .video)
|
|
702
|
+
handleDenied(currentStatus)
|
|
611
703
|
}
|
|
612
704
|
}
|
|
613
|
-
|
|
705
|
+
case .denied, .restricted:
|
|
706
|
+
handleDenied(authorizationStatus)
|
|
707
|
+
@unknown default:
|
|
708
|
+
handleDenied(authorizationStatus)
|
|
709
|
+
}
|
|
614
710
|
}
|
|
615
711
|
|
|
616
712
|
private func completeStartCamera(call: CAPPluginCall) {
|
|
@@ -768,6 +864,94 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
|
|
|
768
864
|
call.resolve()
|
|
769
865
|
}
|
|
770
866
|
}
|
|
867
|
+
|
|
868
|
+
override public func checkPermissions(_ call: CAPPluginCall) {
|
|
869
|
+
let disableAudio = call.getBool("disableAudio") ?? true
|
|
870
|
+
let cameraStatus = self.mapAuthorizationStatus(AVCaptureDevice.authorizationStatus(for: .video))
|
|
871
|
+
|
|
872
|
+
var result: [String: Any] = [
|
|
873
|
+
"camera": cameraStatus
|
|
874
|
+
]
|
|
875
|
+
|
|
876
|
+
if disableAudio == false {
|
|
877
|
+
let audioPermission = AVAudioSession.sharedInstance().recordPermission
|
|
878
|
+
result["microphone"] = self.mapAudioPermission(audioPermission)
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
call.resolve(result)
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
override public func requestPermissions(_ call: CAPPluginCall) {
|
|
885
|
+
let disableAudio = call.getBool("disableAudio") ?? true
|
|
886
|
+
self.disableAudio = disableAudio
|
|
887
|
+
|
|
888
|
+
let title = call.getString("title") ?? "Camera Permission Needed"
|
|
889
|
+
let message = call.getString("message") ?? "Enable camera access in Settings to use the preview."
|
|
890
|
+
let openSettingsText = call.getString("openSettingsButtonTitle") ?? "Open Settings"
|
|
891
|
+
let cancelText = call.getString("cancelButtonTitle") ?? "Cancel"
|
|
892
|
+
let showSettingsAlert = call.getBool("showSettingsAlert") ?? false
|
|
893
|
+
|
|
894
|
+
var currentCameraStatus = AVCaptureDevice.authorizationStatus(for: .video)
|
|
895
|
+
let audioSession = AVAudioSession.sharedInstance()
|
|
896
|
+
var currentAudioStatus: AVAudioSession.RecordPermission? = disableAudio ? nil : audioSession.recordPermission
|
|
897
|
+
|
|
898
|
+
let dispatchGroup = DispatchGroup()
|
|
899
|
+
var pendingRequests = 0
|
|
900
|
+
|
|
901
|
+
if currentCameraStatus == .notDetermined {
|
|
902
|
+
pendingRequests += 1
|
|
903
|
+
dispatchGroup.enter()
|
|
904
|
+
AVCaptureDevice.requestAccess(for: .video) { granted in
|
|
905
|
+
currentCameraStatus = granted ? .authorized : .denied
|
|
906
|
+
dispatchGroup.leave()
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if let audioStatus = currentAudioStatus,
|
|
911
|
+
audioStatus == .undetermined {
|
|
912
|
+
pendingRequests += 1
|
|
913
|
+
dispatchGroup.enter()
|
|
914
|
+
audioSession.requestRecordPermission { granted in
|
|
915
|
+
currentAudioStatus = granted ? .granted : .denied
|
|
916
|
+
dispatchGroup.leave()
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
let finalizeResponse: () -> Void = { [weak self] in
|
|
921
|
+
guard let self = self else { return }
|
|
922
|
+
|
|
923
|
+
let cameraResult = self.mapAuthorizationStatus(currentCameraStatus)
|
|
924
|
+
var result: [String: Any] = [
|
|
925
|
+
"camera": cameraResult
|
|
926
|
+
]
|
|
927
|
+
|
|
928
|
+
if let audioStatus = currentAudioStatus {
|
|
929
|
+
result["microphone"] = self.mapAudioPermission(audioStatus)
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
let shouldShowAlert = showSettingsAlert &&
|
|
933
|
+
(cameraResult == "denied" ||
|
|
934
|
+
((result["microphone"] as? String) == "denied"))
|
|
935
|
+
|
|
936
|
+
guard shouldShowAlert else {
|
|
937
|
+
call.resolve(result)
|
|
938
|
+
return
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
self.presentCameraPermissionAlert(title: title,
|
|
942
|
+
message: message,
|
|
943
|
+
openSettingsText: openSettingsText,
|
|
944
|
+
cancelText: cancelText) {
|
|
945
|
+
call.resolve(result)
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if pendingRequests == 0 {
|
|
950
|
+
DispatchQueue.main.async(execute: finalizeResponse)
|
|
951
|
+
} else {
|
|
952
|
+
dispatchGroup.notify(queue: .main, execute: finalizeResponse)
|
|
953
|
+
}
|
|
954
|
+
}
|
|
771
955
|
// Get user's cache directory path
|
|
772
956
|
@objc func getTempFilePath() -> URL {
|
|
773
957
|
let path = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|