Julian Steenbakker

Merge branch 'master' into ci

# Conflicts:
#	.github/workflows/flutter.yml
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 release-please: 7 release-please:
8 runs-on: ubuntu-latest 8 runs-on: ubuntu-latest
9 steps: 9 steps:
10 - - uses: GoogleCloudPlatform/release-please-action@v3.7.13 10 + - uses: GoogleCloudPlatform/release-please-action@v4.0.1
11 with: 11 with:
12 token: ${{ secrets.GITHUB_TOKEN }} 12 token: ${{ secrets.GITHUB_TOKEN }}
13 release-type: simple 13 release-type: simple
@@ -69,6 +69,43 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega @@ -69,6 +69,43 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
69 super.init() 69 super.init()
70 } 70 }
71 71
  72 + /// Get the default camera device for the given `position`.
  73 + ///
  74 + /// This function selects the most appropriate camera, when it is available.
  75 + private func getDefaultCameraDevice(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
  76 + if #available(iOS 13.0, *) {
  77 + // Find the built-in Triple Camera, if it exists.
  78 + if let device = AVCaptureDevice.default(.builtInTripleCamera,
  79 + for: .video,
  80 + position: position) {
  81 + return device
  82 + }
  83 +
  84 + // Find the built-in Dual-Wide Camera, if it exists.
  85 + if let device = AVCaptureDevice.default(.builtInDualWideCamera,
  86 + for: .video,
  87 + position: position) {
  88 + return device
  89 + }
  90 + }
  91 +
  92 + // Find the built-in Dual Camera, if it exists.
  93 + if let device = AVCaptureDevice.default(.builtInDualCamera,
  94 + for: .video,
  95 + position: position) {
  96 + return device
  97 + }
  98 +
  99 + // Find the built-in Wide-Angle Camera, if it exists.
  100 + if let device = AVCaptureDevice.default(.builtInWideAngleCamera,
  101 + for: .video,
  102 + position: position) {
  103 + return device
  104 + }
  105 +
  106 + return nil
  107 + }
  108 +
72 /// Check if we already have camera permission. 109 /// Check if we already have camera permission.
73 func checkPermission() -> Int { 110 func checkPermission() -> Int {
74 let status = AVCaptureDevice.authorizationStatus(for: .video) 111 let status = AVCaptureDevice.authorizationStatus(for: .video)
@@ -146,11 +183,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega @@ -146,11 +183,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
146 textureId = registry?.register(self) 183 textureId = registry?.register(self)
147 184
148 // Open the camera device 185 // Open the camera device
149 - if #available(iOS 13.0, *) {  
150 - device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInTripleCamera, .builtInDualWideCamera, .builtInDualCamera, .builtInWideAngleCamera], mediaType: .video, position: cameraPosition).devices.first  
151 - } else {  
152 - device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInWideAngleCamera], mediaType: .video, position: cameraPosition).devices.first  
153 - } 186 + device = getDefaultCameraDevice(position: cameraPosition)
154 187
155 if (device == nil) { 188 if (device == nil) {
156 throw MobileScannerError.noCamera 189 throw MobileScannerError.noCamera
@@ -215,26 +248,35 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega @@ -215,26 +248,35 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
215 248
216 backgroundQueue.async { 249 backgroundQueue.async {
217 self.captureSession.startRunning() 250 self.captureSession.startRunning()
218 -  
219 - // Turn on the flashlight if requested,  
220 - // but after the capture session started. 251 +
  252 + // After the capture session started, turn on the torch (if requested)
  253 + // and reset the zoom scale back to the default.
  254 + // Ensure that these adjustments are done on the main DispatchQueue,
  255 + // as they interact with the hardware camera.
221 if (torch) { 256 if (torch) {
  257 + DispatchQueue.main.async {
  258 + do {
  259 + try self.toggleTorch(.on)
  260 + } catch {
  261 + // If the torch does not turn on,
  262 + // continue with the capture session anyway.
  263 + }
  264 + }
  265 + }
  266 +
  267 + DispatchQueue.main.async {
222 do { 268 do {
223 - try self.toggleTorch(.on) 269 + try self.resetScale()
224 } catch { 270 } catch {
225 - // If the torch does not turn on, 271 + // If the zoom scale could not be reset,
226 // continue with the capture session anyway. 272 // continue with the capture session anyway.
227 } 273 }
228 } 274 }
229 275
230 - do {  
231 - try self.resetScale()  
232 - } catch {  
233 - // If the zoom scale could not be reset,  
234 - // continue with the capture session anyway.  
235 - }  
236 -  
237 if let device = self.device { 276 if let device = self.device {
  277 + // When querying the dimensions of the camera,
  278 + // stay on the background thread,
  279 + // as this does not change the configuration of the hardware camera.
238 let dimensions = CMVideoFormatDescriptionGetDimensions( 280 let dimensions = CMVideoFormatDescriptionGetDimensions(
239 device.activeFormat.formatDescription) 281 device.activeFormat.formatDescription)
240 let hasTorch = device.hasTorch 282 let hasTorch = device.hasTorch
@@ -277,12 +319,18 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega @@ -277,12 +319,18 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
277 device = nil 319 device = nil
278 } 320 }
279 321
280 - /// Toggle the flashlight between on and off. 322 + /// Set the torch mode.
  323 + ///
  324 + /// This method should be called on the main DispatchQueue.
281 func toggleTorch(_ torch: AVCaptureDevice.TorchMode) throws { 325 func toggleTorch(_ torch: AVCaptureDevice.TorchMode) throws {
282 - if (device == nil || !device.hasTorch || !device.isTorchAvailable) { 326 + guard let device = self.device else {
283 return 327 return
284 } 328 }
285 - 329 +
  330 + if (!device.hasTorch || !device.isTorchAvailable || !device.isTorchModeSupported(torch)) {
  331 + return
  332 + }
  333 +
286 if (device.torchMode != torch) { 334 if (device.torchMode != torch) {
287 try device.lockForConfiguration() 335 try device.lockForConfiguration()
288 device.torchMode = torch 336 device.torchMode = torch
@@ -61,6 +61,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -61,6 +61,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
61 start(call, result) 61 start(call, result)
62 case "torch": 62 case "torch":
63 toggleTorch(call, result) 63 toggleTorch(call, result)
  64 + case "setScale":
  65 + setScale(call, result)
  66 + case "resetScale":
  67 + resetScale(call, result)
64 // case "analyze": 68 // case "analyze":
65 // switchAnalyzeMode(call, result) 69 // switchAnalyzeMode(call, result)
66 case "stop": 70 case "stop":
@@ -320,7 +324,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -320,7 +324,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
320 324
321 // TODO: this method should be removed when iOS and MacOS share their implementation. 325 // TODO: this method should be removed when iOS and MacOS share their implementation.
322 private func toggleTorchInternal(_ torch: AVCaptureDevice.TorchMode) throws { 326 private func toggleTorchInternal(_ torch: AVCaptureDevice.TorchMode) throws {
323 - if (device == nil || !device.hasTorch) { 327 + guard let device = self.device else {
  328 + return
  329 + }
  330 +
  331 + if (!device.hasTorch || !device.isTorchModeSupported(torch)) {
324 return 332 return
325 } 333 }
326 334
@@ -337,7 +345,19 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -337,7 +345,19 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
337 } 345 }
338 } 346 }
339 347
340 - func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 348 + /// Reset the zoom scale.
  349 + private func resetScale(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  350 + // The zoom scale is not yet supported on MacOS.
  351 + result(nil)
  352 + }
  353 +
  354 + /// Set the zoom scale.
  355 + private func setScale(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  356 + // The zoom scale is not yet supported on MacOS.
  357 + result(nil)
  358 + }
  359 +
  360 + private func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
341 let requestedTorchMode: AVCaptureDevice.TorchMode = call.arguments as! Int == 1 ? .on : .off 361 let requestedTorchMode: AVCaptureDevice.TorchMode = call.arguments as! Int == 1 ? .on : .off
342 362
343 do { 363 do {