capacitor-camera-module 0.0.65 → 0.0.67

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,26 +5,29 @@ import UIKit
5
5
  import Photos
6
6
 
7
7
  @objc(CameraModule)
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
- ]
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
+
28
31
 
29
32
  private var previewView: UIView?
30
33
  private var captureSession: AVCaptureSession?
@@ -33,6 +36,7 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
33
36
  private var photoDelegate: PhotoDelegate?
34
37
 
35
38
  // MARK: - Barcode
39
+
36
40
  private var isScanning = false
37
41
  private var scanCall: CAPPluginCall?
38
42
  private var barcodeRequest: VNDetectBarcodesRequest?
@@ -42,14 +46,20 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
42
46
  private let scanDebounceInterval: TimeInterval = 0.5
43
47
 
44
48
  // MARK: - Gallery
49
+
45
50
  private var galleryCall: CAPPluginCall?
46
51
 
47
52
  // MARK: - Lifecycle
48
53
  public override func load() {
54
+ // Este método ya no fuerza el color negro
55
+ // El webView será configurado dinámicamente en startPreview/stopPreview
49
56
  print("📱 CameraModulePlugin cargado")
50
57
  }
51
58
 
59
+
60
+
52
61
  // MARK: - Camera Permission
62
+
53
63
  @objc func checkPermission(_ call: CAPPluginCall) {
54
64
  let status = AVCaptureDevice.authorizationStatus(for: .video)
55
65
  call.resolve([
@@ -70,8 +80,10 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
70
80
  }
71
81
 
72
82
  // MARK: - Gallery Permission
83
+
73
84
  @objc func checkGalleryPermission(_ call: CAPPluginCall) {
74
85
  let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
86
+
75
87
  switch status {
76
88
  case .authorized, .limited:
77
89
  call.resolve(["granted": true, "status": "granted"])
@@ -104,6 +116,7 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
104
116
  }
105
117
 
106
118
  // MARK: - Pick Image
119
+
107
120
  @objc func pickImageBase64(_ call: CAPPluginCall) {
108
121
  let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
109
122
  guard status == .authorized || status == .limited else {
@@ -128,101 +141,108 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
128
141
  }
129
142
 
130
143
  // MARK: - Camera Preview
131
- @objc func startPreview(_ call: CAPPluginCall) {
132
- DispatchQueue.main.async {
133
- if self.previewView != nil {
134
- call.resolve()
135
- return
136
- }
137
144
 
138
- guard AVCaptureDevice.authorizationStatus(for: .video) == .authorized else {
139
- call.reject("Camera permission not granted")
140
- return
141
- }
145
+ @objc func startPreview(_ call: CAPPluginCall) {
146
+ DispatchQueue.main.async {
147
+ if self.previewView != nil {
148
+ call.resolve()
149
+ return
150
+ }
142
151
 
143
- let session = AVCaptureSession()
152
+ guard AVCaptureDevice.authorizationStatus(for: .video) == .authorized else {
153
+ call.reject("Camera permission not granted")
154
+ return
155
+ }
144
156
 
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
- }
157
+ let session = AVCaptureSession()
150
158
 
151
- if session.canAddInput(input) {
152
- session.addInput(input)
153
- }
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
+ }
154
164
 
155
- if session.canAddOutput(self.photoOutput) {
156
- session.addOutput(self.photoOutput)
157
- }
165
+ if session.canAddInput(input) {
166
+ session.addInput(input)
167
+ }
158
168
 
159
- guard let container = self.bridge?.viewController?.view else {
160
- call.reject("No container view")
161
- return
162
- }
169
+ if session.canAddOutput(self.photoOutput) {
170
+ session.addOutput(self.photoOutput)
171
+ }
163
172
 
164
- guard let webView = self.bridge?.webView as? WKWebView else {
165
- call.reject("No webView")
166
- return
167
- }
173
+ guard let container = self.bridge?.viewController?.view else {
174
+ call.reject("No container view")
175
+ return
176
+ }
168
177
 
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
- ])
178
+ guard let webView = self.bridge?.webView as? WKWebView else {
179
+ call.reject("No webView")
180
+ return
181
+ }
191
182
 
192
- // Asegurar orden de capas
193
- previewView.layer.zPosition = -1
194
- webView.layer.zPosition = 0
183
+ // 1. Hacer el webView transparente
184
+ webView.isOpaque = false
185
+ webView.backgroundColor = .clear
186
+ webView.scrollView.backgroundColor = .clear
187
+ webView.scrollView.isOpaque = false
188
+
189
+ // 2. Crear previewView
190
+ let previewView = UIView()
191
+ previewView.translatesAutoresizingMaskIntoConstraints = false
192
+ previewView.backgroundColor = .clear // Transparente desde el inicio
193
+ previewView.isUserInteractionEnabled = false // No intercepta toques
194
+
195
+ // 3. Insertar previewView DETRÁS de todo
196
+ container.insertSubview(previewView, at: 0)
197
+
198
+ // 4. Configurar constraints para toda la pantalla
199
+ NSLayoutConstraint.activate([
200
+ previewView.topAnchor.constraint(equalTo: container.topAnchor),
201
+ previewView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
202
+ previewView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
203
+ previewView.trailingAnchor.constraint(equalTo: container.trailingAnchor)
204
+ ])
195
205
 
196
- // Forzar layout
197
- container.layoutIfNeeded()
206
+ // 5. Asegurar orden de capas
207
+ previewView.layer.zPosition = -1 // Detrás de todo
208
+ webView.layer.zPosition = 0 // Normal
198
209
 
199
- // Crear previewLayer
200
- let previewLayer = AVCaptureVideoPreviewLayer(session: session)
201
- previewLayer.videoGravity = .resizeAspectFill
202
- previewLayer.frame = previewView.bounds
203
- previewLayer.masksToBounds = true
210
+ // 6. Forzar layout
211
+ container.layoutIfNeeded()
204
212
 
205
- previewView.layer.insertSublayer(previewLayer, at: 0)
213
+ // 7. Crear previewLayer
214
+ let previewLayer = AVCaptureVideoPreviewLayer(session: session)
215
+ previewLayer.videoGravity = .resizeAspectFill
216
+ previewLayer.frame = previewView.bounds
217
+ previewLayer.masksToBounds = true
206
218
 
207
- // Guardar referencias
208
- self.previewView = previewView
209
- self.videoPreviewLayer = previewLayer
210
- self.captureSession = session
219
+ previewView.layer.insertSublayer(previewLayer, at: 0)
211
220
 
212
- // Configurar sesión
213
- session.sessionPreset = .photo
221
+ // 8. Guardar referencias
222
+ self.previewView = previewView
223
+ self.videoPreviewLayer = previewLayer
224
+ self.captureSession = session
214
225
 
215
- // Iniciar sesión en background
216
- DispatchQueue.global(qos: .userInitiated).async {
217
- session.startRunning()
218
- }
226
+ // 9. Configurar sesión
227
+ session.sessionPreset = .photo
219
228
 
220
- call.resolve([
221
- "success": true,
222
- "message": "Preview de cámara iniciado"
223
- ])
229
+ // 10. Iniciar sesión en background
230
+ DispatchQueue.global(qos: .userInitiated).async {
231
+ session.startRunning()
232
+
233
+ DispatchQueue.main.async {
234
+ print("Cámara iniciada correctamente")
235
+ print("Preview visible: \(previewView.window != nil ? "SÍ" : "NO")")
236
+ }
224
237
  }
238
+
239
+ call.resolve([
240
+ "success": true,
241
+ "message": "Preview de cámara iniciado"
242
+ ])
225
243
  }
244
+ }
245
+
226
246
 
227
247
  @objc func stopPreview(_ call: CAPPluginCall) {
228
248
  DispatchQueue.main.async {
@@ -237,19 +257,23 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
237
257
  self.videoPreviewLayer = nil
238
258
  self.previewView = nil
239
259
 
240
- // Restaurar webView a estado normal
260
+ // Restaurar webView a estado normal (opcional)
241
261
  if let webView = self.bridge?.webView as? WKWebView {
242
262
  webView.isOpaque = true
243
- webView.backgroundColor = .white
263
+ webView.backgroundColor = .white // O el color de tu app
244
264
  webView.scrollView.backgroundColor = .white
245
265
  webView.scrollView.isOpaque = true
246
266
  }
247
267
 
268
+ print("🛑 Preview detenido - webView restaurado")
248
269
  call.resolve()
249
270
  }
250
271
  }
251
272
 
273
+
274
+
252
275
  // MARK: - Flash
276
+
253
277
  @objc func toggleFlash(_ call: CAPPluginCall) {
254
278
  guard let enable = call.getBool("enable"),
255
279
  let device = AVCaptureDevice.default(for: .video),
@@ -275,50 +299,53 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
275
299
  }
276
300
 
277
301
  @objc func getLastGalleryImage(_ call: CAPPluginCall) {
278
- let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
279
302
 
280
- guard status == .authorized || status == .limited else {
281
- call.reject("Gallery permission not granted")
282
- return
283
- }
303
+ let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
284
304
 
285
- let fetchOptions = PHFetchOptions()
286
- fetchOptions.sortDescriptors = [
287
- NSSortDescriptor(key: "creationDate", ascending: false)
288
- ]
289
- fetchOptions.fetchLimit = 1
305
+ guard status == .authorized || status == .limited else {
306
+ call.reject("Gallery permission not granted")
307
+ return
308
+ }
290
309
 
291
- let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
310
+ let fetchOptions = PHFetchOptions()
311
+ fetchOptions.sortDescriptors = [
312
+ NSSortDescriptor(key: "creationDate", ascending: false)
313
+ ]
314
+ fetchOptions.fetchLimit = 1
292
315
 
293
- guard let asset = assets.firstObject else {
294
- call.reject("No images found")
295
- return
296
- }
316
+ let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
297
317
 
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")
318
+ guard let asset = assets.firstObject else {
319
+ call.reject("No images found")
310
320
  return
311
321
  }
312
322
 
313
- let base64 = imageData.base64EncodedString()
323
+ let imageManager = PHImageManager.default()
324
+ let options = PHImageRequestOptions()
325
+ options.isSynchronous = true
326
+ options.deliveryMode = .highQualityFormat
327
+ options.resizeMode = .none
328
+
329
+ imageManager.requestImageDataAndOrientation(
330
+ for: asset,
331
+ options: options
332
+ ) { data, _, _, _ in
333
+
334
+ guard let imageData = data else {
335
+ call.reject("Error fetching last image")
336
+ return
337
+ }
314
338
 
315
- var ret = JSObject()
316
- ret["base64"] = base64
317
- call.resolve(ret)
339
+ let base64 = imageData.base64EncodedString()
340
+
341
+ var ret = JSObject()
342
+ ret["base64"] = base64
343
+ call.resolve(ret)
344
+ }
318
345
  }
319
- }
320
346
 
321
347
  // MARK: - Photo Capture
348
+
322
349
  @objc func takePhotoBase64(_ call: CAPPluginCall) {
323
350
  guard captureSession?.isRunning == true else {
324
351
  call.reject("Camera not started")
@@ -340,6 +367,7 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
340
367
  }
341
368
 
342
369
  // MARK: - Barcode Scan
370
+
343
371
  @objc func startBarcodeScan(_ call: CAPPluginCall) {
344
372
  guard let session = captureSession else {
345
373
  call.reject("Preview not started")
@@ -416,7 +444,8 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
416
444
 
417
445
  let handler = VNImageRequestHandler(
418
446
  cvPixelBuffer: pixelBuffer,
419
- orientation: CGImagePropertyOrientation.right,
447
+ orientation: CGImagePropertyOrientation.right
448
+ ,
420
449
  options: [:]
421
450
  )
422
451
 
@@ -425,10 +454,12 @@ public class CameraModulePlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureVideoData
425
454
  }
426
455
 
427
456
  // MARK: - UIImagePicker Delegate
457
+
428
458
  extension CameraModulePlugin: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
429
459
 
430
460
  public func imagePickerController(_ picker: UIImagePickerController,
431
461
  didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
462
+
432
463
  picker.dismiss(animated: true)
433
464
 
434
465
  guard let image = info[.originalImage] as? UIImage,
@@ -454,6 +485,7 @@ extension CameraModulePlugin: UIImagePickerControllerDelegate, UINavigationContr
454
485
  }
455
486
 
456
487
  // MARK: - UIImage Resize
488
+
457
489
  extension UIImage {
458
490
  func resized(maxSize: CGFloat) -> UIImage? {
459
491
  let maxSide = max(size.width, size.height)
@@ -468,23 +500,6 @@ extension UIImage {
468
500
  UIGraphicsEndImageContext()
469
501
  return img
470
502
  }
471
- }
472
-
473
- // MARK: - Photo Delegate (si necesitas esta clase)
474
- class PhotoDelegate: NSObject, AVCapturePhotoCaptureDelegate {
475
- private let completion: (String) -> Void
476
503
 
477
- init(completion: @escaping (String) -> Void) {
478
- self.completion = completion
479
- }
480
504
 
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
- }
505
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-camera-module",
3
- "version": "0.0.65",
3
+ "version": "0.0.67",
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",