capacitor-camera-view 2.0.2 → 2.2.0-rc.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 (31) hide show
  1. package/README.md +215 -19
  2. package/android/build.gradle +9 -5
  3. package/android/src/main/AndroidManifest.xml +1 -0
  4. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraView.kt +491 -116
  5. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraViewPlugin.kt +181 -31
  6. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/CameraResult.kt +47 -0
  7. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/CameraSessionConfiguration.kt +11 -1
  8. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/VideoRecordingQuality.kt +10 -0
  9. package/android/src/main/java/com/michaelwolz/capacitorcameraview/utils.kt +114 -5
  10. package/dist/docs.json +281 -8
  11. package/dist/esm/definitions.d.ts +128 -6
  12. package/dist/esm/definitions.js.map +1 -1
  13. package/dist/esm/web.d.ts +26 -4
  14. package/dist/esm/web.js +218 -18
  15. package/dist/esm/web.js.map +1 -1
  16. package/dist/plugin.cjs.js +219 -18
  17. package/dist/plugin.cjs.js.map +1 -1
  18. package/dist/plugin.js +219 -18
  19. package/dist/plugin.js.map +1 -1
  20. package/ios/Sources/CameraViewPlugin/CameraError.swift +125 -2
  21. package/ios/Sources/CameraViewPlugin/CameraEvents.swift +109 -0
  22. package/ios/Sources/CameraViewPlugin/CameraSessionConfiguration.swift +28 -1
  23. package/ios/Sources/CameraViewPlugin/CameraViewManager+BarcodeScan.swift +30 -41
  24. package/ios/Sources/CameraViewPlugin/CameraViewManager+PhotoCapture.swift +38 -7
  25. package/ios/Sources/CameraViewPlugin/CameraViewManager+VideoDataOutput.swift +4 -3
  26. package/ios/Sources/CameraViewPlugin/CameraViewManager+VideoRecording.swift +302 -0
  27. package/ios/Sources/CameraViewPlugin/CameraViewManager.swift +246 -166
  28. package/ios/Sources/CameraViewPlugin/CameraViewPlugin.swift +194 -96
  29. package/ios/Sources/CameraViewPlugin/TempFileManager.swift +215 -0
  30. package/ios/Sources/CameraViewPlugin/Utils.swift +102 -0
  31. package/package.json +17 -17
@@ -0,0 +1,215 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Manages temporary files created during camera capture operations.
5
+ /// Provides automatic cleanup to prevent disk space leaks.
6
+ ///
7
+ /// This singleton tracks all temporary files created by the camera plugin
8
+ /// and provides cleanup at various lifecycle points:
9
+ /// - On app activation (cleans up stale files older than 30 minutes)
10
+ /// - On app termination (cleans up all tracked files)
11
+ public final class TempFileManager: @unchecked Sendable {
12
+ /// Shared singleton instance
13
+ public static let shared = TempFileManager()
14
+
15
+ /// Serial queue for thread-safe file tracking
16
+ private let queue = DispatchQueue(label: "com.michaelwolz.capacitorcameraview.tempFileManager")
17
+
18
+ /// Set of tracked temporary file URLs
19
+ private var trackedFiles = Set<URL>()
20
+
21
+ /// Directory prefix used to identify camera capture temp files
22
+ private let tempFilePrefix = "camera_capture_"
23
+
24
+ /// Stale file threshold in seconds (30 minutes)
25
+ private let staleThresholdSeconds: TimeInterval = 1800
26
+
27
+ private init() {
28
+ setupAppLifecycleObservers()
29
+ }
30
+
31
+ deinit {
32
+ NotificationCenter.default.removeObserver(self)
33
+ }
34
+
35
+ // MARK: - Public API
36
+
37
+ /// Creates a temporary file URL for storing captured images and tracks it for cleanup.
38
+ ///
39
+ /// - Returns: A URL pointing to the temporary file location
40
+ /// - Throws: Never throws, always returns a valid temp directory URL
41
+ public func createTempImageFile() -> URL {
42
+ let timestamp = Int(Date().timeIntervalSince1970 * 1000)
43
+ let fileName = "\(tempFilePrefix)\(timestamp).jpg"
44
+ let tempDir = FileManager.default.temporaryDirectory
45
+ let fileURL = tempDir.appendingPathComponent(fileName)
46
+
47
+ queue.sync {
48
+ _ = trackedFiles.insert(fileURL)
49
+ }
50
+
51
+ return fileURL
52
+ }
53
+
54
+ /// Creates a temporary file URL for storing recorded videos and tracks it for cleanup.
55
+ ///
56
+ /// - Returns: A URL pointing to the temporary video file location
57
+ public func createTempVideoFile() -> URL {
58
+ let timestamp = Int(Date().timeIntervalSince1970 * 1000)
59
+ let fileName = "\(tempFilePrefix)\(timestamp).mp4"
60
+ let tempDir = FileManager.default.temporaryDirectory
61
+ let fileURL = tempDir.appendingPathComponent(fileName)
62
+
63
+ queue.sync {
64
+ _ = trackedFiles.insert(fileURL)
65
+ }
66
+
67
+ return fileURL
68
+ }
69
+
70
+ /// Registers an externally created file for tracking.
71
+ ///
72
+ /// - Parameter url: The URL of the file to track
73
+ public func trackFile(_ url: URL) {
74
+ queue.sync {
75
+ _ = trackedFiles.insert(url)
76
+ }
77
+ }
78
+
79
+ /// Removes a file from tracking without deleting it.
80
+ ///
81
+ /// - Parameter url: The URL of the file to untrack
82
+ public func untrackFile(_ url: URL) {
83
+ queue.sync {
84
+ _ = trackedFiles.remove(url)
85
+ }
86
+ }
87
+
88
+ /// Cleans up all tracked temporary files.
89
+ /// Call this when the camera session stops.
90
+ public func cleanupSessionFiles() {
91
+ queue.async { [weak self] in
92
+ guard let self = self else { return }
93
+
94
+ let filesToDelete = self.trackedFiles
95
+ self.trackedFiles.removeAll()
96
+
97
+ for fileURL in filesToDelete {
98
+ self.deleteFile(at: fileURL)
99
+ }
100
+ }
101
+ }
102
+
103
+ /// Cleans up stale temporary files that are older than the threshold.
104
+ /// This helps recover from cases where cleanup was missed.
105
+ public func cleanupStaleFiles() {
106
+ queue.async { [weak self] in
107
+ guard let self = self else { return }
108
+
109
+ let fileManager = FileManager.default
110
+ let tempDir = fileManager.temporaryDirectory
111
+
112
+ guard let contents = try? fileManager.contentsOfDirectory(
113
+ at: tempDir,
114
+ includingPropertiesForKeys: [.creationDateKey],
115
+ options: .skipsHiddenFiles
116
+ ) else { return }
117
+
118
+ let staleDate = Date().addingTimeInterval(-self.staleThresholdSeconds)
119
+
120
+ for fileURL in contents {
121
+ // Only process our camera capture files
122
+ guard fileURL.lastPathComponent.hasPrefix(self.tempFilePrefix) else { continue }
123
+
124
+ // Check file age
125
+ if let attributes = try? fileManager.attributesOfItem(atPath: fileURL.path),
126
+ let creationDate = attributes[.creationDate] as? Date,
127
+ creationDate < staleDate {
128
+ self.deleteFile(at: fileURL)
129
+ self.trackedFiles.remove(fileURL)
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ /// Cleans up all camera capture temporary files in the temp directory.
136
+ /// Use this for aggressive cleanup on app termination.
137
+ public func cleanupAllCaptureFiles() {
138
+ queue.async { [weak self] in
139
+ guard let self = self else { return }
140
+
141
+ let fileManager = FileManager.default
142
+ let tempDir = fileManager.temporaryDirectory
143
+
144
+ guard let contents = try? fileManager.contentsOfDirectory(
145
+ at: tempDir,
146
+ includingPropertiesForKeys: nil,
147
+ options: .skipsHiddenFiles
148
+ ) else { return }
149
+
150
+ for fileURL in contents {
151
+ if fileURL.lastPathComponent.hasPrefix(self.tempFilePrefix) {
152
+ self.deleteFile(at: fileURL)
153
+ }
154
+ }
155
+
156
+ self.trackedFiles.removeAll()
157
+ }
158
+ }
159
+
160
+ // MARK: - Private Methods
161
+
162
+ private func deleteFile(at url: URL) {
163
+ do {
164
+ try FileManager.default.removeItem(at: url)
165
+ } catch {
166
+ // Silently ignore - file may already be deleted or inaccessible
167
+ }
168
+ }
169
+
170
+ private func setupAppLifecycleObservers() {
171
+ NotificationCenter.default.addObserver(
172
+ self,
173
+ selector: #selector(handleAppDidBecomeActive),
174
+ name: UIApplication.didBecomeActiveNotification,
175
+ object: nil
176
+ )
177
+
178
+ NotificationCenter.default.addObserver(
179
+ self,
180
+ selector: #selector(handleAppWillTerminate),
181
+ name: UIApplication.willTerminateNotification,
182
+ object: nil
183
+ )
184
+ }
185
+
186
+ @objc private func handleAppDidBecomeActive() {
187
+ // Clean up any stale files when app becomes active
188
+ cleanupStaleFiles()
189
+ }
190
+
191
+ @objc private func handleAppWillTerminate() {
192
+ // Clean up all capture files on termination
193
+ // Note: This is synchronous to ensure cleanup happens before termination
194
+ queue.sync { [weak self] in
195
+ guard let self = self else { return }
196
+
197
+ let fileManager = FileManager.default
198
+ let tempDir = fileManager.temporaryDirectory
199
+
200
+ if let contents = try? fileManager.contentsOfDirectory(
201
+ at: tempDir,
202
+ includingPropertiesForKeys: nil,
203
+ options: .skipsHiddenFiles
204
+ ) {
205
+ for fileURL in contents {
206
+ if fileURL.lastPathComponent.hasPrefix(self.tempFilePrefix) {
207
+ try? fileManager.removeItem(at: fileURL)
208
+ }
209
+ }
210
+ }
211
+
212
+ self.trackedFiles.removeAll()
213
+ }
214
+ }
215
+ }
@@ -1,5 +1,7 @@
1
1
  import AVFoundation
2
2
 
3
+ // MARK: - Camera Type Conversion
4
+
3
5
  /// Converts string camera type identifiers from JavaScript to native AVCaptureDevice.DeviceType values
4
6
  /// - Parameter stringType: The string camera type from JavaScript
5
7
  /// - Returns: The corresponding AVCaptureDevice.DeviceType or nil if no match
@@ -56,9 +58,109 @@ public func convertToNativeCameraTypes(_ stringTypes: [String]) -> [AVCaptureDev
56
58
  }
57
59
 
58
60
  /// Creates a temporary file URL for storing captured images
61
+ /// @deprecated Use TempFileManager.shared.createTempImageFile() for automatic cleanup
59
62
  public func createTempImageFile() throws -> URL {
60
63
  let timestamp = Int(Date().timeIntervalSince1970 * 1000)
61
64
  let fileName = "camera_capture_\(timestamp).jpg"
62
65
  let tempDir = FileManager.default.temporaryDirectory
63
66
  return tempDir.appendingPathComponent(fileName)
64
67
  }
68
+
69
+ // MARK: - Barcode Type Conversion
70
+
71
+ /// All supported barcode types for detection.
72
+ /// These are enabled by default when no specific types are configured.
73
+ public let ALL_SUPPORTED_BARCODE_TYPES: [AVMetadataObject.ObjectType] = [
74
+ .qr,
75
+ .code128,
76
+ .code39,
77
+ .code39Mod43,
78
+ .code93,
79
+ .ean8,
80
+ .ean13,
81
+ .interleaved2of5,
82
+ .itf14,
83
+ .pdf417,
84
+ .aztec,
85
+ .dataMatrix,
86
+ .upce
87
+ ]
88
+
89
+ /// Converts a string barcode type identifier from JavaScript to native AVMetadataObject.ObjectType
90
+ /// - Parameter stringType: The string barcode type from JavaScript
91
+ /// - Returns: The corresponding AVMetadataObject.ObjectType or nil if no match
92
+ public func convertToNativeBarcodeType(_ stringType: String) -> AVMetadataObject.ObjectType? {
93
+ switch stringType {
94
+ case "qr":
95
+ return .qr
96
+ case "code128":
97
+ return .code128
98
+ case "code39":
99
+ return .code39
100
+ case "code39Mod43":
101
+ return .code39Mod43
102
+ case "code93":
103
+ return .code93
104
+ case "ean8":
105
+ return .ean8
106
+ case "ean13":
107
+ return .ean13
108
+ case "interleaved2of5":
109
+ return .interleaved2of5
110
+ case "itf14":
111
+ return .itf14
112
+ case "pdf417":
113
+ return .pdf417
114
+ case "aztec":
115
+ return .aztec
116
+ case "dataMatrix":
117
+ return .dataMatrix
118
+ case "upce":
119
+ return .upce
120
+ default:
121
+ return nil
122
+ }
123
+ }
124
+
125
+ /// Converts AVMetadataObject.ObjectType to JavaScript string value
126
+ /// - Parameter barcodeType: The AVMetadataObject.ObjectType
127
+ /// - Returns: The corresponding string or nil if no match
128
+ public func convertToStringBarcodeType(_ barcodeType: AVMetadataObject.ObjectType) -> String? {
129
+ switch barcodeType {
130
+ case .qr:
131
+ return "qr"
132
+ case .code128:
133
+ return "code128"
134
+ case .code39:
135
+ return "code39"
136
+ case .code39Mod43:
137
+ return "code39Mod43"
138
+ case .code93:
139
+ return "code93"
140
+ case .ean8:
141
+ return "ean8"
142
+ case .ean13:
143
+ return "ean13"
144
+ case .interleaved2of5:
145
+ return "interleaved2of5"
146
+ case .itf14:
147
+ return "itf14"
148
+ case .pdf417:
149
+ return "pdf417"
150
+ case .aztec:
151
+ return "aztec"
152
+ case .dataMatrix:
153
+ return "dataMatrix"
154
+ case .upce:
155
+ return "upce"
156
+ default:
157
+ return nil
158
+ }
159
+ }
160
+
161
+ /// Converts an array of string barcode type identifiers to native AVMetadataObject.ObjectType values
162
+ /// - Parameter stringTypes: Array of string barcode types from JavaScript
163
+ /// - Returns: Array of AVMetadataObject.ObjectType values (invalid types are filtered out)
164
+ public func convertToNativeBarcodeTypes(_ stringTypes: [String]) -> [AVMetadataObject.ObjectType] {
165
+ return stringTypes.compactMap { convertToNativeBarcodeType($0) }
166
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-camera-view",
3
- "version": "2.0.2",
3
+ "version": "2.2.0-rc.1",
4
4
  "description": "A Capacitor plugin for embedding a live camera feed directly into your app.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -48,33 +48,33 @@
48
48
  "prepublishOnly": "npm run build"
49
49
  },
50
50
  "devDependencies": {
51
- "@capacitor/android": "^8.0.0",
52
- "@capacitor/core": "^8.0.0",
51
+ "@capacitor/android": "^8.1.0",
52
+ "@capacitor/core": "^8.1.0",
53
53
  "@capacitor/docgen": "^0.3.1",
54
- "@capacitor/ios": "^8.0.0",
55
- "@commitlint/config-conventional": "^20.3.0",
54
+ "@capacitor/ios": "^8.1.0",
55
+ "@commitlint/config-conventional": "^20.4.2",
56
56
  "@ionic/prettier-config": "^4.0.0",
57
57
  "@ionic/swiftlint-config": "^2.0.0",
58
58
  "@semantic-release/changelog": "^6.0.3",
59
59
  "@semantic-release/commit-analyzer": "^13.0.1",
60
60
  "@semantic-release/git": "^10.0.1",
61
- "@semantic-release/github": "^12.0.2",
62
- "@semantic-release/npm": "^13.1.3",
61
+ "@semantic-release/github": "^12.0.6",
62
+ "@semantic-release/npm": "^13.1.4",
63
63
  "@semantic-release/release-notes-generator": "^14.1.0",
64
- "@typescript-eslint/eslint-plugin": "^8.51.0",
65
- "@typescript-eslint/parser": "^8.51.0",
66
- "commitlint": "^20.3.0",
64
+ "@typescript-eslint/eslint-plugin": "^8.56.0",
65
+ "@typescript-eslint/parser": "^8.56.0",
66
+ "commitlint": "^20.4.2",
67
67
  "eslint": "^9.39.2",
68
68
  "eslint-config-prettier": "^10.1.8",
69
- "eslint-plugin-prettier": "^5.5.4",
69
+ "eslint-plugin-prettier": "^5.5.5",
70
70
  "husky": "^9.1.7",
71
- "prettier": "^3.7.4",
72
- "prettier-plugin-java": "^2.7.7",
71
+ "prettier": "^3.8.1",
72
+ "prettier-plugin-java": "^2.8.1",
73
73
  "prettier-plugin-organize-imports": "^4.3.0",
74
- "prettier-plugin-packagejson": "^2.5.20",
75
- "rimraf": "^6.1.2",
76
- "rollup": "^4.54.0",
77
- "semantic-release": "^25.0.2",
74
+ "prettier-plugin-packagejson": "^3.0.0",
75
+ "rimraf": "^6.1.3",
76
+ "rollup": "^4.58.0",
77
+ "semantic-release": "^25.0.3",
78
78
  "swiftlint": "^2.0.0",
79
79
  "typescript": "^5.9.3"
80
80
  },