capacitor-camera-module 0.0.63 → 0.0.65
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.
|
@@ -5,29 +5,26 @@ import UIKit
|
|
|
5
5
|
import Photos
|
|
6
6
|
|
|
7
7
|
@objc(CameraModule)
|
|
8
|
-
public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOutputSampleBufferDelegate {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
]
|
|
30
|
-
|
|
8
|
+
public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoDataOutputSampleBufferDelegate {
|
|
9
|
+
|
|
10
|
+
public let identifier = "CameraModule"
|
|
11
|
+
public let jsName = "CameraModule"
|
|
12
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
13
|
+
CAPPluginMethod(name: "checkPermission", returnType: CAPPluginReturnPromise),
|
|
14
|
+
CAPPluginMethod(name: "requestPermission", returnType: CAPPluginReturnPromise),
|
|
15
|
+
CAPPluginMethod(name: "checkGalleryPermission", returnType: CAPPluginReturnPromise),
|
|
16
|
+
CAPPluginMethod(name: "requestGalleryPermission", returnType: CAPPluginReturnPromise),
|
|
17
|
+
CAPPluginMethod(name: "checkAndRequestGalleryPermission", returnType: CAPPluginReturnPromise),
|
|
18
|
+
CAPPluginMethod(name: "pickImageBase64", returnType: CAPPluginReturnPromise),
|
|
19
|
+
CAPPluginMethod(name: "startPreview", returnType: CAPPluginReturnPromise),
|
|
20
|
+
CAPPluginMethod(name: "stopPreview", returnType: CAPPluginReturnPromise),
|
|
21
|
+
CAPPluginMethod(name: "toggleFlash", returnType: CAPPluginReturnPromise),
|
|
22
|
+
CAPPluginMethod(name: "hasFlash", returnType: CAPPluginReturnPromise),
|
|
23
|
+
CAPPluginMethod(name: "takePhotoBase64", returnType: CAPPluginReturnPromise),
|
|
24
|
+
CAPPluginMethod(name: "startBarcodeScan", returnType: CAPPluginReturnPromise),
|
|
25
|
+
CAPPluginMethod(name: "stopBarcodeScan", returnType: CAPPluginReturnPromise),
|
|
26
|
+
CAPPluginMethod(name: "getLastGalleryImage", returnType: CAPPluginReturnPromise)
|
|
27
|
+
]
|
|
31
28
|
|
|
32
29
|
private var previewView: UIView?
|
|
33
30
|
private var captureSession: AVCaptureSession?
|
|
@@ -36,7 +33,6 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
36
33
|
private var photoDelegate: PhotoDelegate?
|
|
37
34
|
|
|
38
35
|
// MARK: - Barcode
|
|
39
|
-
|
|
40
36
|
private var isScanning = false
|
|
41
37
|
private var scanCall: CAPPluginCall?
|
|
42
38
|
private var barcodeRequest: VNDetectBarcodesRequest?
|
|
@@ -46,22 +42,14 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
46
42
|
private let scanDebounceInterval: TimeInterval = 0.5
|
|
47
43
|
|
|
48
44
|
// MARK: - Gallery
|
|
49
|
-
|
|
50
45
|
private var galleryCall: CAPPluginCall?
|
|
51
46
|
|
|
52
47
|
// MARK: - Lifecycle
|
|
53
48
|
public override func load() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
webView.isOpaque = true
|
|
57
|
-
webView.backgroundColor = .black
|
|
58
|
-
webView.scrollView.backgroundColor = .black
|
|
49
|
+
print("📱 CameraModulePlugin cargado")
|
|
59
50
|
}
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
|
|
63
52
|
// MARK: - Camera Permission
|
|
64
|
-
|
|
65
53
|
@objc func checkPermission(_ call: CAPPluginCall) {
|
|
66
54
|
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
|
67
55
|
call.resolve([
|
|
@@ -82,10 +70,8 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
82
70
|
}
|
|
83
71
|
|
|
84
72
|
// MARK: - Gallery Permission
|
|
85
|
-
|
|
86
73
|
@objc func checkGalleryPermission(_ call: CAPPluginCall) {
|
|
87
74
|
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
|
|
88
|
-
|
|
89
75
|
switch status {
|
|
90
76
|
case .authorized, .limited:
|
|
91
77
|
call.resolve(["granted": true, "status": "granted"])
|
|
@@ -118,7 +104,6 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
118
104
|
}
|
|
119
105
|
|
|
120
106
|
// MARK: - Pick Image
|
|
121
|
-
|
|
122
107
|
@objc func pickImageBase64(_ call: CAPPluginCall) {
|
|
123
108
|
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
|
|
124
109
|
guard status == .authorized || status == .limited else {
|
|
@@ -143,7 +128,6 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
143
128
|
}
|
|
144
129
|
|
|
145
130
|
// MARK: - Camera Preview
|
|
146
|
-
|
|
147
131
|
@objc func startPreview(_ call: CAPPluginCall) {
|
|
148
132
|
DispatchQueue.main.async {
|
|
149
133
|
if self.previewView != nil {
|
|
@@ -177,41 +161,42 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
177
161
|
return
|
|
178
162
|
}
|
|
179
163
|
|
|
180
|
-
|
|
181
|
-
let previewView = UIView()
|
|
182
|
-
previewView.translatesAutoresizingMaskIntoConstraints = false
|
|
183
|
-
previewView.backgroundColor = .black
|
|
184
|
-
previewView.isUserInteractionEnabled = true // CAMBIO IMPORTANTE
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
previewView.backgroundColor = .red // Para ver si la vista se muestra
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// Agrega esto después de crear la vista
|
|
191
|
-
print("PreviewView frame: \(previewView.frame)")
|
|
192
|
-
print("Superview: \(previewView.superview)")
|
|
193
|
-
|
|
194
|
-
guard let webView = self.bridge?.webView else {
|
|
164
|
+
guard let webView = self.bridge?.webView as? WKWebView else {
|
|
195
165
|
call.reject("No webView")
|
|
196
166
|
return
|
|
197
167
|
}
|
|
198
168
|
|
|
199
|
-
//
|
|
200
|
-
|
|
169
|
+
// Hacer el webView transparente
|
|
170
|
+
webView.isOpaque = false
|
|
171
|
+
webView.backgroundColor = .clear
|
|
172
|
+
webView.scrollView.backgroundColor = .clear
|
|
173
|
+
webView.scrollView.isOpaque = false
|
|
174
|
+
|
|
175
|
+
// Crear previewView
|
|
176
|
+
let previewView = UIView()
|
|
177
|
+
previewView.translatesAutoresizingMaskIntoConstraints = false
|
|
178
|
+
previewView.backgroundColor = .clear
|
|
179
|
+
previewView.isUserInteractionEnabled = false
|
|
201
180
|
|
|
202
|
-
//
|
|
203
|
-
|
|
181
|
+
// Insertar previewView DETRÁS de todo
|
|
182
|
+
container.insertSubview(previewView, at: 0)
|
|
183
|
+
|
|
184
|
+
// Configurar constraints para toda la pantalla
|
|
204
185
|
NSLayoutConstraint.activate([
|
|
205
|
-
previewView.topAnchor.constraint(equalTo:
|
|
206
|
-
previewView.bottomAnchor.constraint(equalTo:
|
|
186
|
+
previewView.topAnchor.constraint(equalTo: container.topAnchor),
|
|
187
|
+
previewView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
|
|
207
188
|
previewView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
|
|
208
189
|
previewView.trailingAnchor.constraint(equalTo: container.trailingAnchor)
|
|
209
190
|
])
|
|
210
191
|
|
|
211
|
-
//
|
|
192
|
+
// Asegurar orden de capas
|
|
193
|
+
previewView.layer.zPosition = -1
|
|
194
|
+
webView.layer.zPosition = 0
|
|
195
|
+
|
|
196
|
+
// Forzar layout
|
|
212
197
|
container.layoutIfNeeded()
|
|
213
198
|
|
|
214
|
-
//
|
|
199
|
+
// Crear previewLayer
|
|
215
200
|
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
|
|
216
201
|
previewLayer.videoGravity = .resizeAspectFill
|
|
217
202
|
previewLayer.frame = previewView.bounds
|
|
@@ -219,50 +204,52 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
219
204
|
|
|
220
205
|
previewView.layer.insertSublayer(previewLayer, at: 0)
|
|
221
206
|
|
|
222
|
-
//
|
|
207
|
+
// Guardar referencias
|
|
223
208
|
self.previewView = previewView
|
|
224
209
|
self.videoPreviewLayer = previewLayer
|
|
225
210
|
self.captureSession = session
|
|
226
211
|
|
|
227
|
-
//
|
|
212
|
+
// Configurar sesión
|
|
228
213
|
session.sessionPreset = .photo
|
|
229
214
|
|
|
230
|
-
//
|
|
215
|
+
// Iniciar sesión en background
|
|
231
216
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
232
217
|
session.startRunning()
|
|
233
|
-
|
|
234
|
-
// 9. Vuelve al main thread para verificar que se esté mostrando
|
|
235
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
236
|
-
print("Preview frame: \(previewView.frame)")
|
|
237
|
-
print("Preview layer frame: \(previewLayer.frame)")
|
|
238
|
-
print("Preview visible: \(previewView.window != nil)")
|
|
239
|
-
}
|
|
240
218
|
}
|
|
241
219
|
|
|
242
|
-
call.resolve(
|
|
220
|
+
call.resolve([
|
|
221
|
+
"success": true,
|
|
222
|
+
"message": "Preview de cámara iniciado"
|
|
223
|
+
])
|
|
243
224
|
}
|
|
244
225
|
}
|
|
245
226
|
|
|
246
|
-
|
|
247
227
|
@objc func stopPreview(_ call: CAPPluginCall) {
|
|
248
228
|
DispatchQueue.main.async {
|
|
229
|
+
// Detener cámara
|
|
249
230
|
self.captureSession?.stopRunning()
|
|
250
231
|
self.captureSession = nil
|
|
232
|
+
|
|
233
|
+
// Remover preview
|
|
251
234
|
self.videoPreviewLayer?.removeFromSuperlayer()
|
|
252
235
|
self.previewView?.removeFromSuperview()
|
|
253
236
|
self.videoPreviewLayer?.session = nil
|
|
254
|
-
|
|
255
237
|
self.videoPreviewLayer = nil
|
|
256
|
-
|
|
257
238
|
self.previewView = nil
|
|
239
|
+
|
|
240
|
+
// Restaurar webView a estado normal
|
|
241
|
+
if let webView = self.bridge?.webView as? WKWebView {
|
|
242
|
+
webView.isOpaque = true
|
|
243
|
+
webView.backgroundColor = .white
|
|
244
|
+
webView.scrollView.backgroundColor = .white
|
|
245
|
+
webView.scrollView.isOpaque = true
|
|
246
|
+
}
|
|
247
|
+
|
|
258
248
|
call.resolve()
|
|
259
249
|
}
|
|
260
250
|
}
|
|
261
251
|
|
|
262
|
-
|
|
263
|
-
|
|
264
252
|
// MARK: - Flash
|
|
265
|
-
|
|
266
253
|
@objc func toggleFlash(_ call: CAPPluginCall) {
|
|
267
254
|
guard let enable = call.getBool("enable"),
|
|
268
255
|
let device = AVCaptureDevice.default(for: .video),
|
|
@@ -288,53 +275,50 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
288
275
|
}
|
|
289
276
|
|
|
290
277
|
@objc func getLastGalleryImage(_ call: CAPPluginCall) {
|
|
278
|
+
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
|
|
291
279
|
|
|
292
|
-
|
|
280
|
+
guard status == .authorized || status == .limited else {
|
|
281
|
+
call.reject("Gallery permission not granted")
|
|
282
|
+
return
|
|
283
|
+
}
|
|
293
284
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
285
|
+
let fetchOptions = PHFetchOptions()
|
|
286
|
+
fetchOptions.sortDescriptors = [
|
|
287
|
+
NSSortDescriptor(key: "creationDate", ascending: false)
|
|
288
|
+
]
|
|
289
|
+
fetchOptions.fetchLimit = 1
|
|
298
290
|
|
|
299
|
-
|
|
300
|
-
fetchOptions.sortDescriptors = [
|
|
301
|
-
NSSortDescriptor(key: "creationDate", ascending: false)
|
|
302
|
-
]
|
|
303
|
-
fetchOptions.fetchLimit = 1
|
|
291
|
+
let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
|
|
304
292
|
|
|
305
|
-
|
|
293
|
+
guard let asset = assets.firstObject else {
|
|
294
|
+
call.reject("No images found")
|
|
295
|
+
return
|
|
296
|
+
}
|
|
306
297
|
|
|
307
|
-
|
|
308
|
-
|
|
298
|
+
let imageManager = PHImageManager.default()
|
|
299
|
+
let options = PHImageRequestOptions()
|
|
300
|
+
options.isSynchronous = true
|
|
301
|
+
options.deliveryMode = .highQualityFormat
|
|
302
|
+
options.resizeMode = .none
|
|
303
|
+
|
|
304
|
+
imageManager.requestImageDataAndOrientation(
|
|
305
|
+
for: asset,
|
|
306
|
+
options: options
|
|
307
|
+
) { data, _, _, _ in
|
|
308
|
+
guard let imageData = data else {
|
|
309
|
+
call.reject("Error fetching last image")
|
|
309
310
|
return
|
|
310
311
|
}
|
|
311
312
|
|
|
312
|
-
let
|
|
313
|
-
let options = PHImageRequestOptions()
|
|
314
|
-
options.isSynchronous = true
|
|
315
|
-
options.deliveryMode = .highQualityFormat
|
|
316
|
-
options.resizeMode = .none
|
|
317
|
-
|
|
318
|
-
imageManager.requestImageDataAndOrientation(
|
|
319
|
-
for: asset,
|
|
320
|
-
options: options
|
|
321
|
-
) { data, _, _, _ in
|
|
313
|
+
let base64 = imageData.base64EncodedString()
|
|
322
314
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
let base64 = imageData.base64EncodedString()
|
|
329
|
-
|
|
330
|
-
var ret = JSObject()
|
|
331
|
-
ret["base64"] = base64
|
|
332
|
-
call.resolve(ret)
|
|
333
|
-
}
|
|
315
|
+
var ret = JSObject()
|
|
316
|
+
ret["base64"] = base64
|
|
317
|
+
call.resolve(ret)
|
|
334
318
|
}
|
|
319
|
+
}
|
|
335
320
|
|
|
336
321
|
// MARK: - Photo Capture
|
|
337
|
-
|
|
338
322
|
@objc func takePhotoBase64(_ call: CAPPluginCall) {
|
|
339
323
|
guard captureSession?.isRunning == true else {
|
|
340
324
|
call.reject("Camera not started")
|
|
@@ -356,7 +340,6 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
356
340
|
}
|
|
357
341
|
|
|
358
342
|
// MARK: - Barcode Scan
|
|
359
|
-
|
|
360
343
|
@objc func startBarcodeScan(_ call: CAPPluginCall) {
|
|
361
344
|
guard let session = captureSession else {
|
|
362
345
|
call.reject("Preview not started")
|
|
@@ -433,8 +416,7 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
433
416
|
|
|
434
417
|
let handler = VNImageRequestHandler(
|
|
435
418
|
cvPixelBuffer: pixelBuffer,
|
|
436
|
-
orientation: CGImagePropertyOrientation.right
|
|
437
|
-
,
|
|
419
|
+
orientation: CGImagePropertyOrientation.right,
|
|
438
420
|
options: [:]
|
|
439
421
|
)
|
|
440
422
|
|
|
@@ -443,12 +425,10 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
443
425
|
}
|
|
444
426
|
|
|
445
427
|
// MARK: - UIImagePicker Delegate
|
|
446
|
-
|
|
447
428
|
extension CameraModulePlugin: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
|
448
429
|
|
|
449
430
|
public func imagePickerController(_ picker: UIImagePickerController,
|
|
450
431
|
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
|
451
|
-
|
|
452
432
|
picker.dismiss(animated: true)
|
|
453
433
|
|
|
454
434
|
guard let image = info[.originalImage] as? UIImage,
|
|
@@ -474,7 +454,6 @@ extension CameraModulePlugin: UIImagePickerControllerDelegate, UINavigationContr
|
|
|
474
454
|
}
|
|
475
455
|
|
|
476
456
|
// MARK: - UIImage Resize
|
|
477
|
-
|
|
478
457
|
extension UIImage {
|
|
479
458
|
func resized(maxSize: CGFloat) -> UIImage? {
|
|
480
459
|
let maxSide = max(size.width, size.height)
|
|
@@ -489,6 +468,23 @@ extension UIImage {
|
|
|
489
468
|
UIGraphicsEndImageContext()
|
|
490
469
|
return img
|
|
491
470
|
}
|
|
471
|
+
}
|
|
492
472
|
|
|
473
|
+
// MARK: - Photo Delegate (si necesitas esta clase)
|
|
474
|
+
class PhotoDelegate: NSObject, AVCapturePhotoCaptureDelegate {
|
|
475
|
+
private let completion: (String) -> Void
|
|
493
476
|
|
|
494
|
-
|
|
477
|
+
init(completion: @escaping (String) -> Void) {
|
|
478
|
+
self.completion = completion
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
func photoOutput(_ output: AVCapturePhotoOutput,
|
|
482
|
+
didFinishProcessingPhoto photo: AVCapturePhoto,
|
|
483
|
+
error: Error?) {
|
|
484
|
+
guard let data = photo.fileDataRepresentation(),
|
|
485
|
+
let base64 = data.base64EncodedString() else {
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
completion(base64)
|
|
489
|
+
}
|
|
490
|
+
}
|