capacitor-camera-module 0.0.68 → 0.0.69
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?
|
|
@@ -51,13 +48,9 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
51
48
|
|
|
52
49
|
// MARK: - Lifecycle
|
|
53
50
|
public override func load() {
|
|
54
|
-
// Este método ya no fuerza el color negro
|
|
55
|
-
// El webView será configurado dinámicamente en startPreview/stopPreview
|
|
56
51
|
print("📱 CameraModulePlugin cargado")
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
|
|
60
|
-
|
|
61
54
|
// MARK: - Camera Permission
|
|
62
55
|
|
|
63
56
|
@objc func checkPermission(_ call: CAPPluginCall) {
|
|
@@ -142,107 +135,106 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
142
135
|
|
|
143
136
|
// MARK: - Camera Preview
|
|
144
137
|
|
|
145
|
-
@objc func startPreview(_ call: CAPPluginCall) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
138
|
+
@objc func startPreview(_ call: CAPPluginCall) {
|
|
139
|
+
DispatchQueue.main.async {
|
|
140
|
+
if self.previewView != nil {
|
|
141
|
+
call.resolve()
|
|
142
|
+
return
|
|
143
|
+
}
|
|
151
144
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
145
|
+
guard AVCaptureDevice.authorizationStatus(for: .video) == .authorized else {
|
|
146
|
+
call.reject("Camera permission not granted")
|
|
147
|
+
return
|
|
148
|
+
}
|
|
156
149
|
|
|
157
|
-
|
|
150
|
+
let session = AVCaptureSession()
|
|
158
151
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
152
|
+
guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
|
|
153
|
+
let input = try? AVCaptureDeviceInput(device: device) else {
|
|
154
|
+
call.reject("Camera not available")
|
|
155
|
+
return
|
|
156
|
+
}
|
|
164
157
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
158
|
+
if session.canAddInput(input) {
|
|
159
|
+
session.addInput(input)
|
|
160
|
+
}
|
|
168
161
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
162
|
+
if session.canAddOutput(self.photoOutput) {
|
|
163
|
+
session.addOutput(self.photoOutput)
|
|
164
|
+
}
|
|
172
165
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
166
|
+
guard let container = self.bridge?.viewController?.view else {
|
|
167
|
+
call.reject("No container view")
|
|
168
|
+
return
|
|
169
|
+
}
|
|
177
170
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
171
|
+
guard let webView = self.bridge?.webView as? WKWebView else {
|
|
172
|
+
call.reject("No webView")
|
|
173
|
+
return
|
|
174
|
+
}
|
|
182
175
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
176
|
+
// 1. Hacer el webView transparente
|
|
177
|
+
webView.isOpaque = false
|
|
178
|
+
webView.backgroundColor = .clear
|
|
179
|
+
webView.scrollView.backgroundColor = .clear
|
|
180
|
+
webView.scrollView.isOpaque = false
|
|
181
|
+
|
|
182
|
+
// 2. Crear previewView
|
|
183
|
+
let previewView = UIView()
|
|
184
|
+
previewView.translatesAutoresizingMaskIntoConstraints = false
|
|
185
|
+
previewView.backgroundColor = .clear // Transparente desde el inicio
|
|
186
|
+
previewView.isUserInteractionEnabled = false // No intercepta toques
|
|
187
|
+
|
|
188
|
+
// 3. Insertar previewView DETRÁS de todo
|
|
189
|
+
container.insertSubview(previewView, at: 0)
|
|
190
|
+
|
|
191
|
+
// 4. Configurar constraints para toda la pantalla
|
|
192
|
+
NSLayoutConstraint.activate([
|
|
193
|
+
previewView.topAnchor.constraint(equalTo: container.topAnchor),
|
|
194
|
+
previewView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
|
|
195
|
+
previewView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
|
|
196
|
+
previewView.trailingAnchor.constraint(equalTo: container.trailingAnchor)
|
|
197
|
+
])
|
|
205
198
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
199
|
+
// 5. Asegurar orden de capas
|
|
200
|
+
previewView.layer.zPosition = -1 // Detrás de todo
|
|
201
|
+
webView.layer.zPosition = 0 // Normal
|
|
209
202
|
|
|
210
|
-
|
|
211
|
-
|
|
203
|
+
// 6. Forzar layout
|
|
204
|
+
container.layoutIfNeeded()
|
|
212
205
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
206
|
+
// 7. Crear previewLayer
|
|
207
|
+
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
|
|
208
|
+
previewLayer.videoGravity = .resizeAspectFill
|
|
209
|
+
previewLayer.frame = previewView.bounds
|
|
210
|
+
previewLayer.masksToBounds = true
|
|
218
211
|
|
|
219
|
-
|
|
212
|
+
previewView.layer.insertSublayer(previewLayer, at: 0)
|
|
220
213
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
214
|
+
// 8. Guardar referencias
|
|
215
|
+
self.previewView = previewView
|
|
216
|
+
self.videoPreviewLayer = previewLayer
|
|
217
|
+
self.captureSession = session
|
|
225
218
|
|
|
226
|
-
|
|
227
|
-
|
|
219
|
+
// 9. Configurar sesión
|
|
220
|
+
session.sessionPreset = .photo
|
|
228
221
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
222
|
+
// 10. Iniciar sesión en background
|
|
223
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
224
|
+
session.startRunning()
|
|
232
225
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
226
|
+
DispatchQueue.main.async {
|
|
227
|
+
print("Cámara iniciada correctamente")
|
|
228
|
+
print("Preview visible: \(previewView.window != nil ? "SÍ" : "NO")")
|
|
229
|
+
}
|
|
236
230
|
}
|
|
237
|
-
}
|
|
238
231
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
232
|
+
call.resolve([
|
|
233
|
+
"success": true,
|
|
234
|
+
"message": "Preview de cámara iniciado"
|
|
235
|
+
])
|
|
236
|
+
}
|
|
243
237
|
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
238
|
|
|
247
239
|
@objc func stopPreview(_ call: CAPPluginCall) {
|
|
248
240
|
DispatchQueue.main.async {
|
|
@@ -286,9 +278,6 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
286
278
|
}
|
|
287
279
|
}
|
|
288
280
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
281
|
// MARK: - Flash
|
|
293
282
|
|
|
294
283
|
@objc func toggleFlash(_ call: CAPPluginCall) {
|
|
@@ -317,49 +306,49 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
317
306
|
|
|
318
307
|
@objc func getLastGalleryImage(_ call: CAPPluginCall) {
|
|
319
308
|
|
|
320
|
-
|
|
309
|
+
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
|
|
321
310
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
311
|
+
guard status == .authorized || status == .limited else {
|
|
312
|
+
call.reject("Gallery permission not granted")
|
|
313
|
+
return
|
|
314
|
+
}
|
|
326
315
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
316
|
+
let fetchOptions = PHFetchOptions()
|
|
317
|
+
fetchOptions.sortDescriptors = [
|
|
318
|
+
NSSortDescriptor(key: "creationDate", ascending: false)
|
|
319
|
+
]
|
|
320
|
+
fetchOptions.fetchLimit = 1
|
|
332
321
|
|
|
333
|
-
|
|
322
|
+
let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
|
|
334
323
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
324
|
+
guard let asset = assets.firstObject else {
|
|
325
|
+
call.reject("No images found")
|
|
326
|
+
return
|
|
327
|
+
}
|
|
339
328
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
329
|
+
let imageManager = PHImageManager.default()
|
|
330
|
+
let options = PHImageRequestOptions()
|
|
331
|
+
options.isSynchronous = true
|
|
332
|
+
options.deliveryMode = .highQualityFormat
|
|
333
|
+
options.resizeMode = .none
|
|
345
334
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
335
|
+
imageManager.requestImageDataAndOrientation(
|
|
336
|
+
for: asset,
|
|
337
|
+
options: options
|
|
338
|
+
) { data, _, _, _ in
|
|
350
339
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
340
|
+
guard let imageData = data else {
|
|
341
|
+
call.reject("Error fetching last image")
|
|
342
|
+
return
|
|
343
|
+
}
|
|
355
344
|
|
|
356
|
-
|
|
345
|
+
let base64 = imageData.base64EncodedString()
|
|
357
346
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
347
|
+
var ret = JSObject()
|
|
348
|
+
ret["base64"] = base64
|
|
349
|
+
call.resolve(ret)
|
|
362
350
|
}
|
|
351
|
+
}
|
|
363
352
|
|
|
364
353
|
// MARK: - Photo Capture
|
|
365
354
|
|
|
@@ -434,7 +423,7 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
434
423
|
let output = AVCaptureVideoDataOutput()
|
|
435
424
|
output.videoSettings = [
|
|
436
425
|
kCVPixelBufferPixelFormatTypeKey as String:
|
|
437
|
-
|
|
426
|
+
kCVPixelFormatType_32BGRA
|
|
438
427
|
]
|
|
439
428
|
output.alwaysDiscardsLateVideoFrames = true
|
|
440
429
|
output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "barcode.queue"))
|
|
@@ -478,9 +467,35 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
|
|
|
478
467
|
try? handler.perform([request])
|
|
479
468
|
}
|
|
480
469
|
|
|
470
|
+
// MARK: - PhotoDelegate Class
|
|
481
471
|
|
|
482
|
-
|
|
472
|
+
private class PhotoDelegate: NSObject, AVCapturePhotoCaptureDelegate {
|
|
473
|
+
private let completion: (String) -> Void
|
|
474
|
+
|
|
475
|
+
init(completion: @escaping (String) -> Void) {
|
|
476
|
+
self.completion = completion
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
func photoOutput(_ output: AVCapturePhotoOutput,
|
|
480
|
+
didFinishProcessingPhoto photo: AVCapturePhoto,
|
|
481
|
+
error: Error?) {
|
|
482
|
+
if let error = error {
|
|
483
|
+
print("Error capturando foto: \(error)")
|
|
484
|
+
return
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
guard let data = photo.fileDataRepresentation(),
|
|
488
|
+
let base64 = data.base64EncodedString() as? String else {
|
|
489
|
+
print("No se pudo convertir la imagen a base64")
|
|
490
|
+
return
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
completion(base64)
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
483
497
|
|
|
498
|
+
// MARK: - UIImagePicker Delegate
|
|
484
499
|
extension CameraModulePlugin: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
|
485
500
|
|
|
486
501
|
public func imagePickerController(_ picker: UIImagePickerController,
|
|
@@ -511,7 +526,6 @@ extension CameraModulePlugin: UIImagePickerControllerDelegate, UINavigationContr
|
|
|
511
526
|
}
|
|
512
527
|
|
|
513
528
|
// MARK: - UIImage Resize
|
|
514
|
-
|
|
515
529
|
extension UIImage {
|
|
516
530
|
func resized(maxSize: CGFloat) -> UIImage? {
|
|
517
531
|
let maxSide = max(size.width, size.height)
|
|
@@ -526,6 +540,4 @@ extension UIImage {
|
|
|
526
540
|
UIGraphicsEndImageContext()
|
|
527
541
|
return img
|
|
528
542
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
543
|
+
}
|