Committed by
GitHub
Merge pull request #842 from navaronbracke/fix_torch_on_start
fix: Fix torch state sync issues
Showing
9 changed files
with
68 additions
and
66 deletions
| @@ -376,7 +376,7 @@ class MobileScanner( | @@ -376,7 +376,7 @@ class MobileScanner( | ||
| 376 | */ | 376 | */ |
| 377 | fun toggleTorch(enableTorch: Boolean) { | 377 | fun toggleTorch(enableTorch: Boolean) { |
| 378 | if (camera == null) { | 378 | if (camera == null) { |
| 379 | - throw TorchWhenStopped() | 379 | + return |
| 380 | } | 380 | } |
| 381 | 381 | ||
| 382 | if (camera?.cameraInfo?.hasFlashUnit() == true) { | 382 | if (camera?.cameraInfo?.hasFlashUnit() == true) { |
| @@ -4,6 +4,5 @@ class NoCamera : Exception() | @@ -4,6 +4,5 @@ class NoCamera : Exception() | ||
| 4 | class AlreadyStarted : Exception() | 4 | class AlreadyStarted : Exception() |
| 5 | class AlreadyStopped : Exception() | 5 | class AlreadyStopped : Exception() |
| 6 | class CameraError : Exception() | 6 | class CameraError : Exception() |
| 7 | -class TorchWhenStopped : Exception() | ||
| 8 | class ZoomWhenStopped : Exception() | 7 | class ZoomWhenStopped : Exception() |
| 9 | class ZoomNotInRange : Exception() | 8 | class ZoomNotInRange : Exception() |
| @@ -237,12 +237,8 @@ class MobileScannerHandler( | @@ -237,12 +237,8 @@ class MobileScannerHandler( | ||
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | private fun toggleTorch(call: MethodCall, result: MethodChannel.Result) { | 239 | private fun toggleTorch(call: MethodCall, result: MethodChannel.Result) { |
| 240 | - try { | ||
| 241 | - mobileScanner!!.toggleTorch(call.arguments == 1) | ||
| 242 | - result.success(null) | ||
| 243 | - } catch (e: TorchWhenStopped) { | ||
| 244 | - result.error("MobileScanner", "Called toggleTorch() while stopped!", null) | ||
| 245 | - } | 240 | + mobileScanner!!.toggleTorch(call.arguments == 1) |
| 241 | + result.success(null) | ||
| 246 | } | 242 | } |
| 247 | 243 | ||
| 248 | private fun setScale(call: MethodCall, result: MethodChannel.Result) { | 244 | private fun setScale(call: MethodCall, result: MethodChannel.Result) { |
| @@ -133,7 +133,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -133,7 +133,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | /// Start scanning for barcodes | 135 | /// Start scanning for barcodes |
| 136 | - func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: AVCaptureDevice.TorchMode, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { | 136 | + func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { |
| 137 | self.detectionSpeed = detectionSpeed | 137 | self.detectionSpeed = detectionSpeed |
| 138 | if (device != nil) { | 138 | if (device != nil) { |
| 139 | throw MobileScannerError.alreadyStarted | 139 | throw MobileScannerError.alreadyStarted |
| @@ -213,18 +213,23 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -213,18 +213,23 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 213 | 213 | ||
| 214 | backgroundQueue.async { | 214 | backgroundQueue.async { |
| 215 | self.captureSession.startRunning() | 215 | self.captureSession.startRunning() |
| 216 | - // Enable the torch if parameter is set and torch is available | ||
| 217 | - // torch should be set after 'startRunning' is called | ||
| 218 | - do { | ||
| 219 | - try self.toggleTorch(torch) | ||
| 220 | - } catch { | ||
| 221 | - print("Failed to set initial torch state.") | 216 | + |
| 217 | + // Turn on the flashlight if requested, | ||
| 218 | + // but after the capture session started. | ||
| 219 | + if (torch) { | ||
| 220 | + do { | ||
| 221 | + try self.toggleTorch(.on) | ||
| 222 | + } catch { | ||
| 223 | + // If the torch does not turn on, | ||
| 224 | + // continue with the capture session anyway. | ||
| 225 | + } | ||
| 222 | } | 226 | } |
| 223 | 227 | ||
| 224 | do { | 228 | do { |
| 225 | try self.resetScale() | 229 | try self.resetScale() |
| 226 | } catch { | 230 | } catch { |
| 227 | - print("Failed to reset zoom scale") | 231 | + // If the zoom scale could not be reset, |
| 232 | + // continue with the capture session anyway. | ||
| 228 | } | 233 | } |
| 229 | 234 | ||
| 230 | if let device = self.device { | 235 | if let device = self.device { |
| @@ -270,19 +275,16 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -270,19 +275,16 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 270 | device = nil | 275 | device = nil |
| 271 | } | 276 | } |
| 272 | 277 | ||
| 273 | - /// Toggle the flashlight between on and off | 278 | + /// Toggle the flashlight between on and off. |
| 274 | func toggleTorch(_ torch: AVCaptureDevice.TorchMode) throws { | 279 | func toggleTorch(_ torch: AVCaptureDevice.TorchMode) throws { |
| 275 | - if (device == nil) { | ||
| 276 | - throw MobileScannerError.torchWhenStopped | 280 | + if (device == nil || !device.hasTorch || !device.isTorchAvailable) { |
| 281 | + return | ||
| 277 | } | 282 | } |
| 278 | - if (device.hasTorch && device.isTorchAvailable) { | ||
| 279 | - do { | ||
| 280 | - try device.lockForConfiguration() | ||
| 281 | - device.torchMode = torch | ||
| 282 | - device.unlockForConfiguration() | ||
| 283 | - } catch { | ||
| 284 | - throw MobileScannerError.torchError(error) | ||
| 285 | - } | 283 | + |
| 284 | + if (device.torchMode != torch) { | ||
| 285 | + try device.lockForConfiguration() | ||
| 286 | + device.torchMode = torch | ||
| 287 | + device.unlockForConfiguration() | ||
| 286 | } | 288 | } |
| 287 | } | 289 | } |
| 288 | 290 |
| @@ -10,9 +10,7 @@ enum MobileScannerError: Error { | @@ -10,9 +10,7 @@ enum MobileScannerError: Error { | ||
| 10 | case noCamera | 10 | case noCamera |
| 11 | case alreadyStarted | 11 | case alreadyStarted |
| 12 | case alreadyStopped | 12 | case alreadyStopped |
| 13 | - case torchError(_ error: Error) | ||
| 14 | case cameraError(_ error: Error) | 13 | case cameraError(_ error: Error) |
| 15 | - case torchWhenStopped | ||
| 16 | case zoomWhenStopped | 14 | case zoomWhenStopped |
| 17 | case zoomError(_ error: Error) | 15 | case zoomError(_ error: Error) |
| 18 | case analyzerError(_ error: Error) | 16 | case analyzerError(_ error: Error) |
| @@ -120,7 +120,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -120,7 +120,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 120 | let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)! | 120 | let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)! |
| 121 | 121 | ||
| 122 | do { | 122 | do { |
| 123 | - try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch ? .on : .off, detectionSpeed: detectionSpeed) { parameters in | 123 | + try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in |
| 124 | DispatchQueue.main.async { | 124 | DispatchQueue.main.async { |
| 125 | result([ | 125 | result([ |
| 126 | "textureId": parameters.textureId, | 126 | "textureId": parameters.textureId, |
| @@ -136,17 +136,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -136,17 +136,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 136 | result(FlutterError(code: "MobileScanner", | 136 | result(FlutterError(code: "MobileScanner", |
| 137 | message: "No camera found or failed to open camera!", | 137 | message: "No camera found or failed to open camera!", |
| 138 | details: nil)) | 138 | details: nil)) |
| 139 | - } catch MobileScannerError.torchError(let error) { | ||
| 140 | - result(FlutterError(code: "MobileScanner", | ||
| 141 | - message: "Error occured when setting torch!", | ||
| 142 | - details: error)) | ||
| 143 | } catch MobileScannerError.cameraError(let error) { | 139 | } catch MobileScannerError.cameraError(let error) { |
| 144 | result(FlutterError(code: "MobileScanner", | 140 | result(FlutterError(code: "MobileScanner", |
| 145 | message: "Error occured when setting up camera!", | 141 | message: "Error occured when setting up camera!", |
| 146 | details: error)) | 142 | details: error)) |
| 147 | } catch { | 143 | } catch { |
| 148 | result(FlutterError(code: "MobileScanner", | 144 | result(FlutterError(code: "MobileScanner", |
| 149 | - message: "Unknown error occured..", | 145 | + message: "Unknown error occured.", |
| 150 | details: nil)) | 146 | details: nil)) |
| 151 | } | 147 | } |
| 152 | } | 148 | } |
| @@ -165,9 +161,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -165,9 +161,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 165 | try mobileScanner.toggleTorch(call.arguments as? Int == 1 ? .on : .off) | 161 | try mobileScanner.toggleTorch(call.arguments as? Int == 1 ? .on : .off) |
| 166 | result(nil) | 162 | result(nil) |
| 167 | } catch { | 163 | } catch { |
| 168 | - result(FlutterError(code: "MobileScanner", | ||
| 169 | - message: "Called toggleTorch() while stopped!", | ||
| 170 | - details: nil)) | 164 | + result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil)) |
| 171 | } | 165 | } |
| 172 | } | 166 | } |
| 173 | 167 |
| @@ -149,9 +149,10 @@ class MobileScannerWebPlugin { | @@ -149,9 +149,10 @@ class MobileScannerWebPlugin { | ||
| 149 | }); | 149 | }); |
| 150 | 150 | ||
| 151 | final hasTorch = await barCodeReader.hasTorch(); | 151 | final hasTorch = await barCodeReader.hasTorch(); |
| 152 | + final bool? enableTorch = arguments['torch'] as bool?; | ||
| 152 | 153 | ||
| 153 | - if (hasTorch && arguments.containsKey('torch')) { | ||
| 154 | - await barCodeReader.toggleTorch(enabled: arguments['torch'] as bool); | 154 | + if (hasTorch && enableTorch != null) { |
| 155 | + await barCodeReader.toggleTorch(enabled: enableTorch); | ||
| 155 | } | 156 | } |
| 156 | 157 | ||
| 157 | return { | 158 | return { |
| @@ -282,9 +282,6 @@ class MobileScannerController { | @@ -282,9 +282,6 @@ class MobileScannerController { | ||
| 282 | 282 | ||
| 283 | final hasTorch = startResult['torchable'] as bool? ?? false; | 283 | final hasTorch = startResult['torchable'] as bool? ?? false; |
| 284 | hasTorchState.value = hasTorch; | 284 | hasTorchState.value = hasTorch; |
| 285 | - if (hasTorch && torchEnabled) { | ||
| 286 | - torchState.value = TorchState.on; | ||
| 287 | - } | ||
| 288 | 285 | ||
| 289 | final Size size; | 286 | final Size size; |
| 290 | 287 | ||
| @@ -314,11 +311,11 @@ class MobileScannerController { | @@ -314,11 +311,11 @@ class MobileScannerController { | ||
| 314 | 311 | ||
| 315 | /// Stops the camera, but does not dispose this controller. | 312 | /// Stops the camera, but does not dispose this controller. |
| 316 | Future<void> stop() async { | 313 | Future<void> stop() async { |
| 317 | - try { | ||
| 318 | - await _methodChannel.invokeMethod('stop'); | ||
| 319 | - } catch (e) { | ||
| 320 | - debugPrint('$e'); | ||
| 321 | - } | 314 | + await _methodChannel.invokeMethod('stop'); |
| 315 | + | ||
| 316 | + // After the camera stopped, set the torch state to off, | ||
| 317 | + // as the torch state callback is never called when the camera is stopped. | ||
| 318 | + torchState.value = TorchState.off; | ||
| 322 | } | 319 | } |
| 323 | 320 | ||
| 324 | /// Switches the torch on or off. | 321 | /// Switches the torch on or off. |
| @@ -333,14 +330,16 @@ class MobileScannerController { | @@ -333,14 +330,16 @@ class MobileScannerController { | ||
| 333 | throw const MobileScannerException( | 330 | throw const MobileScannerException( |
| 334 | errorCode: MobileScannerErrorCode.controllerUninitialized, | 331 | errorCode: MobileScannerErrorCode.controllerUninitialized, |
| 335 | ); | 332 | ); |
| 336 | - } else if (!hasTorch) { | 333 | + } |
| 334 | + | ||
| 335 | + if (!hasTorch) { | ||
| 337 | return; | 336 | return; |
| 338 | } | 337 | } |
| 339 | 338 | ||
| 340 | - torchState.value = | 339 | + final TorchState newState = |
| 341 | torchState.value == TorchState.off ? TorchState.on : TorchState.off; | 340 | torchState.value == TorchState.off ? TorchState.on : TorchState.off; |
| 342 | 341 | ||
| 343 | - await _methodChannel.invokeMethod('torch', torchState.value.rawValue); | 342 | + await _methodChannel.invokeMethod('torch', newState.rawValue); |
| 344 | } | 343 | } |
| 345 | 344 | ||
| 346 | /// Changes the state of the camera (front or back). | 345 | /// Changes the state of the camera (front or back). |
| @@ -274,15 +274,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -274,15 +274,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 274 | return | 274 | return |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | - // Enable the torch if parameter is set and torch is available | ||
| 278 | - if (device.hasTorch) { | 277 | + // Turn on the torch if requested. |
| 278 | + if (torch) { | ||
| 279 | do { | 279 | do { |
| 280 | - try device.lockForConfiguration() | ||
| 281 | - device.torchMode = torch ? .on : .off | ||
| 282 | - device.unlockForConfiguration() | 280 | + try self.toggleTorchInternal(.on) |
| 283 | } catch { | 281 | } catch { |
| 284 | - result(FlutterError(code: error.localizedDescription, message: nil, details: nil)) | ||
| 285 | - return | 282 | + // If the torch could not be turned on, |
| 283 | + // continue the capture session. | ||
| 286 | } | 284 | } |
| 287 | } | 285 | } |
| 288 | 286 | ||
| @@ -294,7 +292,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -294,7 +292,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 294 | let input = try AVCaptureDeviceInput(device: device) | 292 | let input = try AVCaptureDeviceInput(device: device) |
| 295 | captureSession.addInput(input) | 293 | captureSession.addInput(input) |
| 296 | } catch { | 294 | } catch { |
| 297 | - result(FlutterError(code: error.localizedDescription, message: nil, details: nil)) | 295 | + result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil)) |
| 298 | return | 296 | return |
| 299 | } | 297 | } |
| 300 | captureSession.sessionPreset = AVCaptureSession.Preset.photo | 298 | captureSession.sessionPreset = AVCaptureSession.Preset.photo |
| @@ -319,19 +317,34 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -319,19 +317,34 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 319 | let answer: [String : Any?] = ["textureId": textureId, "size": size, "torchable": device.hasTorch] | 317 | let answer: [String : Any?] = ["textureId": textureId, "size": size, "torchable": device.hasTorch] |
| 320 | result(answer) | 318 | result(answer) |
| 321 | } | 319 | } |
| 322 | - | ||
| 323 | - func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 324 | - if (device == nil) { | ||
| 325 | - result(nil) | 320 | + |
| 321 | + // TODO: this method should be removed when iOS and MacOS share their implementation. | ||
| 322 | + private func toggleTorchInternal(_ torch: AVCaptureDevice.TorchMode) throws { | ||
| 323 | + if (device == nil || !device.hasTorch) { | ||
| 326 | return | 324 | return |
| 327 | } | 325 | } |
| 328 | - do { | 326 | + |
| 327 | + if #available(macOS 15.0, *) { | ||
| 328 | + if(!device.isTorchAvailable) { | ||
| 329 | + return | ||
| 330 | + } | ||
| 331 | + } | ||
| 332 | + | ||
| 333 | + if (device.torchMode != torch) { | ||
| 329 | try device.lockForConfiguration() | 334 | try device.lockForConfiguration() |
| 330 | - device.torchMode = call.arguments as! Int == 1 ? .on : .off | 335 | + device.torchMode = torch |
| 331 | device.unlockForConfiguration() | 336 | device.unlockForConfiguration() |
| 337 | + } | ||
| 338 | + } | ||
| 339 | + | ||
| 340 | + func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 341 | + let requestedTorchMode: AVCaptureDevice.TorchMode = call.arguments as! Int == 1 ? .on : .off | ||
| 342 | + | ||
| 343 | + do { | ||
| 344 | + try self.toggleTorchInternal(requestedTorchMode) | ||
| 332 | result(nil) | 345 | result(nil) |
| 333 | } catch { | 346 | } catch { |
| 334 | - result(FlutterError(code: error.localizedDescription, message: nil, details: nil)) | 347 | + result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil)) |
| 335 | } | 348 | } |
| 336 | } | 349 | } |
| 337 | 350 |
-
Please register or login to post a comment