@capgo/camera-preview 7.3.9 → 7.4.0-beta.1

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.
Files changed (55) hide show
  1. package/CapgoCameraPreview.podspec +16 -13
  2. package/README.md +298 -68
  3. package/android/.gradle/8.14.2/checksums/checksums.lock +0 -0
  4. package/android/.gradle/8.14.2/checksums/sha1-checksums.bin +0 -0
  5. package/android/.gradle/8.14.2/executionHistory/executionHistory.bin +0 -0
  6. package/android/.gradle/8.14.2/executionHistory/executionHistory.lock +0 -0
  7. package/android/.gradle/8.14.2/fileChanges/last-build.bin +0 -0
  8. package/android/.gradle/8.14.2/fileHashes/fileHashes.bin +0 -0
  9. package/android/.gradle/8.14.2/fileHashes/fileHashes.lock +0 -0
  10. package/android/.gradle/8.14.2/fileHashes/resourceHashesCache.bin +0 -0
  11. package/android/.gradle/8.14.2/gc.properties +0 -0
  12. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  13. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  14. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  15. package/android/.gradle/file-system.probe +0 -0
  16. package/android/.gradle/vcs-1/gc.properties +0 -0
  17. package/android/build.gradle +9 -0
  18. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +239 -545
  19. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +848 -0
  20. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +54 -0
  21. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +70 -0
  22. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +65 -0
  23. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +34 -0
  24. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +34 -0
  25. package/dist/docs.json +702 -151
  26. package/dist/esm/definitions.d.ts +326 -80
  27. package/dist/esm/definitions.js +10 -1
  28. package/dist/esm/definitions.js.map +1 -1
  29. package/dist/esm/web.d.ts +27 -1
  30. package/dist/esm/web.js +244 -4
  31. package/dist/esm/web.js.map +1 -1
  32. package/dist/plugin.cjs.js +254 -4
  33. package/dist/plugin.cjs.js.map +1 -1
  34. package/dist/plugin.js +254 -4
  35. package/dist/plugin.js.map +1 -1
  36. package/ios/{Plugin → Sources/CapgoCameraPreview}/CameraController.swift +359 -34
  37. package/ios/{Plugin → Sources/CapgoCameraPreview}/Plugin.swift +307 -16
  38. package/ios/Tests/CameraPreviewPluginTests/CameraPreviewPluginTests.swift +15 -0
  39. package/package.json +1 -1
  40. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraActivity.java +0 -1279
  41. package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomSurfaceView.java +0 -29
  42. package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomTextureView.java +0 -39
  43. package/android/src/main/java/com/ahm/capacitor/camera/preview/Preview.java +0 -461
  44. package/android/src/main/java/com/ahm/capacitor/camera/preview/TapGestureDetector.java +0 -24
  45. package/ios/Plugin/Info.plist +0 -24
  46. package/ios/Plugin/Plugin.h +0 -10
  47. package/ios/Plugin/Plugin.m +0 -18
  48. package/ios/Plugin.xcodeproj/project.pbxproj +0 -593
  49. package/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  50. package/ios/Plugin.xcworkspace/contents.xcworkspacedata +0 -10
  51. package/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  52. package/ios/PluginTests/Info.plist +0 -22
  53. package/ios/PluginTests/PluginTests.swift +0 -83
  54. package/ios/Podfile +0 -13
  55. package/ios/Podfile.lock +0 -23
@@ -47,7 +47,14 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
47
47
  CAPPluginMethod(name: "startRecordVideo", returnType: CAPPluginReturnPromise),
48
48
  CAPPluginMethod(name: "stopRecordVideo", returnType: CAPPluginReturnPromise),
49
49
  CAPPluginMethod(name: "getTempFilePath", returnType: CAPPluginReturnPromise),
50
- CAPPluginMethod(name: "getSupportedPictureSizes", returnType: CAPPluginReturnPromise)
50
+ CAPPluginMethod(name: "getSupportedPictureSizes", returnType: CAPPluginReturnPromise),
51
+ CAPPluginMethod(name: "isRunning", returnType: CAPPluginReturnPromise),
52
+ CAPPluginMethod(name: "getAvailableDevices", returnType: CAPPluginReturnPromise),
53
+ CAPPluginMethod(name: "getZoom", returnType: CAPPluginReturnPromise),
54
+ CAPPluginMethod(name: "setZoom", returnType: CAPPluginReturnPromise),
55
+ CAPPluginMethod(name: "getFlashMode", returnType: CAPPluginReturnPromise),
56
+ CAPPluginMethod(name: "setDeviceId", returnType: CAPPluginReturnPromise),
57
+ CAPPluginMethod(name: "getDeviceId", returnType: CAPPluginReturnPromise)
51
58
  ]
52
59
  // Camera state tracking
53
60
  private var isInitializing: Bool = false
@@ -68,6 +75,39 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
68
75
  var highResolutionOutput: Bool = false
69
76
  var disableAudio: Bool = false
70
77
 
78
+ // MARK: - Transparency Methods
79
+
80
+ private func makeWebViewTransparent() {
81
+ guard let webView = self.webView else { return }
82
+
83
+ // Define a recursive function to traverse the view hierarchy
84
+ func makeSubviewsTransparent(_ view: UIView) {
85
+ // Set the background color to clear
86
+ view.backgroundColor = .clear
87
+
88
+ // Recurse for all subviews
89
+ for subview in view.subviews {
90
+ makeSubviewsTransparent(subview)
91
+ }
92
+ }
93
+
94
+ // Set the main webView to be transparent
95
+ webView.isOpaque = false
96
+ webView.backgroundColor = .clear
97
+
98
+ // Recursively make all subviews transparent
99
+ makeSubviewsTransparent(webView)
100
+
101
+ // Also ensure the webview's container is transparent
102
+ webView.superview?.backgroundColor = .clear
103
+
104
+ // Force a layout pass to apply changes
105
+ DispatchQueue.main.async {
106
+ webView.setNeedsLayout()
107
+ webView.layoutIfNeeded()
108
+ }
109
+ }
110
+
71
111
  @objc func rotated() {
72
112
  guard let previewView = self.previewView,
73
113
  let posX = self.posX,
@@ -105,6 +145,27 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
105
145
  }
106
146
 
107
147
  cameraController.updateVideoOrientation()
148
+
149
+ // Ensure webview remains transparent after rotation
150
+ if self.isInitialized {
151
+ self.makeWebViewTransparent()
152
+ }
153
+ }
154
+
155
+ @objc func appDidBecomeActive() {
156
+ if self.isInitialized {
157
+ DispatchQueue.main.async {
158
+ self.makeWebViewTransparent()
159
+ }
160
+ }
161
+ }
162
+
163
+ @objc func appWillEnterForeground() {
164
+ if self.isInitialized {
165
+ DispatchQueue.main.async {
166
+ self.makeWebViewTransparent()
167
+ }
168
+ }
108
169
  }
109
170
 
110
171
  struct CameraInfo {
@@ -119,8 +180,11 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
119
180
  // Discover all available cameras
120
181
  let deviceTypes: [AVCaptureDevice.DeviceType] = [
121
182
  .builtInWideAngleCamera,
122
- .builtInTelephotoCamera,
123
183
  .builtInUltraWideCamera,
184
+ .builtInTelephotoCamera,
185
+ .builtInDualCamera,
186
+ .builtInDualWideCamera,
187
+ .builtInTripleCamera,
124
188
  .builtInTrueDepthCamera
125
189
  ]
126
190
 
@@ -197,6 +261,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
197
261
  self.isInitializing = true
198
262
 
199
263
  self.cameraPosition = call.getString("position") ?? "rear"
264
+ let deviceId = call.getString("deviceId")
200
265
  let cameraMode = call.getBool("cameraMode") ?? false
201
266
  self.highResolutionOutput = call.getBool("enableHighResolution") ?? false
202
267
  self.cameraController.highResolutionOutput = self.highResolutionOutput
@@ -218,7 +283,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
218
283
  }
219
284
 
220
285
  self.rotateWhenOrientationChanged = call.getBool("rotateWhenOrientationChanged") ?? true
221
- self.toBack = call.getBool("toBack") ?? false
286
+ self.toBack = call.getBool("toBack") ?? true
222
287
  self.storeToFile = call.getBool("storeToFile") ?? false
223
288
  self.enableZoom = call.getBool("enableZoom") ?? false
224
289
  self.disableAudio = call.getBool("disableAudio") ?? false
@@ -233,7 +298,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
233
298
  if self.cameraController.captureSession?.isRunning ?? false {
234
299
  call.reject("camera already started")
235
300
  } else {
236
- self.cameraController.prepare(cameraPosition: self.cameraPosition, disableAudio: self.disableAudio, cameraMode: cameraMode) {error in
301
+ self.cameraController.prepare(cameraPosition: self.cameraPosition, deviceId: deviceId, disableAudio: self.disableAudio, cameraMode: cameraMode) {error in
237
302
  if let error = error {
238
303
  print(error)
239
304
  call.reject(error.localizedDescription)
@@ -241,9 +306,10 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
241
306
  }
242
307
  let height = self.paddingBottom != nil ? self.height! - self.paddingBottom!: self.height!
243
308
  self.previewView = UIView(frame: CGRect(x: self.posX ?? 0, y: self.posY ?? 0, width: self.width!, height: height))
244
- self.webView?.isOpaque = false
245
- self.webView?.backgroundColor = UIColor.clear
246
- self.webView?.scrollView.backgroundColor = UIColor.clear
309
+
310
+ // Make webview transparent - comprehensive approach
311
+ self.makeWebViewTransparent()
312
+
247
313
  self.webView?.superview?.addSubview(self.previewView)
248
314
  if self.toBack! {
249
315
  self.webView?.superview?.bringSubviewToFront(self.webView!)
@@ -256,6 +322,10 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
256
322
  if self.rotateWhenOrientationChanged == true {
257
323
  NotificationCenter.default.addObserver(self, selector: #selector(CameraPreview.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
258
324
  }
325
+
326
+ // Add observers for app state changes to maintain transparency
327
+ NotificationCenter.default.addObserver(self, selector: #selector(CameraPreview.appDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
328
+ NotificationCenter.default.addObserver(self, selector: #selector(CameraPreview.appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
259
329
 
260
330
  self.isInitializing = false
261
331
  self.isInitialized = true
@@ -273,34 +343,38 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
273
343
  call.reject("Camera not initialized")
274
344
  return
275
345
  }
276
-
346
+
277
347
  DispatchQueue.main.async { [weak self] in
278
348
  guard let self = self else {
279
349
  call.reject("Camera controller deallocated")
280
350
  return
281
351
  }
282
-
352
+
283
353
  // Disable user interaction during flip
284
354
  self.previewView.isUserInteractionEnabled = false
285
-
355
+
286
356
  // Perform camera switch on background thread
287
357
  DispatchQueue.global(qos: .userInitiated).async {
288
358
  var retryCount = 0
289
359
  let maxRetries = 3
290
-
360
+
291
361
  func attemptFlip() {
292
362
  do {
293
363
  try self.cameraController.switchCameras()
294
-
364
+
295
365
  DispatchQueue.main.async {
296
366
  self.cameraController.previewLayer?.frame = self.previewView.bounds
297
367
  self.cameraController.previewLayer?.videoGravity = .resizeAspectFill
298
368
  self.previewView.isUserInteractionEnabled = true
369
+
370
+ // Ensure webview remains transparent after flip
371
+ self.makeWebViewTransparent()
372
+
299
373
  call.resolve()
300
374
  }
301
375
  } catch {
302
376
  retryCount += 1
303
-
377
+
304
378
  if retryCount < maxRetries {
305
379
  DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 0.5) {
306
380
  attemptFlip()
@@ -314,7 +388,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
314
388
  }
315
389
  }
316
390
  }
317
-
391
+
318
392
  attemptFlip()
319
393
  }
320
394
  }
@@ -330,18 +404,21 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
330
404
  call.reject("camera not initialized")
331
405
  return
332
406
  }
333
-
407
+
334
408
  // Always attempt to stop and clean up, regardless of captureSession state
335
409
  if let previewView = self.previewView {
336
410
  previewView.removeFromSuperview()
337
411
  self.previewView = nil
338
412
  }
339
-
413
+
340
414
  self.webView?.isOpaque = true
341
415
  self.isInitialized = false
342
416
  self.isInitializing = false
343
417
  self.cameraController.cleanup()
344
418
 
419
+ // Remove notification observers
420
+ NotificationCenter.default.removeObserver(self)
421
+
345
422
  call.resolve()
346
423
  }
347
424
  }
@@ -508,4 +585,218 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
508
585
  }
509
586
  }
510
587
 
588
+ @objc func isRunning(_ call: CAPPluginCall) {
589
+ let isRunning = self.isInitialized && (self.cameraController.captureSession?.isRunning ?? false)
590
+ call.resolve(["isRunning": isRunning])
591
+ }
592
+
593
+ @objc func getAvailableDevices(_ call: CAPPluginCall) {
594
+ let deviceTypes: [AVCaptureDevice.DeviceType] = [
595
+ .builtInWideAngleCamera,
596
+ .builtInUltraWideCamera,
597
+ .builtInTelephotoCamera,
598
+ .builtInDualCamera,
599
+ .builtInDualWideCamera,
600
+ .builtInTripleCamera,
601
+ .builtInTrueDepthCamera
602
+ ]
603
+
604
+ let session = AVCaptureDevice.DiscoverySession(
605
+ deviceTypes: deviceTypes,
606
+ mediaType: .video,
607
+ position: .unspecified
608
+ )
609
+
610
+ var devices: [[String: Any]] = []
611
+
612
+ // Collect all devices by position
613
+ for device in session.devices {
614
+ var lenses: [[String: Any]] = []
615
+
616
+ let constituentDevices = device.isVirtualDevice ? device.constituentDevices : [device]
617
+
618
+ for lensDevice in constituentDevices {
619
+ var deviceType: String
620
+ switch lensDevice.deviceType {
621
+ case .builtInWideAngleCamera: deviceType = "wideAngle"
622
+ case .builtInUltraWideCamera: deviceType = "ultraWide"
623
+ case .builtInTelephotoCamera: deviceType = "telephoto"
624
+ case .builtInDualCamera: deviceType = "dual"
625
+ case .builtInDualWideCamera: deviceType = "dualWide"
626
+ case .builtInTripleCamera: deviceType = "triple"
627
+ case .builtInTrueDepthCamera: deviceType = "trueDepth"
628
+ default: deviceType = "unknown"
629
+ }
630
+
631
+ var baseZoomRatio: Float = 1.0
632
+ if lensDevice.deviceType == .builtInUltraWideCamera {
633
+ baseZoomRatio = 0.5
634
+ } else if lensDevice.deviceType == .builtInTelephotoCamera {
635
+ baseZoomRatio = 2.0 // A common value for telephoto lenses
636
+ }
637
+
638
+ let lensInfo: [String: Any] = [
639
+ "label": lensDevice.localizedName,
640
+ "deviceType": deviceType,
641
+ "focalLength": 4.25, // Placeholder
642
+ "baseZoomRatio": baseZoomRatio,
643
+ "minZoom": Float(lensDevice.minAvailableVideoZoomFactor),
644
+ "maxZoom": Float(lensDevice.maxAvailableVideoZoomFactor)
645
+ ]
646
+ lenses.append(lensInfo)
647
+ }
648
+
649
+ let deviceData: [String: Any] = [
650
+ "deviceId": device.uniqueID,
651
+ "label": device.localizedName,
652
+ "position": device.position == .front ? "front" : "rear",
653
+ "lenses": lenses,
654
+ "minZoom": Float(device.minAvailableVideoZoomFactor),
655
+ "maxZoom": Float(device.maxAvailableVideoZoomFactor),
656
+ "isLogical": device.isVirtualDevice
657
+ ]
658
+
659
+ devices.append(deviceData)
660
+ }
661
+
662
+ call.resolve(["devices": devices])
663
+ }
664
+
665
+ @objc func getZoom(_ call: CAPPluginCall) {
666
+ guard isInitialized else {
667
+ call.reject("Camera not initialized")
668
+ return
669
+ }
670
+
671
+ do {
672
+ let zoomInfo = try self.cameraController.getZoom()
673
+ let lensInfo = try self.cameraController.getCurrentLensInfo()
674
+
675
+ var minZoom = zoomInfo.min
676
+ var maxZoom = zoomInfo.max
677
+ var currentZoom = zoomInfo.current
678
+
679
+ // If using the multi-lens camera, translate the native zoom values for JS
680
+ if self.cameraController.isUsingMultiLensVirtualCamera {
681
+ minZoom -= 0.5
682
+ maxZoom -= 0.5
683
+ currentZoom -= 0.5
684
+ }
685
+
686
+ call.resolve([
687
+ "min": minZoom,
688
+ "max": maxZoom,
689
+ "current": currentZoom,
690
+ "lens": [
691
+ "focalLength": lensInfo.focalLength,
692
+ "deviceType": lensInfo.deviceType,
693
+ "baseZoomRatio": lensInfo.baseZoomRatio,
694
+ "digitalZoom": Float(currentZoom) / lensInfo.baseZoomRatio
695
+ ]
696
+ ])
697
+ } catch {
698
+ call.reject("Failed to get zoom: \(error.localizedDescription)")
699
+ }
700
+ }
701
+
702
+ @objc func setZoom(_ call: CAPPluginCall) {
703
+ guard isInitialized else {
704
+ call.reject("Camera not initialized")
705
+ return
706
+ }
707
+
708
+ guard var level = call.getFloat("level") else {
709
+ call.reject("level parameter is required")
710
+ return
711
+ }
712
+
713
+ // If using the multi-lens camera, translate the JS zoom value for the native layer
714
+ if self.cameraController.isUsingMultiLensVirtualCamera {
715
+ level += 0.5
716
+ }
717
+
718
+ let ramp = call.getBool("ramp") ?? true
719
+
720
+ do {
721
+ try self.cameraController.setZoom(level: CGFloat(level), ramp: ramp)
722
+ call.resolve()
723
+ } catch {
724
+ call.reject("Failed to set zoom: \(error.localizedDescription)")
725
+ }
726
+ }
727
+
728
+ @objc func getFlashMode(_ call: CAPPluginCall) {
729
+ guard isInitialized else {
730
+ call.reject("Camera not initialized")
731
+ return
732
+ }
733
+
734
+ do {
735
+ let flashMode = try self.cameraController.getFlashMode()
736
+ call.resolve(["flashMode": flashMode])
737
+ } catch {
738
+ call.reject("Failed to get flash mode: \(error.localizedDescription)")
739
+ }
740
+ }
741
+
742
+ @objc func setDeviceId(_ call: CAPPluginCall) {
743
+ guard isInitialized else {
744
+ call.reject("Camera not initialized")
745
+ return
746
+ }
747
+
748
+ guard let deviceId = call.getString("deviceId") else {
749
+ call.reject("deviceId parameter is required")
750
+ return
751
+ }
752
+
753
+ DispatchQueue.main.async { [weak self] in
754
+ guard let self = self else {
755
+ call.reject("Camera controller deallocated")
756
+ return
757
+ }
758
+
759
+ // Disable user interaction during device swap
760
+ self.previewView.isUserInteractionEnabled = false
761
+
762
+ DispatchQueue.global(qos: .userInitiated).async {
763
+ do {
764
+ try self.cameraController.swapToDevice(deviceId: deviceId)
765
+
766
+ DispatchQueue.main.async {
767
+ self.cameraController.previewLayer?.frame = self.previewView.bounds
768
+ self.cameraController.previewLayer?.videoGravity = .resizeAspectFill
769
+ self.previewView.isUserInteractionEnabled = true
770
+
771
+ // Ensure webview remains transparent after device switch
772
+ self.makeWebViewTransparent()
773
+
774
+ call.resolve()
775
+ }
776
+ } catch {
777
+ DispatchQueue.main.async {
778
+ self.previewView.isUserInteractionEnabled = true
779
+ call.reject("Failed to swap to device \(deviceId): \(error.localizedDescription)")
780
+ }
781
+ }
782
+ }
783
+ }
784
+ }
785
+
786
+ @objc func getDeviceId(_ call: CAPPluginCall) {
787
+ guard isInitialized else {
788
+ call.reject("Camera not initialized")
789
+ return
790
+ }
791
+
792
+ do {
793
+ let deviceId = try self.cameraController.getCurrentDeviceId()
794
+ call.resolve(["deviceId": deviceId])
795
+ } catch {
796
+ call.reject("Failed to get device ID: \(error.localizedDescription)")
797
+ }
798
+ }
799
+
800
+
801
+
511
802
  }
@@ -0,0 +1,15 @@
1
+ import XCTest
2
+ @testable import CameraViewPlugin
3
+
4
+ class CameraViewTests: XCTestCase {
5
+ func testEcho() {
6
+ // This is an example of a functional test case for a plugin.
7
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
8
+
9
+ let implementation = CameraView()
10
+ let value = "Hello, World!"
11
+ let result = implementation.echo(value)
12
+
13
+ XCTAssertEqual(value, result)
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/camera-preview",
3
- "version": "7.3.9",
3
+ "version": "7.4.0-beta.1",
4
4
  "description": "Camera preview",
5
5
  "license": "MIT",
6
6
  "repository": {