Navaron Bracke
Committed by GitHub

Merge pull request #842 from navaronbracke/fix_torch_on_start

fix: Fix torch state sync issues
... ... @@ -376,7 +376,7 @@ class MobileScanner(
*/
fun toggleTorch(enableTorch: Boolean) {
if (camera == null) {
throw TorchWhenStopped()
return
}
if (camera?.cameraInfo?.hasFlashUnit() == true) {
... ...
... ... @@ -4,6 +4,5 @@ class NoCamera : Exception()
class AlreadyStarted : Exception()
class AlreadyStopped : Exception()
class CameraError : Exception()
class TorchWhenStopped : Exception()
class ZoomWhenStopped : Exception()
class ZoomNotInRange : Exception()
\ No newline at end of file
... ...
... ... @@ -237,12 +237,8 @@ class MobileScannerHandler(
}
private fun toggleTorch(call: MethodCall, result: MethodChannel.Result) {
try {
mobileScanner!!.toggleTorch(call.arguments == 1)
result.success(null)
} catch (e: TorchWhenStopped) {
result.error("MobileScanner", "Called toggleTorch() while stopped!", null)
}
}
private fun setScale(call: MethodCall, result: MethodChannel.Result) {
... ...
... ... @@ -133,7 +133,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
}
/// Start scanning for barcodes
func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: AVCaptureDevice.TorchMode, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws {
func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws {
self.detectionSpeed = detectionSpeed
if (device != nil) {
throw MobileScannerError.alreadyStarted
... ... @@ -213,18 +213,23 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
backgroundQueue.async {
self.captureSession.startRunning()
// Enable the torch if parameter is set and torch is available
// torch should be set after 'startRunning' is called
// Turn on the flashlight if requested,
// but after the capture session started.
if (torch) {
do {
try self.toggleTorch(torch)
try self.toggleTorch(.on)
} catch {
print("Failed to set initial torch state.")
// If the torch does not turn on,
// continue with the capture session anyway.
}
}
do {
try self.resetScale()
} catch {
print("Failed to reset zoom scale")
// If the zoom scale could not be reset,
// continue with the capture session anyway.
}
if let device = self.device {
... ... @@ -270,19 +275,16 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
device = nil
}
/// Toggle the flashlight between on and off
/// Toggle the flashlight between on and off.
func toggleTorch(_ torch: AVCaptureDevice.TorchMode) throws {
if (device == nil) {
throw MobileScannerError.torchWhenStopped
if (device == nil || !device.hasTorch || !device.isTorchAvailable) {
return
}
if (device.hasTorch && device.isTorchAvailable) {
do {
if (device.torchMode != torch) {
try device.lockForConfiguration()
device.torchMode = torch
device.unlockForConfiguration()
} catch {
throw MobileScannerError.torchError(error)
}
}
}
... ...
... ... @@ -10,9 +10,7 @@ enum MobileScannerError: Error {
case noCamera
case alreadyStarted
case alreadyStopped
case torchError(_ error: Error)
case cameraError(_ error: Error)
case torchWhenStopped
case zoomWhenStopped
case zoomError(_ error: Error)
case analyzerError(_ error: Error)
... ...
... ... @@ -120,7 +120,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)!
do {
try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch ? .on : .off, detectionSpeed: detectionSpeed) { parameters in
try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in
DispatchQueue.main.async {
result([
"textureId": parameters.textureId,
... ... @@ -136,17 +136,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
result(FlutterError(code: "MobileScanner",
message: "No camera found or failed to open camera!",
details: nil))
} catch MobileScannerError.torchError(let error) {
result(FlutterError(code: "MobileScanner",
message: "Error occured when setting torch!",
details: error))
} catch MobileScannerError.cameraError(let error) {
result(FlutterError(code: "MobileScanner",
message: "Error occured when setting up camera!",
details: error))
} catch {
result(FlutterError(code: "MobileScanner",
message: "Unknown error occured..",
message: "Unknown error occured.",
details: nil))
}
}
... ... @@ -165,9 +161,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
try mobileScanner.toggleTorch(call.arguments as? Int == 1 ? .on : .off)
result(nil)
} catch {
result(FlutterError(code: "MobileScanner",
message: "Called toggleTorch() while stopped!",
details: nil))
result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil))
}
}
... ...
... ... @@ -149,9 +149,10 @@ class MobileScannerWebPlugin {
});
final hasTorch = await barCodeReader.hasTorch();
final bool? enableTorch = arguments['torch'] as bool?;
if (hasTorch && arguments.containsKey('torch')) {
await barCodeReader.toggleTorch(enabled: arguments['torch'] as bool);
if (hasTorch && enableTorch != null) {
await barCodeReader.toggleTorch(enabled: enableTorch);
}
return {
... ...
... ... @@ -282,9 +282,6 @@ class MobileScannerController {
final hasTorch = startResult['torchable'] as bool? ?? false;
hasTorchState.value = hasTorch;
if (hasTorch && torchEnabled) {
torchState.value = TorchState.on;
}
final Size size;
... ... @@ -314,11 +311,11 @@ class MobileScannerController {
/// Stops the camera, but does not dispose this controller.
Future<void> stop() async {
try {
await _methodChannel.invokeMethod('stop');
} catch (e) {
debugPrint('$e');
}
// After the camera stopped, set the torch state to off,
// as the torch state callback is never called when the camera is stopped.
torchState.value = TorchState.off;
}
/// Switches the torch on or off.
... ... @@ -333,14 +330,16 @@ class MobileScannerController {
throw const MobileScannerException(
errorCode: MobileScannerErrorCode.controllerUninitialized,
);
} else if (!hasTorch) {
}
if (!hasTorch) {
return;
}
torchState.value =
final TorchState newState =
torchState.value == TorchState.off ? TorchState.on : TorchState.off;
await _methodChannel.invokeMethod('torch', torchState.value.rawValue);
await _methodChannel.invokeMethod('torch', newState.rawValue);
}
/// Changes the state of the camera (front or back).
... ...
... ... @@ -274,15 +274,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
return
}
// Enable the torch if parameter is set and torch is available
if (device.hasTorch) {
// Turn on the torch if requested.
if (torch) {
do {
try device.lockForConfiguration()
device.torchMode = torch ? .on : .off
device.unlockForConfiguration()
try self.toggleTorchInternal(.on)
} catch {
result(FlutterError(code: error.localizedDescription, message: nil, details: nil))
return
// If the torch could not be turned on,
// continue the capture session.
}
}
... ... @@ -294,7 +292,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
let input = try AVCaptureDeviceInput(device: device)
captureSession.addInput(input)
} catch {
result(FlutterError(code: error.localizedDescription, message: nil, details: nil))
result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil))
return
}
captureSession.sessionPreset = AVCaptureSession.Preset.photo
... ... @@ -320,18 +318,33 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
result(answer)
}
func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
if (device == nil) {
result(nil)
// TODO: this method should be removed when iOS and MacOS share their implementation.
private func toggleTorchInternal(_ torch: AVCaptureDevice.TorchMode) throws {
if (device == nil || !device.hasTorch) {
return
}
do {
if #available(macOS 15.0, *) {
if(!device.isTorchAvailable) {
return
}
}
if (device.torchMode != torch) {
try device.lockForConfiguration()
device.torchMode = call.arguments as! Int == 1 ? .on : .off
device.torchMode = torch
device.unlockForConfiguration()
}
}
func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
let requestedTorchMode: AVCaptureDevice.TorchMode = call.arguments as! Int == 1 ? .on : .off
do {
try self.toggleTorchInternal(requestedTorchMode)
result(nil)
} catch {
result(FlutterError(code: error.localizedDescription, message: nil, details: nil))
result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil))
}
}
... ...