capacitor-camera-module 0.0.64 → 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
- public let identifier = "CameraModule"
12
- public let jsName = "CameraModule"
13
- public let pluginMethods: [CAPPluginMethod] = [
14
- CAPPluginMethod(name: "checkPermission", returnType: CAPPluginReturnPromise),
15
- CAPPluginMethod(name: "requestPermission", returnType: CAPPluginReturnPromise),
16
- CAPPluginMethod(name: "checkGalleryPermission", returnType: CAPPluginReturnPromise),
17
- CAPPluginMethod(name: "requestGalleryPermission", returnType: CAPPluginReturnPromise),
18
- CAPPluginMethod(name: "checkAndRequestGalleryPermission", returnType: CAPPluginReturnPromise),
19
- CAPPluginMethod(name: "pickImageBase64", returnType: CAPPluginReturnPromise),
20
- CAPPluginMethod(name: "startPreview", returnType: CAPPluginReturnPromise),
21
- CAPPluginMethod(name: "stopPreview", returnType: CAPPluginReturnPromise),
22
- CAPPluginMethod(name: "toggleFlash", returnType: CAPPluginReturnPromise),
23
- CAPPluginMethod(name: "hasFlash", returnType: CAPPluginReturnPromise),
24
- CAPPluginMethod(name: "takePhotoBase64", returnType: CAPPluginReturnPromise),
25
- CAPPluginMethod(name: "startBarcodeScan", returnType: CAPPluginReturnPromise),
26
- CAPPluginMethod(name: "stopBarcodeScan", returnType: CAPPluginReturnPromise),
27
- CAPPluginMethod(name: "getLastGalleryImage", returnType: CAPPluginReturnPromise)
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,20 +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
- // Este método ya no fuerza el color negro
55
- // El webView será configurado dinámicamente en startPreview/stopPreview
56
49
  print("📱 CameraModulePlugin cargado")
57
50
  }
58
51
 
59
-
60
-
61
52
  // MARK: - Camera Permission
62
-
63
53
  @objc func checkPermission(_ call: CAPPluginCall) {
64
54
  let status = AVCaptureDevice.authorizationStatus(for: .video)
65
55
  call.resolve([
@@ -80,10 +70,8 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
80
70
  }
81
71
 
82
72
  // MARK: - Gallery Permission
83
-
84
73
  @objc func checkGalleryPermission(_ call: CAPPluginCall) {
85
74
  let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
86
-
87
75
  switch status {
88
76
  case .authorized, .limited:
89
77
  call.resolve(["granted": true, "status": "granted"])
@@ -116,7 +104,6 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
116
104
  }
117
105
 
118
106
  // MARK: - Pick Image
119
-
120
107
  @objc func pickImageBase64(_ call: CAPPluginCall) {
121
108
  let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
122
109
  guard status == .authorized || status == .limited else {
@@ -141,131 +128,101 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
141
128
  }
142
129
 
143
130
  // MARK: - Camera Preview
131
+ @objc func startPreview(_ call: CAPPluginCall) {
132
+ DispatchQueue.main.async {
133
+ if self.previewView != nil {
134
+ call.resolve()
135
+ return
136
+ }
144
137
 
145
- @objc func startPreview(_ call: CAPPluginCall) {
146
- DispatchQueue.main.async {
147
- if self.previewView != nil {
148
- call.resolve()
149
- return
150
- }
151
-
152
- guard AVCaptureDevice.authorizationStatus(for: .video) == .authorized else {
153
- call.reject("Camera permission not granted")
154
- return
155
- }
156
-
157
- let session = AVCaptureSession()
158
-
159
- guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
160
- let input = try? AVCaptureDeviceInput(device: device) else {
161
- call.reject("Camera not available")
162
- return
163
- }
164
-
165
- if session.canAddInput(input) {
166
- session.addInput(input)
167
- }
168
-
169
- if session.canAddOutput(self.photoOutput) {
170
- session.addOutput(self.photoOutput)
171
- }
172
-
173
- guard let container = self.bridge?.viewController?.view else {
174
- call.reject("No container view")
175
- return
176
- }
177
-
178
- guard let webView = self.bridge?.webView as? WKWebView else {
179
- call.reject("No webView")
180
- return
181
- }
182
-
183
- // 1. Hacer el webView transparente ANTES de crear el preview
184
- webView.isOpaque = false
185
- webView.backgroundColor = .clear
186
- webView.scrollView.backgroundColor = .clear
187
-
188
- // Si necesitas mantener scroll pero transparente:
189
- webView.scrollView.isOpaque = false
190
-
191
- // 2. Crear previewView
192
- let previewView = UIView()
193
- previewView.translatesAutoresizingMaskIntoConstraints = false
194
- previewView.backgroundColor = .black // Temporal para debug
195
- previewView.isUserInteractionEnabled = false // Importante: no intercepta toques
196
-
197
- // DEBUG: Borde para ver los límites
198
- previewView.layer.borderWidth = 2
199
- previewView.layer.borderColor = UIColor.green.cgColor
138
+ guard AVCaptureDevice.authorizationStatus(for: .video) == .authorized else {
139
+ call.reject("Camera permission not granted")
140
+ return
141
+ }
200
142
 
201
- print("🔍 DEBUG: Container frame: \(container.frame)")
202
- print("🔍 DEBUG: WebView frame: \(webView.frame)")
143
+ let session = AVCaptureSession()
203
144
 
204
- // 3. Insertar previewView DETRÁS de todo (posición 0)
205
- container.insertSubview(previewView, at: 0)
145
+ guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
146
+ let input = try? AVCaptureDeviceInput(device: device) else {
147
+ call.reject("Camera not available")
148
+ return
149
+ }
206
150
 
207
- // 4. Configurar constraints para que ocupe TODA la pantalla
208
- NSLayoutConstraint.activate([
209
- previewView.topAnchor.constraint(equalTo: container.topAnchor),
210
- previewView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
211
- previewView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
212
- previewView.trailingAnchor.constraint(equalTo: container.trailingAnchor)
213
- ])
151
+ if session.canAddInput(input) {
152
+ session.addInput(input)
153
+ }
214
154
 
215
- // 5. Asegurar orden de capas
216
- previewView.layer.zPosition = -1 // Detrás de todo
217
- webView.layer.zPosition = 0 // Normal
155
+ if session.canAddOutput(self.photoOutput) {
156
+ session.addOutput(self.photoOutput)
157
+ }
218
158
 
219
- // 6. Forzar layout
220
- container.layoutIfNeeded()
159
+ guard let container = self.bridge?.viewController?.view else {
160
+ call.reject("No container view")
161
+ return
162
+ }
221
163
 
222
- print("🔍 DEBUG: PreviewView frame después de layout: \(previewView.frame)")
164
+ guard let webView = self.bridge?.webView as? WKWebView else {
165
+ call.reject("No webView")
166
+ return
167
+ }
223
168
 
224
- // 7. Crear previewLayer
225
- let previewLayer = AVCaptureVideoPreviewLayer(session: session)
226
- previewLayer.videoGravity = .resizeAspectFill
227
- previewLayer.frame = previewView.bounds
228
- previewLayer.masksToBounds = true
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
180
+
181
+ // Insertar previewView DETRÁS de todo
182
+ container.insertSubview(previewView, at: 0)
183
+
184
+ // Configurar constraints para toda la pantalla
185
+ NSLayoutConstraint.activate([
186
+ previewView.topAnchor.constraint(equalTo: container.topAnchor),
187
+ previewView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
188
+ previewView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
189
+ previewView.trailingAnchor.constraint(equalTo: container.trailingAnchor)
190
+ ])
229
191
 
230
- // DEBUG: Borde para el layer también
231
- previewLayer.borderWidth = 1
232
- previewLayer.borderColor = UIColor.yellow.cgColor
192
+ // Asegurar orden de capas
193
+ previewView.layer.zPosition = -1
194
+ webView.layer.zPosition = 0
233
195
 
234
- previewView.layer.insertSublayer(previewLayer, at: 0)
196
+ // Forzar layout
197
+ container.layoutIfNeeded()
235
198
 
236
- // 8. Guardar referencias
237
- self.previewView = previewView
238
- self.videoPreviewLayer = previewLayer
239
- self.captureSession = session
199
+ // Crear previewLayer
200
+ let previewLayer = AVCaptureVideoPreviewLayer(session: session)
201
+ previewLayer.videoGravity = .resizeAspectFill
202
+ previewLayer.frame = previewView.bounds
203
+ previewLayer.masksToBounds = true
240
204
 
241
- // 9. Configurar sesión
242
- session.sessionPreset = .photo
205
+ previewView.layer.insertSublayer(previewLayer, at: 0)
243
206
 
244
- // 10. Iniciar sesión en background
245
- DispatchQueue.global(qos: .userInitiated).async {
246
- session.startRunning()
207
+ // Guardar referencias
208
+ self.previewView = previewView
209
+ self.videoPreviewLayer = previewLayer
210
+ self.captureSession = session
247
211
 
248
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
249
- print("✅ Cámara iniciada")
250
- print("📍 Preview visible: \(previewView.window != nil ? "SÍ" : "NO")")
251
- print("📍 WebView transparente: \(webView.isOpaque ? "NO" : "SÍ")")
212
+ // Configurar sesión
213
+ session.sessionPreset = .photo
252
214
 
253
- // Quitar borde de debug después de 3 segundos
254
- DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
255
- previewView.layer.borderWidth = 0
256
- previewLayer.borderWidth = 0
257
- previewView.backgroundColor = .clear // Ahora sí transparente
258
- }
215
+ // Iniciar sesión en background
216
+ DispatchQueue.global(qos: .userInitiated).async {
217
+ session.startRunning()
259
218
  }
260
- }
261
219
 
262
- call.resolve([
263
- "success": true,
264
- "message": "Preview iniciado con webView transparente"
265
- ])
220
+ call.resolve([
221
+ "success": true,
222
+ "message": "Preview de cámara iniciado"
223
+ ])
224
+ }
266
225
  }
267
- }
268
-
269
226
 
270
227
  @objc func stopPreview(_ call: CAPPluginCall) {
271
228
  DispatchQueue.main.async {
@@ -280,23 +237,19 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
280
237
  self.videoPreviewLayer = nil
281
238
  self.previewView = nil
282
239
 
283
- // Restaurar webView a estado normal (opcional)
240
+ // Restaurar webView a estado normal
284
241
  if let webView = self.bridge?.webView as? WKWebView {
285
242
  webView.isOpaque = true
286
- webView.backgroundColor = .white // O el color de tu app
243
+ webView.backgroundColor = .white
287
244
  webView.scrollView.backgroundColor = .white
288
245
  webView.scrollView.isOpaque = true
289
246
  }
290
247
 
291
- print("🛑 Preview detenido - webView restaurado")
292
248
  call.resolve()
293
249
  }
294
250
  }
295
251
 
296
-
297
-
298
252
  // MARK: - Flash
299
-
300
253
  @objc func toggleFlash(_ call: CAPPluginCall) {
301
254
  guard let enable = call.getBool("enable"),
302
255
  let device = AVCaptureDevice.default(for: .video),
@@ -322,53 +275,50 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
322
275
  }
323
276
 
324
277
  @objc func getLastGalleryImage(_ call: CAPPluginCall) {
278
+ let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
325
279
 
326
- let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
280
+ guard status == .authorized || status == .limited else {
281
+ call.reject("Gallery permission not granted")
282
+ return
283
+ }
327
284
 
328
- guard status == .authorized || status == .limited else {
329
- call.reject("Gallery permission not granted")
330
- return
331
- }
285
+ let fetchOptions = PHFetchOptions()
286
+ fetchOptions.sortDescriptors = [
287
+ NSSortDescriptor(key: "creationDate", ascending: false)
288
+ ]
289
+ fetchOptions.fetchLimit = 1
332
290
 
333
- let fetchOptions = PHFetchOptions()
334
- fetchOptions.sortDescriptors = [
335
- NSSortDescriptor(key: "creationDate", ascending: false)
336
- ]
337
- fetchOptions.fetchLimit = 1
291
+ let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
338
292
 
339
- let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
293
+ guard let asset = assets.firstObject else {
294
+ call.reject("No images found")
295
+ return
296
+ }
340
297
 
341
- guard let asset = assets.firstObject else {
342
- call.reject("No images found")
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")
343
310
  return
344
311
  }
345
312
 
346
- let imageManager = PHImageManager.default()
347
- let options = PHImageRequestOptions()
348
- options.isSynchronous = true
349
- options.deliveryMode = .highQualityFormat
350
- options.resizeMode = .none
351
-
352
- imageManager.requestImageDataAndOrientation(
353
- for: asset,
354
- options: options
355
- ) { data, _, _, _ in
313
+ let base64 = imageData.base64EncodedString()
356
314
 
357
- guard let imageData = data else {
358
- call.reject("Error fetching last image")
359
- return
360
- }
361
-
362
- let base64 = imageData.base64EncodedString()
363
-
364
- var ret = JSObject()
365
- ret["base64"] = base64
366
- call.resolve(ret)
367
- }
315
+ var ret = JSObject()
316
+ ret["base64"] = base64
317
+ call.resolve(ret)
368
318
  }
319
+ }
369
320
 
370
321
  // MARK: - Photo Capture
371
-
372
322
  @objc func takePhotoBase64(_ call: CAPPluginCall) {
373
323
  guard captureSession?.isRunning == true else {
374
324
  call.reject("Camera not started")
@@ -390,7 +340,6 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
390
340
  }
391
341
 
392
342
  // MARK: - Barcode Scan
393
-
394
343
  @objc func startBarcodeScan(_ call: CAPPluginCall) {
395
344
  guard let session = captureSession else {
396
345
  call.reject("Preview not started")
@@ -467,8 +416,7 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
467
416
 
468
417
  let handler = VNImageRequestHandler(
469
418
  cvPixelBuffer: pixelBuffer,
470
- orientation: CGImagePropertyOrientation.right
471
- ,
419
+ orientation: CGImagePropertyOrientation.right,
472
420
  options: [:]
473
421
  )
474
422
 
@@ -477,12 +425,10 @@ public class CameraModulePlugin: CAPPlugin,CAPBridgedPlugin,AVCaptureVideoDataOu
477
425
  }
478
426
 
479
427
  // MARK: - UIImagePicker Delegate
480
-
481
428
  extension CameraModulePlugin: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
482
429
 
483
430
  public func imagePickerController(_ picker: UIImagePickerController,
484
431
  didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
485
-
486
432
  picker.dismiss(animated: true)
487
433
 
488
434
  guard let image = info[.originalImage] as? UIImage,
@@ -508,7 +454,6 @@ extension CameraModulePlugin: UIImagePickerControllerDelegate, UINavigationContr
508
454
  }
509
455
 
510
456
  // MARK: - UIImage Resize
511
-
512
457
  extension UIImage {
513
458
  func resized(maxSize: CGFloat) -> UIImage? {
514
459
  let maxSide = max(size.width, size.height)
@@ -523,6 +468,23 @@ extension UIImage {
523
468
  UIGraphicsEndImageContext()
524
469
  return img
525
470
  }
471
+ }
526
472
 
473
+ // MARK: - Photo Delegate (si necesitas esta clase)
474
+ class PhotoDelegate: NSObject, AVCapturePhotoCaptureDelegate {
475
+ private let completion: (String) -> Void
527
476
 
528
- }
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-camera-module",
3
- "version": "0.0.64",
3
+ "version": "0.0.65",
4
4
  "description": "Plugin to request permissiones view camera take phots",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",