Committed by
GitHub
Merge pull request #352 from juliansteenbakker/imp/return-image-ios
fix: fixed build issue for ios
Showing
2 changed files
with
82 additions
and
315 deletions
| 1 | -import AVFoundation | ||
| 2 | import Flutter | 1 | import Flutter |
| 3 | import MLKitVision | 2 | import MLKitVision |
| 4 | import MLKitBarcodeScanning | 3 | import MLKitBarcodeScanning |
| 4 | +import AVFoundation | ||
| 5 | 5 | ||
| 6 | -public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate { | ||
| 7 | - | ||
| 8 | - let registry: FlutterTextureRegistry | ||
| 9 | - | ||
| 10 | - // Sink for publishing event changes | ||
| 11 | - var sink: FlutterEventSink! | ||
| 12 | - | ||
| 13 | - // Texture id of the camera preview | ||
| 14 | - var textureId: Int64! | ||
| 15 | - | ||
| 16 | - // Capture session of the camera | ||
| 17 | - var captureSession: AVCaptureSession! | ||
| 18 | - | ||
| 19 | - // The selected camera | ||
| 20 | - var device: AVCaptureDevice! | ||
| 21 | - | ||
| 22 | - // Image to be sent to the texture | ||
| 23 | - var latestBuffer: CVImageBuffer! | ||
| 24 | - | ||
| 25 | - // Return image buffer with the Barcode event | ||
| 26 | - var returnImage: Bool = false | ||
| 27 | - | ||
| 28 | -// var analyzeMode: Int = 0 | ||
| 29 | - var analyzing: Bool = false | ||
| 30 | - var position = AVCaptureDevice.Position.back | 6 | +public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { |
| 31 | 7 | ||
| 32 | - var scanner = BarcodeScanner.barcodeScanner() | 8 | + /// The mobile scanner object that handles all logic |
| 9 | + private let mobileScanner: MobileScanner | ||
| 33 | 10 | ||
| 34 | - public static func register(with registrar: FlutterPluginRegistrar) { | ||
| 35 | - let instance = SwiftMobileScannerPlugin(registrar.textures()) | 11 | + /// The handler sends all information via an event channel back to Flutter |
| 12 | + private let barcodeHandler: BarcodeHandler | ||
| 36 | 13 | ||
| 37 | - let method = FlutterMethodChannel(name: | ||
| 38 | - "dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: registrar.messenger()) | ||
| 39 | - let event = FlutterEventChannel(name: | ||
| 40 | - "dev.steenbakker.mobile_scanner/scanner/event", binaryMessenger: registrar.messenger()) | ||
| 41 | - registrar.addMethodCallDelegate(instance, channel: method) | ||
| 42 | - event.setStreamHandler(instance) | 14 | + init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) { |
| 15 | + self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in | ||
| 16 | + if barcodes != nil { | ||
| 17 | + let barcodesMap = barcodes!.map { barcode in | ||
| 18 | + return barcode.data | ||
| 43 | } | 19 | } |
| 44 | - | ||
| 45 | - init(_ registry: FlutterTextureRegistry) { | ||
| 46 | - self.registry = registry | 20 | + if (!barcodesMap.isEmpty) { |
| 21 | + barcodeHandler.publishEvent(["name": "barcode", "data": barcodesMap, "image": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!)]) | ||
| 22 | + } | ||
| 23 | + } else if (error != nil){ | ||
| 24 | + barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription]) | ||
| 25 | + } | ||
| 26 | + }) | ||
| 27 | + self.barcodeHandler = barcodeHandler | ||
| 47 | super.init() | 28 | super.init() |
| 48 | } | 29 | } |
| 49 | 30 | ||
| 31 | + public static func register(with registrar: FlutterPluginRegistrar) { | ||
| 32 | + let instance = SwiftMobileScannerPlugin(barcodeHandler: BarcodeHandler(registrar: registrar), registry: registrar.textures()) | ||
| 33 | + let methodChannel = FlutterMethodChannel(name: | ||
| 34 | + "dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: registrar.messenger()) | ||
| 35 | + registrar.addMethodCallDelegate(instance, channel: methodChannel) | ||
| 36 | + } | ||
| 50 | 37 | ||
| 51 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { | 38 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
| 52 | switch call.method { | 39 | switch call.method { |
| 53 | case "state": | 40 | case "state": |
| 54 | - checkPermission(call, result) | 41 | + result(mobileScanner.checkPermission()) |
| 55 | case "request": | 42 | case "request": |
| 56 | - requestPermission(call, result) | 43 | + AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) }) |
| 57 | case "start": | 44 | case "start": |
| 58 | start(call, result) | 45 | start(call, result) |
| 59 | - case "torch": | ||
| 60 | - toggleTorch(call, result) | ||
| 61 | -// case "analyze": | ||
| 62 | -// switchAnalyzeMode(call, result) | ||
| 63 | case "stop": | 46 | case "stop": |
| 64 | stop(result) | 47 | stop(result) |
| 48 | + case "torch": | ||
| 49 | + toggleTorch(call, result) | ||
| 65 | case "analyzeImage": | 50 | case "analyzeImage": |
| 66 | analyzeImage(call, result) | 51 | analyzeImage(call, result) |
| 67 | - | ||
| 68 | default: | 52 | default: |
| 69 | result(FlutterMethodNotImplemented) | 53 | result(FlutterMethodNotImplemented) |
| 70 | } | 54 | } |
| 71 | } | 55 | } |
| 72 | 56 | ||
| 73 | - // FlutterStreamHandler | ||
| 74 | - public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { | ||
| 75 | - sink = events | ||
| 76 | - return nil | ||
| 77 | - } | ||
| 78 | - | ||
| 79 | - // FlutterStreamHandler | ||
| 80 | - public func onCancel(withArguments arguments: Any?) -> FlutterError? { | ||
| 81 | - sink = nil | ||
| 82 | - return nil | ||
| 83 | - } | ||
| 84 | - | ||
| 85 | - // FlutterTexture | ||
| 86 | - public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? { | ||
| 87 | - if latestBuffer == nil { | ||
| 88 | - return nil | ||
| 89 | - } | ||
| 90 | - return Unmanaged<CVPixelBuffer>.passRetained(latestBuffer) | ||
| 91 | - } | ||
| 92 | - | ||
| 93 | - private func ciImageToJpeg(ciImage: CIImage) -> Data { | ||
| 94 | - | ||
| 95 | - // let ciImage = CIImage(cvPixelBuffer: latestBuffer) | ||
| 96 | - let context:CIContext = CIContext.init(options: nil) | ||
| 97 | - let cgImage:CGImage = context.createCGImage(ciImage, from: ciImage.extent)! | ||
| 98 | - let uiImage:UIImage = UIImage(cgImage: cgImage, scale: 1, orientation: UIImage.Orientation.up) | ||
| 99 | - | ||
| 100 | - return uiImage.jpegData(compressionQuality: 0.8)!; | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | - // Gets called when a new image is added to the buffer | ||
| 104 | - public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { | ||
| 105 | - | ||
| 106 | - latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) | ||
| 107 | - registry.textureFrameAvailable(textureId) | ||
| 108 | - | ||
| 109 | -// switch analyzeMode { | ||
| 110 | -// case 1: // barcode | ||
| 111 | - if analyzing { | ||
| 112 | - return | ||
| 113 | - } | ||
| 114 | - analyzing = true | ||
| 115 | - let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) | ||
| 116 | - let image = VisionImage(image: buffer!.image) | ||
| 117 | - image.orientation = imageOrientation( | ||
| 118 | - deviceOrientation: UIDevice.current.orientation, | ||
| 119 | - defaultOrientation: .portrait | ||
| 120 | - ) | ||
| 121 | - | ||
| 122 | - scanner.process(image) { [self] barcodes, error in | ||
| 123 | - if error == nil && barcodes != nil { | ||
| 124 | - for barcode in barcodes! { | ||
| 125 | - | ||
| 126 | - var event: [String: Any?] = ["name": "barcode", "data": barcode.data] | ||
| 127 | - if (returnImage && latestBuffer != nil) { | ||
| 128 | - let image: CIImage = CIImage(cvPixelBuffer: latestBuffer) | ||
| 129 | - | ||
| 130 | - event["image"] = FlutterStandardTypedData(bytes: ciImageToJpeg(ciImage: image)) | ||
| 131 | - } | ||
| 132 | - sink?(event) | ||
| 133 | - } | ||
| 134 | - } | ||
| 135 | - analyzing = false | ||
| 136 | - } | ||
| 137 | -// default: // none | ||
| 138 | -// break | ||
| 139 | -// } | ||
| 140 | - } | ||
| 141 | - | ||
| 142 | - func imageOrientation( | ||
| 143 | - deviceOrientation: UIDeviceOrientation, | ||
| 144 | - defaultOrientation: UIDeviceOrientation | ||
| 145 | - ) -> UIImage.Orientation { | ||
| 146 | - switch deviceOrientation { | ||
| 147 | - case .portrait: | ||
| 148 | - return position == .front ? .leftMirrored : .right | ||
| 149 | - case .landscapeLeft: | ||
| 150 | - return position == .front ? .downMirrored : .up | ||
| 151 | - case .portraitUpsideDown: | ||
| 152 | - return position == .front ? .rightMirrored : .left | ||
| 153 | - case .landscapeRight: | ||
| 154 | - return position == .front ? .upMirrored : .down | ||
| 155 | - case .faceDown, .faceUp, .unknown: | ||
| 156 | - return .up | ||
| 157 | - @unknown default: | ||
| 158 | - return imageOrientation(deviceOrientation: defaultOrientation, defaultOrientation: .portrait) | ||
| 159 | - } | ||
| 160 | - } | ||
| 161 | - | ||
| 162 | - func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 163 | - let status = AVCaptureDevice.authorizationStatus(for: .video) | ||
| 164 | - switch status { | ||
| 165 | - case .notDetermined: | ||
| 166 | - result(0) | ||
| 167 | - case .authorized: | ||
| 168 | - result(1) | ||
| 169 | - default: | ||
| 170 | - result(2) | ||
| 171 | - } | ||
| 172 | - } | ||
| 173 | - | ||
| 174 | - func requestPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 175 | - AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) }) | ||
| 176 | - } | ||
| 177 | - | ||
| 178 | - func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 179 | - if (device != nil) { | ||
| 180 | - result(FlutterError(code: "MobileScanner", | ||
| 181 | - message: "Called start() while already started!", | ||
| 182 | - details: nil)) | ||
| 183 | - return | ||
| 184 | - } | ||
| 185 | - | ||
| 186 | - textureId = registry.register(self) | ||
| 187 | - captureSession = AVCaptureSession() | 57 | + /// Parses all parameters and starts the mobileScanner |
| 58 | + private func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 59 | + // let ratio: Int = (call.arguments as! Dictionary<String, Any?>)["ratio"] as! Int | ||
| 60 | + let torch: Bool = (call.arguments as! Dictionary<String, Any?>)["torch"] as? Bool ?? false | ||
| 61 | + let facing: Int = (call.arguments as! Dictionary<String, Any?>)["facing"] as? Int ?? 1 | ||
| 62 | + let formats: Array<Int> = (call.arguments as! Dictionary<String, Any?>)["formats"] as? Array ?? [] | ||
| 63 | + let returnImage: Bool = (call.arguments as! Dictionary<String, Any?>)["returnImage"] as? Bool ?? false | ||
| 188 | 64 | ||
| 189 | - let argReader = MapArgumentReader(call.arguments as? [String: Any]) | 65 | + let formatList = formats.map { format in return BarcodeFormat(rawValue: format)} |
| 66 | + var barcodeOptions: BarcodeScannerOptions? = nil | ||
| 190 | 67 | ||
| 191 | - returnImage = argReader.bool(key: "returnImage") ?? false | ||
| 192 | - | ||
| 193 | -// let ratio: Int = argReader.int(key: "ratio") | ||
| 194 | - let torch: Bool = argReader.bool(key: "torch") ?? false | ||
| 195 | - let facing: Int = argReader.int(key: "facing") ?? 1 | ||
| 196 | - let formats: Array = argReader.intArray(key: "formats") ?? [] | ||
| 197 | - | ||
| 198 | - if (formats.count != 0) { | 68 | + if (formatList.count != 0) { |
| 199 | var barcodeFormats: BarcodeFormat = [] | 69 | var barcodeFormats: BarcodeFormat = [] |
| 200 | for index in formats { | 70 | for index in formats { |
| 201 | barcodeFormats.insert(BarcodeFormat(rawValue: index)) | 71 | barcodeFormats.insert(BarcodeFormat(rawValue: index)) |
| 202 | } | 72 | } |
| 203 | - | ||
| 204 | - let barcodeOptions = BarcodeScannerOptions(formats: barcodeFormats) | ||
| 205 | - scanner = BarcodeScanner.barcodeScanner(options: barcodeOptions) | 73 | + barcodeOptions = BarcodeScannerOptions(formats: barcodeFormats) |
| 206 | } | 74 | } |
| 207 | 75 | ||
| 208 | - // Set the camera to use | ||
| 209 | - position = facing == 0 ? AVCaptureDevice.Position.front : .back | ||
| 210 | 76 | ||
| 211 | - // Open the camera device | ||
| 212 | - if #available(iOS 10.0, *) { | ||
| 213 | - device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices.first | ||
| 214 | - } else { | ||
| 215 | - device = AVCaptureDevice.devices(for: .video).filter({$0.position == position}).first | ||
| 216 | - } | 77 | + let position = facing == 0 ? AVCaptureDevice.Position.front : .back |
| 78 | + let speed: DetectionSpeed = DetectionSpeed(rawValue: (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0)! | ||
| 217 | 79 | ||
| 218 | - if (device == nil) { | 80 | + do { |
| 81 | + let parameters = try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch ? .on : .off, detectionSpeed: speed) | ||
| 82 | + result(["textureId": parameters.textureId, "size": ["width": parameters.width, "height": parameters.height], "torchable": parameters.hasTorch]) | ||
| 83 | + } catch MobileScannerError.alreadyStarted { | ||
| 84 | + result(FlutterError(code: "MobileScanner", | ||
| 85 | + message: "Called start() while already started!", | ||
| 86 | + details: nil)) | ||
| 87 | + } catch MobileScannerError.noCamera { | ||
| 219 | result(FlutterError(code: "MobileScanner", | 88 | result(FlutterError(code: "MobileScanner", |
| 220 | message: "No camera found or failed to open camera!", | 89 | message: "No camera found or failed to open camera!", |
| 221 | details: nil)) | 90 | details: nil)) |
| 222 | - return | ||
| 223 | - } | ||
| 224 | - | ||
| 225 | - // Enable the torch if parameter is set and torch is available | ||
| 226 | - if (device.hasTorch && device.isTorchAvailable) { | ||
| 227 | - do { | ||
| 228 | - try device.lockForConfiguration() | ||
| 229 | - device.torchMode = torch ? .on : .off | ||
| 230 | - device.unlockForConfiguration() | 91 | + } catch MobileScannerError.torchError(let error) { |
| 92 | + result(FlutterError(code: "MobileScanner", | ||
| 93 | + message: "Error occured when setting toch!", | ||
| 94 | + details: error)) | ||
| 95 | + } catch MobileScannerError.cameraError(let error) { | ||
| 96 | + result(FlutterError(code: "MobileScanner", | ||
| 97 | + message: "Error occured when setting up camera!", | ||
| 98 | + details: error)) | ||
| 231 | } catch { | 99 | } catch { |
| 232 | - error.throwNative(result) | 100 | + result(FlutterError(code: "MobileScanner", |
| 101 | + message: "Unknown error occured..", | ||
| 102 | + details: nil)) | ||
| 233 | } | 103 | } |
| 234 | } | 104 | } |
| 235 | 105 | ||
| 236 | - device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil) | ||
| 237 | - captureSession.beginConfiguration() | ||
| 238 | - | ||
| 239 | - // Add device input | 106 | + /// Stops the mobileScanner and closes the texture |
| 107 | + private func stop(_ result: @escaping FlutterResult) { | ||
| 240 | do { | 108 | do { |
| 241 | - let input = try AVCaptureDeviceInput(device: device) | ||
| 242 | - captureSession.addInput(input) | 109 | + try mobileScanner.stop() |
| 243 | } catch { | 110 | } catch { |
| 244 | - error.throwNative(result) | ||
| 245 | - } | ||
| 246 | - captureSession.sessionPreset = AVCaptureSession.Preset.photo; | ||
| 247 | - // Add video output. | ||
| 248 | - let videoOutput = AVCaptureVideoDataOutput() | ||
| 249 | - | ||
| 250 | - videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA] | ||
| 251 | - videoOutput.alwaysDiscardsLateVideoFrames = true | ||
| 252 | - | ||
| 253 | - videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) | ||
| 254 | - captureSession.addOutput(videoOutput) | ||
| 255 | - for connection in videoOutput.connections { | ||
| 256 | - connection.videoOrientation = .portrait | ||
| 257 | - if position == .front && connection.isVideoMirroringSupported { | ||
| 258 | - connection.isVideoMirrored = true | ||
| 259 | - } | 111 | + result(FlutterError(code: "MobileScanner", |
| 112 | + message: "Called stop() while already stopped!", | ||
| 113 | + details: nil)) | ||
| 260 | } | 114 | } |
| 261 | - captureSession.commitConfiguration() | ||
| 262 | - captureSession.startRunning() | ||
| 263 | - let demensions = CMVideoFormatDescriptionGetDimensions(device.activeFormat.formatDescription) | ||
| 264 | - let width = Double(demensions.height) | ||
| 265 | - let height = Double(demensions.width) | ||
| 266 | - let size = ["width": width, "height": height] | ||
| 267 | - let answer: [String : Any?] = ["textureId": textureId, "size": size, "torchable": device.hasTorch] | ||
| 268 | - result(answer) | 115 | + result(nil) |
| 269 | } | 116 | } |
| 270 | 117 | ||
| 271 | - func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 272 | - if (device == nil) { | 118 | + /// Toggles the torch |
| 119 | + private func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 120 | + do { | ||
| 121 | + try mobileScanner.toggleTorch(call.arguments as? Int == 1 ? .on : .off) | ||
| 122 | + } catch { | ||
| 273 | result(FlutterError(code: "MobileScanner", | 123 | result(FlutterError(code: "MobileScanner", |
| 274 | message: "Called toggleTorch() while stopped!", | 124 | message: "Called toggleTorch() while stopped!", |
| 275 | details: nil)) | 125 | details: nil)) |
| 276 | - return | ||
| 277 | } | 126 | } |
| 278 | - do { | ||
| 279 | - try device.lockForConfiguration() | ||
| 280 | - device.torchMode = call.arguments as! Int == 1 ? .on : .off | ||
| 281 | - device.unlockForConfiguration() | ||
| 282 | result(nil) | 127 | result(nil) |
| 283 | - } catch { | ||
| 284 | - error.throwNative(result) | ||
| 285 | - } | ||
| 286 | } | 128 | } |
| 287 | 129 | ||
| 288 | -// func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 289 | -// analyzeMode = call.arguments as! Int | ||
| 290 | -// result(nil) | ||
| 291 | -// } | ||
| 292 | - | ||
| 293 | - func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 294 | - let uiImage = UIImage(contentsOfFile: call.arguments as! String) | 130 | + /// Analyzes a single image |
| 131 | + private func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 132 | + let uiImage = UIImage(contentsOfFile: call.arguments as? String ?? "") | ||
| 295 | 133 | ||
| 296 | if (uiImage == nil) { | 134 | if (uiImage == nil) { |
| 297 | result(FlutterError(code: "MobileScanner", | 135 | result(FlutterError(code: "MobileScanner", |
| @@ -299,103 +137,32 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -299,103 +137,32 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 299 | details: nil)) | 137 | details: nil)) |
| 300 | return | 138 | return |
| 301 | } | 139 | } |
| 302 | - | ||
| 303 | - let image = VisionImage(image: uiImage!) | ||
| 304 | - image.orientation = imageOrientation( | ||
| 305 | - deviceOrientation: UIDevice.current.orientation, | ||
| 306 | - defaultOrientation: .portrait | ||
| 307 | - ) | ||
| 308 | - | ||
| 309 | - var barcodeFound = false | ||
| 310 | - | ||
| 311 | - scanner.process(image) { [self] barcodes, error in | 140 | + mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back, callback: { [self] barcodes, error in |
| 312 | if error == nil && barcodes != nil { | 141 | if error == nil && barcodes != nil { |
| 313 | for barcode in barcodes! { | 142 | for barcode in barcodes! { |
| 314 | - barcodeFound = true | ||
| 315 | let event: [String: Any?] = ["name": "barcode", "data": barcode.data] | 143 | let event: [String: Any?] = ["name": "barcode", "data": barcode.data] |
| 316 | - sink?(event) | 144 | + barcodeHandler.publishEvent(event) |
| 317 | } | 145 | } |
| 318 | } else if error != nil { | 146 | } else if error != nil { |
| 319 | - result(FlutterError(code: "MobileScanner", | ||
| 320 | - message: error?.localizedDescription, | ||
| 321 | - details: "analyzeImage()")) | ||
| 322 | - } | ||
| 323 | - analyzing = false | ||
| 324 | - result(barcodeFound) | ||
| 325 | - } | ||
| 326 | - | ||
| 327 | - } | ||
| 328 | - | ||
| 329 | - func stop(_ result: FlutterResult) { | ||
| 330 | - if (device == nil) { | ||
| 331 | - result(FlutterError(code: "MobileScanner", | ||
| 332 | - message: "Called stop() while already stopped!", | ||
| 333 | - details: nil)) | ||
| 334 | - return | 147 | + barcodeHandler.publishEvent(["name": "error", "message": error?.localizedDescription]) |
| 335 | } | 148 | } |
| 336 | - captureSession.stopRunning() | ||
| 337 | - for input in captureSession.inputs { | ||
| 338 | - captureSession.removeInput(input) | ||
| 339 | - } | ||
| 340 | - for output in captureSession.outputs { | ||
| 341 | - captureSession.removeOutput(output) | ||
| 342 | - } | ||
| 343 | - device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) | ||
| 344 | - registry.unregisterTexture(textureId) | ||
| 345 | - | ||
| 346 | -// analyzeMode = 0 | ||
| 347 | - latestBuffer = nil | ||
| 348 | - captureSession = nil | ||
| 349 | - device = nil | ||
| 350 | - textureId = nil | ||
| 351 | - | 149 | + }) |
| 352 | result(nil) | 150 | result(nil) |
| 353 | } | 151 | } |
| 354 | 152 | ||
| 355 | - // Observer for torch state | 153 | + /// Observer for torch state |
| 356 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { | 154 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { |
| 357 | switch keyPath { | 155 | switch keyPath { |
| 358 | case "torchMode": | 156 | case "torchMode": |
| 359 | // off = 0; on = 1; auto = 2; | 157 | // off = 0; on = 1; auto = 2; |
| 360 | let state = change?[.newKey] as? Int | 158 | let state = change?[.newKey] as? Int |
| 361 | - let event: [String: Any?] = ["name": "torchState", "data": state] | ||
| 362 | - sink?(event) | 159 | + barcodeHandler.publishEvent(["name": "torchState", "data": state]) |
| 363 | default: | 160 | default: |
| 364 | break | 161 | break |
| 365 | } | 162 | } |
| 366 | } | 163 | } |
| 367 | } | 164 | } |
| 368 | 165 | ||
| 369 | -class MapArgumentReader { | ||
| 370 | - | ||
| 371 | - let args: [String: Any]? | ||
| 372 | - | ||
| 373 | - init(_ args: [String: Any]?) { | ||
| 374 | - self.args = args | ||
| 375 | - } | ||
| 376 | - | ||
| 377 | - func string(key: String) -> String? { | ||
| 378 | - return args?[key] as? String | ||
| 379 | - } | ||
| 380 | - | ||
| 381 | - func int(key: String) -> Int? { | ||
| 382 | - return (args?[key] as? NSNumber)?.intValue | ||
| 383 | - } | ||
| 384 | - | ||
| 385 | - func bool(key: String) -> Bool? { | ||
| 386 | - return (args?[key] as? NSNumber)?.boolValue | ||
| 387 | - } | ||
| 388 | - | ||
| 389 | - func stringArray(key: String) -> [String]? { | ||
| 390 | - return args?[key] as? [String] | ||
| 391 | - } | ||
| 392 | - | ||
| 393 | - func intArray(key: String) -> [Int]? { | ||
| 394 | - return args?[key] as? [Int] | ||
| 395 | - } | ||
| 396 | - | ||
| 397 | -} | ||
| 398 | - | ||
| 399 | enum DetectionSpeed: Int { | 166 | enum DetectionSpeed: Int { |
| 400 | case noDuplicates = 0 | 167 | case noDuplicates = 0 |
| 401 | case normal = 1 | 168 | case normal = 1 |
-
Please register or login to post a comment