@scr2em/capacitor-scanner 6.0.3 → 6.0.4

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.
@@ -7,340 +7,358 @@ import Foundation
7
7
  * here: https://capacitorjs.com/docs/plugins/ios
8
8
  */
9
9
  struct VoteStatus {
10
- var votes: Int
11
- var done: Bool
10
+ var votes: Int
11
+ var done: Bool
12
12
  }
13
13
 
14
14
  @objc(CapacitorScannerPlugin)
15
15
  public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetadataOutputObjectsDelegate, AVCapturePhotoCaptureDelegate {
16
- public let identifier = "CapacitorScannerPlugin"
17
- public let jsName = "CapacitorScanner"
18
- public let pluginMethods: [CAPPluginMethod] = [
19
- CAPPluginMethod(name: "startScanning", returnType: CAPPluginReturnPromise),
20
- CAPPluginMethod(name: "stopScanning", returnType: CAPPluginReturnPromise),
21
- CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
22
- CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
23
- CAPPluginMethod(name: "openSettings", returnType: CAPPluginReturnPromise),
24
- CAPPluginMethod(name: "capturePhoto", returnType: CAPPluginReturnPromise)
25
- ]
26
-
27
- private var captureSession: AVCaptureSession?
28
- private var cameraView: UIView?
29
- private var previewLayer: AVCaptureVideoPreviewLayer?
30
-
31
- private let voteThreshold = 5
32
- private var scannedCodesVotes: [String: VoteStatus] = [:]
33
-
34
- // for capturing images
35
- private var photoOutput: AVCapturePhotoOutput?
36
- private var capturePhotoCall: CAPPluginCall?
37
-
38
- private var orientationObserver: NSObjectProtocol?
39
-
40
- @objc func capturePhoto(_ call: CAPPluginCall) {
41
- guard let photoOutput = self.photoOutput, captureSession?.isRunning == true else {
42
- call.reject("Camera is not set up or running")
43
- return
44
- }
45
-
46
- let photoSettings = AVCapturePhotoSettings()
47
- self.capturePhotoCall = call
48
- photoOutput.capturePhoto(with: photoSettings, delegate: self)
49
- }
50
-
51
- public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
52
- if let captureError = error {
53
- self.capturePhotoCall?.reject("Photo capture failed: \(captureError.localizedDescription)")
54
- return
55
- }
56
-
57
- guard let imageData = photo.fileDataRepresentation() else {
58
- self.capturePhotoCall?.reject("Unable to get image data")
59
- return
60
- }
61
-
62
- let base64String = imageData.base64EncodedString()
63
- self.capturePhotoCall?.resolve(["imageBase64": "data:image/jpeg;base64, \(base64String)"])
64
- }
65
-
66
- @objc func startScanning(_ call: CAPPluginCall) {
67
- self.stopScanning(call)
68
-
69
- DispatchQueue.main.async {
70
- let captureSession = AVCaptureSession()
71
- captureSession.sessionPreset = AVCaptureSession.Preset.hd1280x720
72
-
73
- let cameraDirection: AVCaptureDevice.Position = call.getString("cameraDirection", "BACK") == "BACK" ? .back : .front
74
-
75
- guard let videoCaptureDevice = self.getCaptureDevice(position: cameraDirection) else {
76
- call.reject("Unable to access the camera")
77
- return
78
- }
79
-
80
- let videoInput: AVCaptureDeviceInput
81
-
82
- do {
83
- videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
84
- } catch {
85
- call.reject("Unable to initialize video input")
86
- return
87
- }
88
-
89
- if captureSession.canAddInput(videoInput) {
90
- captureSession.addInput(videoInput)
91
- } else {
92
- call.reject("Unable to add video input to capture session")
93
- return
94
- }
95
-
96
- let metadataOutput = AVCaptureMetadataOutput()
97
-
98
- if captureSession.canAddOutput(metadataOutput) {
99
- captureSession.addOutput(metadataOutput)
100
-
101
- metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
102
-
103
- let formats = call.getArray("formats", String.self) ?? []
104
- let metadataObjectTypes = self.getMetadataObjectTypes(from: formats)
105
- metadataOutput.metadataObjectTypes = metadataObjectTypes
106
- } else {
107
- call.reject("Unable to add metadata output to capture session")
108
- return
109
- }
110
-
111
- // Add photo output
112
- let photoOutput = AVCapturePhotoOutput()
113
- self.photoOutput = photoOutput
114
- if captureSession.canAddOutput(photoOutput) {
115
- captureSession.addOutput(photoOutput)
116
- } else {
117
- call.reject("Unable to add photo output to capture session")
118
- return
119
- }
120
-
121
- self.hideWebViewBackground()
122
-
123
- if let webView = self.webView, let superView = webView.superview {
124
- let cameraView = UIView(frame: superView.bounds)
125
- let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
126
- previewLayer.videoGravity = .resizeAspectFill
127
- previewLayer.frame = superView.bounds
128
- cameraView.layer.addSublayer(previewLayer)
129
-
130
- webView.superview?.insertSubview(cameraView, belowSubview: webView)
131
-
132
- self.captureSession = captureSession
133
- self.cameraView = cameraView
134
- self.previewLayer = previewLayer
135
-
136
- // Add orientation change observer
137
- self.addOrientationChangeObserver()
138
-
139
- // Initial orientation setup
140
- self.updatePreviewOrientation()
141
-
142
- DispatchQueue.global(qos: .background).async {
143
- captureSession.startRunning()
144
- call.resolve()
145
- }
146
-
147
- } else {
148
- call.reject("unknown")
149
- }
150
- }
151
- }
152
-
153
- @objc func stopScanning(_ call: CAPPluginCall) {
154
- DispatchQueue.main.async {
155
- if let captureSession = self.captureSession {
156
- captureSession.stopRunning()
157
- }
158
-
159
- self.previewLayer?.removeFromSuperlayer()
160
- self.cameraView?.removeFromSuperview()
161
-
162
- self.captureSession = nil
163
- self.cameraView = nil
164
- self.previewLayer = nil
165
- self.scannedCodesVotes = [:]
166
- self.showWebViewBackground()
167
-
168
- self.removeOrientationChangeObserver()
169
-
170
- call.resolve()
171
- }
172
- }
173
-
174
- private func addOrientationChangeObserver() {
175
- self.orientationObserver = NotificationCenter.default.addObserver(
176
- forName: UIDevice.orientationDidChangeNotification,
177
- object: nil,
178
- queue: .main
179
- ) { [weak self] _ in
180
- self?.updatePreviewOrientation()
181
- }
182
- }
183
-
184
- private func removeOrientationChangeObserver() {
185
- if let observer = self.orientationObserver {
186
- NotificationCenter.default.removeObserver(observer)
187
- self.orientationObserver = nil
188
- }
189
- }
190
-
191
- private func updatePreviewOrientation() {
192
- guard let previewLayer = self.previewLayer,
193
- let connection = previewLayer.connection,
194
- let cameraView = self.cameraView
195
- else {
196
- return
197
- }
198
-
199
- let deviceOrientation = UIDevice.current.orientation
200
-
201
- if deviceOrientation.isFlat == true || deviceOrientation == .portraitUpsideDown {
202
- return
203
- }
204
-
205
- let newOrientation: AVCaptureVideoOrientation
206
-
207
- switch deviceOrientation {
208
- case .landscapeLeft:
209
- newOrientation = .landscapeRight
210
- case .landscapeRight:
211
- newOrientation = .landscapeLeft
212
- default:
213
- newOrientation = .portrait
214
- }
215
-
216
- connection.videoOrientation = newOrientation
217
-
218
- // Update camera view and preview layer frames
219
- let screenBounds = UIScreen.main.bounds
220
- let screenWidth = screenBounds.width
221
- let screenHeight = screenBounds.height
222
-
223
- // Determine the correct dimensions based on orientation
224
- let width: CGFloat
225
- let height: CGFloat
226
- if newOrientation == .portrait {
227
- width = min(screenWidth, screenHeight)
228
- height = max(screenWidth, screenHeight)
229
- } else {
230
- width = max(screenWidth, screenHeight)
231
- height = min(screenWidth, screenHeight)
232
- }
233
-
234
- // Update frames
235
- cameraView.frame = CGRect(x: 0, y: 0, width: width, height: height)
236
- previewLayer.frame = cameraView.bounds
237
- }
238
-
239
- public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
240
- guard let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
241
- let stringValue = metadataObject.stringValue
242
- else {
243
- return
244
- }
245
-
246
- /*
247
- this is a voting system to
248
- 1. avoid scanning the same code
249
- 2. avoid scanning any code that doesn't appear for at least in 10 frames of the video stream to reduce the number of false positives
250
- */
251
-
252
- var voteStatus = self.scannedCodesVotes[stringValue] ?? VoteStatus(votes: 0, done: false)
253
-
254
- if !voteStatus.done {
255
- voteStatus.votes += 1
256
-
257
- if voteStatus.votes >= self.voteThreshold {
258
- voteStatus.done = true
259
-
260
- self.notifyListeners("barcodeScanned", data: [
261
- "scannedCode": stringValue,
262
- "format": CapacitorScannerHelpers.convertBarcodeScannerFormatToString(metadataObject.type)
263
- ])
264
- }
265
- }
266
-
267
- self.scannedCodesVotes[stringValue] = voteStatus
268
- }
269
-
270
- private func getCaptureDevice(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
271
- let discoverySession = AVCaptureDevice.DiscoverySession(
272
- deviceTypes: [.builtInWideAngleCamera],
273
- mediaType: .video,
274
- position: position
275
- )
276
- return discoverySession.devices.first
277
- }
278
-
279
- private func getMetadataObjectTypes(from formats: [String]) -> [BarcodeFormat] {
280
- if formats.isEmpty {
281
- return CapacitorScannerHelpers.getAllSupportedFormats()
282
- }
283
-
284
- return formats.compactMap { format in
285
- CapacitorScannerHelpers.convertStringToBarcodeScannerFormat(format)
286
- }
287
- }
288
-
289
- /**
290
- * Must run on UI thread.
291
- */
292
- private func hideWebViewBackground() {
293
- guard let webView = self.webView else {
294
- return
295
- }
296
- webView.isOpaque = false
297
- webView.backgroundColor = UIColor.clear
298
- webView.scrollView.backgroundColor = UIColor.clear
299
- }
300
-
301
- /**
302
- * Must run on UI thread.
303
- */
304
- private func showWebViewBackground() {
305
- guard let webView = self.webView else {
306
- return
307
- }
308
- webView.isOpaque = true
309
- webView.backgroundColor = UIColor.white
310
- webView.scrollView.backgroundColor = UIColor.white
311
- }
312
-
313
- @objc override public func checkPermissions(_ call: CAPPluginCall) {
314
- let status = AVCaptureDevice.authorizationStatus(for: .video)
315
-
316
- var stringStatus = "prompt"
317
-
318
- if status == .denied || status == .restricted {
319
- stringStatus = "denied"
320
- }
321
-
322
- if status == .authorized {
323
- stringStatus = "granted"
324
- }
325
-
326
- call.resolve(["camera": stringStatus])
327
- }
328
-
329
- @objc override public func requestPermissions(_ call: CAPPluginCall) {
330
- AVCaptureDevice.requestAccess(for: .video) { _ in
331
- self.checkPermissions(call)
332
- }
333
- }
334
-
335
- @objc func openSettings(_ call: CAPPluginCall) {
336
- let url = URL(string: UIApplication.openSettingsURLString)
337
- DispatchQueue.main.async {
338
- if let url = url, UIApplication.shared.canOpenURL(url) {
339
- UIApplication.shared.open(url)
340
- call.resolve()
341
- } else {
342
- call.reject("unknown")
343
- }
344
- }
345
- }
16
+ public let identifier = "CapacitorScannerPlugin"
17
+ public let jsName = "CapacitorScanner"
18
+ public let pluginMethods: [CAPPluginMethod] = [
19
+ CAPPluginMethod(name: "startScanning", returnType: CAPPluginReturnPromise),
20
+ CAPPluginMethod(name: "stopScanning", returnType: CAPPluginReturnPromise),
21
+ CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
22
+ CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
23
+ CAPPluginMethod(name: "openSettings", returnType: CAPPluginReturnPromise),
24
+ CAPPluginMethod(name: "capturePhoto", returnType: CAPPluginReturnPromise)
25
+ ]
26
+
27
+ private var captureSession: AVCaptureSession?
28
+ private var cameraView: UIView?
29
+ private var previewLayer: AVCaptureVideoPreviewLayer?
30
+
31
+ private let voteThreshold = 5
32
+ private var scannedCodesVotes: [String: VoteStatus] = [:]
33
+
34
+ // for capturing images
35
+ private var photoOutput: AVCapturePhotoOutput?
36
+ private var capturePhotoCall: CAPPluginCall?
37
+
38
+ private var orientationObserver: NSObjectProtocol?
39
+
40
+ @objc func capturePhoto(_ call: CAPPluginCall) {
41
+ guard let photoOutput = self.photoOutput, captureSession?.isRunning == true else {
42
+ call.reject("Camera is not set up or running")
43
+ return
44
+ }
45
+
46
+ let photoSettings = AVCapturePhotoSettings()
47
+ self.capturePhotoCall = call
48
+ photoOutput.capturePhoto(with: photoSettings, delegate: self)
49
+ }
50
+
51
+ public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
52
+ if let captureError = error {
53
+ self.capturePhotoCall?.reject("Photo capture failed: \(captureError.localizedDescription)")
54
+ return
55
+ }
56
+
57
+ guard let imageData = photo.fileDataRepresentation() else {
58
+ self.capturePhotoCall?.reject("Unable to get image data")
59
+ return
60
+ }
61
+
62
+ let base64String = imageData.base64EncodedString()
63
+ self.capturePhotoCall?.resolve(["imageBase64": "data:image/jpeg;base64, \(base64String)"])
64
+ }
65
+
66
+ @objc func startScanning(_ call: CAPPluginCall) {
67
+ self.stopScanning(call)
68
+
69
+ DispatchQueue.main.async {
70
+ let captureSession = AVCaptureSession()
71
+
72
+ // Always check if the device supports 4K first
73
+ if captureSession.canSetSessionPreset(.hd4K3840x2160) {
74
+ captureSession.sessionPreset = .hd4K3840x2160
75
+ } else if captureSession.canSetSessionPreset(.hd1920x1080) {
76
+ captureSession.sessionPreset = .hd1920x1080
77
+ } else {
78
+ captureSession.sessionPreset = .hd1280x720
79
+ }
80
+
81
+ captureSession.sessionPreset = AVCaptureSession.Preset.hd4K3840x2160
82
+
83
+ let cameraDirection: AVCaptureDevice.Position = call.getString("cameraDirection", "BACK") == "BACK" ? .back : .front
84
+
85
+ guard let videoCaptureDevice = self.getCaptureDevice(position: cameraDirection) else {
86
+ call.reject("Unable to access the camera")
87
+ return
88
+ }
89
+
90
+ let videoInput: AVCaptureDeviceInput
91
+
92
+ do {
93
+ videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
94
+ } catch {
95
+ call.reject("Unable to initialize video input")
96
+ return
97
+ }
98
+
99
+ if captureSession.canAddInput(videoInput) {
100
+ captureSession.addInput(videoInput)
101
+ } else {
102
+ call.reject("Unable to add video input to capture session")
103
+ return
104
+ }
105
+
106
+ let metadataOutput = AVCaptureMetadataOutput()
107
+
108
+ if captureSession.canAddOutput(metadataOutput) {
109
+ captureSession.addOutput(metadataOutput)
110
+
111
+ metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
112
+
113
+ let formats = call.getArray("formats", String.self) ?? []
114
+ let metadataObjectTypes = self.getMetadataObjectTypes(from: formats)
115
+ metadataOutput.metadataObjectTypes = metadataObjectTypes
116
+ } else {
117
+ call.reject("Unable to add metadata output to capture session")
118
+ return
119
+ }
120
+
121
+ // Add photo output
122
+ let photoOutput = AVCapturePhotoOutput()
123
+ self.photoOutput = photoOutput
124
+ if captureSession.canAddOutput(photoOutput) {
125
+ captureSession.addOutput(photoOutput)
126
+ } else {
127
+ call.reject("Unable to add photo output to capture session")
128
+ return
129
+ }
130
+
131
+ self.hideWebViewBackground()
132
+
133
+ if let webView = self.webView, let superView = webView.superview {
134
+ let cameraView = UIView(frame: superView.bounds)
135
+ let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
136
+ previewLayer.videoGravity = .resizeAspectFill
137
+ previewLayer.frame = superView.bounds
138
+ cameraView.layer.addSublayer(previewLayer)
139
+
140
+ webView.superview?.insertSubview(cameraView, belowSubview: webView)
141
+
142
+ self.captureSession = captureSession
143
+ self.cameraView = cameraView
144
+ self.previewLayer = previewLayer
145
+
146
+ // Add orientation change observer
147
+ self.addOrientationChangeObserver()
148
+
149
+ // Initial orientation setup
150
+ self.updatePreviewOrientation()
151
+
152
+ DispatchQueue.global(qos: .background).async {
153
+ captureSession.startRunning()
154
+ call.resolve()
155
+ }
156
+
157
+ } else {
158
+ call.reject("unknown")
159
+ }
160
+ }
161
+ }
162
+
163
+ @objc func stopScanning(_ call: CAPPluginCall) {
164
+ DispatchQueue.main.async {
165
+ if let captureSession = self.captureSession {
166
+ captureSession.stopRunning()
167
+ }
168
+
169
+ self.previewLayer?.removeFromSuperlayer()
170
+ self.cameraView?.removeFromSuperview()
171
+
172
+ self.captureSession = nil
173
+ self.cameraView = nil
174
+ self.previewLayer = nil
175
+ self.scannedCodesVotes = [:]
176
+ self.showWebViewBackground()
177
+
178
+ self.removeOrientationChangeObserver()
179
+
180
+ call.resolve()
181
+ }
182
+ }
183
+
184
+ private func addOrientationChangeObserver() {
185
+ self.orientationObserver = NotificationCenter.default.addObserver(
186
+ forName: UIDevice.orientationDidChangeNotification,
187
+ object: nil,
188
+ queue: .main
189
+ ) { [weak self] _ in
190
+ self?.updatePreviewOrientation()
191
+ }
192
+ }
193
+
194
+ private func removeOrientationChangeObserver() {
195
+ if let observer = self.orientationObserver {
196
+ NotificationCenter.default.removeObserver(observer)
197
+ self.orientationObserver = nil
198
+ }
199
+ }
200
+
201
+ private func updatePreviewOrientation() {
202
+ guard let previewLayer = self.previewLayer,
203
+ let connection = previewLayer.connection,
204
+ let cameraView = self.cameraView
205
+ else {
206
+ return
207
+ }
208
+
209
+ let deviceOrientation = UIDevice.current.orientation
210
+
211
+ if deviceOrientation.isFlat == true || deviceOrientation == .portraitUpsideDown {
212
+ return
213
+ }
214
+
215
+ let newOrientation: AVCaptureVideoOrientation
216
+
217
+ switch deviceOrientation {
218
+ case .landscapeLeft:
219
+ newOrientation = .landscapeRight
220
+ case .landscapeRight:
221
+ newOrientation = .landscapeLeft
222
+ default:
223
+ newOrientation = .portrait
224
+ }
225
+
226
+ connection.videoOrientation = newOrientation
227
+
228
+ // Update camera view and preview layer frames
229
+ let screenBounds = UIScreen.main.bounds
230
+ let screenWidth = screenBounds.width
231
+ let screenHeight = screenBounds.height
232
+
233
+ // Determine the correct dimensions based on orientation
234
+ let width: CGFloat
235
+ let height: CGFloat
236
+ if newOrientation == .portrait {
237
+ width = min(screenWidth, screenHeight)
238
+ height = max(screenWidth, screenHeight)
239
+ } else {
240
+ width = max(screenWidth, screenHeight)
241
+ height = min(screenWidth, screenHeight)
242
+ }
243
+
244
+ // Update frames
245
+ cameraView.frame = CGRect(x: 0, y: 0, width: width, height: height)
246
+ previewLayer.frame = cameraView.bounds
247
+ }
248
+
249
+ public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
250
+ guard let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
251
+ let stringValue = metadataObject.stringValue
252
+ else {
253
+ return
254
+ }
255
+
256
+ /*
257
+ this is a voting system to
258
+ 1. avoid scanning the same code
259
+ 2. avoid scanning any code that doesn't appear for at least in 10 frames of the video stream to reduce the number of false positives
260
+ */
261
+
262
+ var voteStatus = self.scannedCodesVotes[stringValue] ?? VoteStatus(votes: 0, done: false)
263
+
264
+ if !voteStatus.done {
265
+ voteStatus.votes += 1
266
+
267
+ if voteStatus.votes >= self.voteThreshold {
268
+ voteStatus.done = true
269
+
270
+ self.notifyListeners("barcodeScanned", data: [
271
+ "scannedCode": stringValue,
272
+ "format": CapacitorScannerHelpers.convertBarcodeScannerFormatToString(metadataObject.type)
273
+ ])
274
+ }
275
+ }
276
+
277
+ self.scannedCodesVotes[stringValue] = voteStatus
278
+ }
279
+
280
+ private func getCaptureDevice(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
281
+ let discoverySession = AVCaptureDevice.DiscoverySession(
282
+ deviceTypes: [.builtInDualCamera, .builtInTripleCamera, .builtInWideAngleCamera],
283
+ mediaType: .video,
284
+ position: position
285
+ )
286
+ // Prioritize higher quality cameras first
287
+ if let device = discoverySession.devices.first(where: { $0.deviceType == .builtInTripleCamera }) ??
288
+ discoverySession.devices.first(where: { $0.deviceType == .builtInDualCamera }) ??
289
+ discoverySession.devices.first(where: { $0.deviceType == .builtInWideAngleCamera })
290
+ {
291
+ return device
292
+ }
293
+
294
+ return nil
295
+ }
296
+
297
+ private func getMetadataObjectTypes(from formats: [String]) -> [BarcodeFormat] {
298
+ if formats.isEmpty {
299
+ return CapacitorScannerHelpers.getAllSupportedFormats()
300
+ }
301
+
302
+ return formats.compactMap { format in
303
+ CapacitorScannerHelpers.convertStringToBarcodeScannerFormat(format)
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Must run on UI thread.
309
+ */
310
+ private func hideWebViewBackground() {
311
+ guard let webView = self.webView else {
312
+ return
313
+ }
314
+ webView.isOpaque = false
315
+ webView.backgroundColor = UIColor.clear
316
+ webView.scrollView.backgroundColor = UIColor.clear
317
+ }
318
+
319
+ /**
320
+ * Must run on UI thread.
321
+ */
322
+ private func showWebViewBackground() {
323
+ guard let webView = self.webView else {
324
+ return
325
+ }
326
+ webView.isOpaque = true
327
+ webView.backgroundColor = UIColor.white
328
+ webView.scrollView.backgroundColor = UIColor.white
329
+ }
330
+
331
+ @objc override public func checkPermissions(_ call: CAPPluginCall) {
332
+ let status = AVCaptureDevice.authorizationStatus(for: .video)
333
+
334
+ var stringStatus = "prompt"
335
+
336
+ if status == .denied || status == .restricted {
337
+ stringStatus = "denied"
338
+ }
339
+
340
+ if status == .authorized {
341
+ stringStatus = "granted"
342
+ }
343
+
344
+ call.resolve(["camera": stringStatus])
345
+ }
346
+
347
+ @objc override public func requestPermissions(_ call: CAPPluginCall) {
348
+ AVCaptureDevice.requestAccess(for: .video) { _ in
349
+ self.checkPermissions(call)
350
+ }
351
+ }
352
+
353
+ @objc func openSettings(_ call: CAPPluginCall) {
354
+ let url = URL(string: UIApplication.openSettingsURLString)
355
+ DispatchQueue.main.async {
356
+ if let url = url, UIApplication.shared.canOpenURL(url) {
357
+ UIApplication.shared.open(url)
358
+ call.resolve()
359
+ } else {
360
+ call.reject("unknown")
361
+ }
362
+ }
363
+ }
346
364
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scr2em/capacitor-scanner",
3
- "version": "6.0.3",
3
+ "version": "6.0.4",
4
4
  "description": "scan codes",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",