Julian Steenbakker

bug: fix start() and stop() crash when called multiple times on iOS and macOS. R…

…emove analyze mode on Android and iOS.
1 include: package:flutter_lints/flutter.yaml 1 include: package:flutter_lints/flutter.yaml
  2 +
  3 +linter:
  4 + rules:
  5 + unawaited_futures: true
@@ -38,8 +38,8 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -38,8 +38,8 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
38 private var camera: Camera? = null 38 private var camera: Camera? = null
39 private var textureEntry: TextureRegistry.SurfaceTextureEntry? = null 39 private var textureEntry: TextureRegistry.SurfaceTextureEntry? = null
40 40
41 - @AnalyzeMode  
42 - private var analyzeMode: Int = AnalyzeMode.NONE 41 +// @AnalyzeMode
  42 +// private var analyzeMode: Int = AnalyzeMode.NONE
43 43
44 @ExperimentalGetImage 44 @ExperimentalGetImage
45 override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result) { 45 override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result) {
@@ -47,8 +47,8 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -47,8 +47,8 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
47 "state" -> checkPermission(result) 47 "state" -> checkPermission(result)
48 "request" -> requestPermission(result) 48 "request" -> requestPermission(result)
49 "start" -> start(call, result) 49 "start" -> start(call, result)
50 - "torch" -> switchTorch(call, result)  
51 - "analyze" -> switchAnalyzeMode(call, result) 50 + "torch" -> toggleTorch(call, result)
  51 +// "analyze" -> switchAnalyzeMode(call, result)
52 "stop" -> stop(result) 52 "stop" -> stop(result)
53 else -> result.notImplemented() 53 else -> result.notImplemented()
54 } 54 }
@@ -92,8 +92,8 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -92,8 +92,8 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
92 92
93 @ExperimentalGetImage 93 @ExperimentalGetImage
94 val analyzer = ImageAnalysis.Analyzer { imageProxy -> // YUV_420_888 format 94 val analyzer = ImageAnalysis.Analyzer { imageProxy -> // YUV_420_888 format
95 - when (analyzeMode) {  
96 - AnalyzeMode.BARCODE -> { 95 +// when (analyzeMode) {
  96 +// AnalyzeMode.BARCODE -> {
97 val mediaImage = imageProxy.image ?: return@Analyzer 97 val mediaImage = imageProxy.image ?: return@Analyzer
98 val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) 98 val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
99 99
@@ -106,9 +106,9 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -106,9 +106,9 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
106 } 106 }
107 .addOnFailureListener { e -> Log.e(TAG, e.message, e) } 107 .addOnFailureListener { e -> Log.e(TAG, e.message, e) }
108 .addOnCompleteListener { imageProxy.close() } 108 .addOnCompleteListener { imageProxy.close() }
109 - }  
110 - else -> imageProxy.close()  
111 - } 109 +// }
  110 +// else -> imageProxy.close()
  111 +// }
112 } 112 }
113 113
114 114
@@ -116,6 +116,10 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -116,6 +116,10 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
116 116
117 @ExperimentalGetImage 117 @ExperimentalGetImage
118 private fun start(call: MethodCall, result: MethodChannel.Result) { 118 private fun start(call: MethodCall, result: MethodChannel.Result) {
  119 + if (camera != null) {
  120 + result.error(TAG, "Called start() while already started!", null)
  121 + return
  122 + }
119 123
120 val facing: Int = call.argument<Int>("facing") ?: 0 124 val facing: Int = call.argument<Int>("facing") ?: 0
121 val ratio: Int? = call.argument<Int>("ratio") 125 val ratio: Int? = call.argument<Int>("ratio")
@@ -186,24 +190,32 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -186,24 +190,32 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
186 }, executor) 190 }, executor)
187 } 191 }
188 192
189 - private fun switchTorch(call: MethodCall, result: MethodChannel.Result) {  
190 - val state = call.arguments == 1  
191 - camera!!.cameraControl.enableTorch(state)  
192 - result.success(null) 193 + private fun toggleTorch(call: MethodCall, result: MethodChannel.Result) {
  194 + if (camera == null) {
  195 + result.error(TAG,"Called toggleTorch() while stopped!", null)
  196 + return
193 } 197 }
194 -  
195 - private fun switchAnalyzeMode(call: MethodCall, result: MethodChannel.Result) {  
196 - analyzeMode = call.arguments as Int 198 + camera!!.cameraControl.enableTorch(call.arguments == 1)
197 result.success(null) 199 result.success(null)
198 } 200 }
199 201
  202 +// private fun switchAnalyzeMode(call: MethodCall, result: MethodChannel.Result) {
  203 +// analyzeMode = call.arguments as Int
  204 +// result.success(null)
  205 +// }
  206 +
200 private fun stop(result: MethodChannel.Result) { 207 private fun stop(result: MethodChannel.Result) {
  208 + if (camera == null) {
  209 + result.error(TAG,"Called stop() while already stopped!", null)
  210 + return
  211 + }
  212 +
201 val owner = activity as LifecycleOwner 213 val owner = activity as LifecycleOwner
202 camera!!.cameraInfo.torchState.removeObservers(owner) 214 camera!!.cameraInfo.torchState.removeObservers(owner)
203 cameraProvider!!.unbindAll() 215 cameraProvider!!.unbindAll()
204 textureEntry!!.release() 216 textureEntry!!.release()
205 217
206 - analyzeMode = AnalyzeMode.NONE 218 +// analyzeMode = AnalyzeMode.NONE
207 camera = null 219 camera = null
208 textureEntry = null 220 textureEntry = null
209 cameraProvider = null 221 cameraProvider = null
@@ -23,7 +23,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -23,7 +23,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
23 var latestBuffer: CVImageBuffer! 23 var latestBuffer: CVImageBuffer!
24 24
25 25
26 - var analyzeMode: Int = 0 26 +// var analyzeMode: Int = 0
27 var analyzing: Bool = false 27 var analyzing: Bool = false
28 var position = AVCaptureDevice.Position.back 28 var position = AVCaptureDevice.Position.back
29 29
@@ -53,9 +53,9 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -53,9 +53,9 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
53 case "start": 53 case "start":
54 start(call, result) 54 start(call, result)
55 case "torch": 55 case "torch":
56 - switchTorch(call, result)  
57 - case "analyze":  
58 - switchAnalyzeMode(call, result) 56 + toggleTorch(call, result)
  57 +// case "analyze":
  58 +// switchAnalyzeMode(call, result)
59 case "stop": 59 case "stop":
60 stop(result) 60 stop(result)
61 default: 61 default:
@@ -89,10 +89,10 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -89,10 +89,10 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
89 latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) 89 latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
90 registry.textureFrameAvailable(textureId) 90 registry.textureFrameAvailable(textureId)
91 91
92 - switch analyzeMode {  
93 - case 1: // barcode 92 +// switch analyzeMode {
  93 +// case 1: // barcode
94 if analyzing { 94 if analyzing {
95 - break 95 + return
96 } 96 }
97 analyzing = true 97 analyzing = true
98 let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) 98 let buffer = CMSampleBufferGetImageBuffer(sampleBuffer)
@@ -112,9 +112,9 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -112,9 +112,9 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
112 } 112 }
113 analyzing = false 113 analyzing = false
114 } 114 }
115 - default: // none  
116 - break  
117 - } 115 +// default: // none
  116 +// break
  117 +// }
118 } 118 }
119 119
120 func imageOrientation( 120 func imageOrientation(
@@ -154,6 +154,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -154,6 +154,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
154 } 154 }
155 155
156 func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 156 func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  157 + if (device != nil) {
  158 + result(FlutterError(code: "MobileScanner",
  159 + message: "Called start() while already started!",
  160 + details: nil))
  161 + return
  162 + }
  163 +
157 textureId = registry.register(self) 164 textureId = registry.register(self)
158 captureSession = AVCaptureSession() 165 captureSession = AVCaptureSession()
159 166
@@ -219,7 +226,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -219,7 +226,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
219 result(answer) 226 result(answer)
220 } 227 }
221 228
222 - func switchTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 229 + func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  230 + if (device == nil) {
  231 + result(FlutterError(code: "MobileScanner",
  232 + message: "Called toggleTorch() while stopped!",
  233 + details: nil))
  234 + return
  235 + }
223 do { 236 do {
224 try device.lockForConfiguration() 237 try device.lockForConfiguration()
225 device.torchMode = call.arguments as! Int == 1 ? .on : .off 238 device.torchMode = call.arguments as! Int == 1 ? .on : .off
@@ -230,12 +243,18 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -230,12 +243,18 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
230 } 243 }
231 } 244 }
232 245
233 - func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {  
234 - analyzeMode = call.arguments as! Int  
235 - result(nil)  
236 - } 246 +// func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  247 +// analyzeMode = call.arguments as! Int
  248 +// result(nil)
  249 +// }
237 250
238 func stop(_ result: FlutterResult) { 251 func stop(_ result: FlutterResult) {
  252 + if (device == nil) {
  253 + result(FlutterError(code: "MobileScanner",
  254 + message: "Called stop() while already stopped!",
  255 + details: nil))
  256 + return
  257 + }
239 captureSession.stopRunning() 258 captureSession.stopRunning()
240 for input in captureSession.inputs { 259 for input in captureSession.inputs {
241 captureSession.removeInput(input) 260 captureSession.removeInput(input)
@@ -246,7 +265,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -246,7 +265,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
246 device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) 265 device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode))
247 registry.unregisterTexture(textureId) 266 registry.unregisterTexture(textureId)
248 267
249 - analyzeMode = 0 268 +// analyzeMode = 0
250 latestBuffer = nil 269 latestBuffer = nil
251 captureSession = nil 270 captureSession = nil
252 device = nil 271 device = nil
@@ -27,7 +27,7 @@ enum TorchState { @@ -27,7 +27,7 @@ enum TorchState {
27 on, 27 on,
28 } 28 }
29 29
30 -enum AnalyzeMode { none, barcode } 30 +// enum AnalyzeMode { none, barcode }
31 31
32 class MobileScannerController { 32 class MobileScannerController {
33 MethodChannel methodChannel = 33 MethodChannel methodChannel =
@@ -62,8 +62,8 @@ class MobileScannerController { @@ -62,8 +62,8 @@ class MobileScannerController {
62 62
63 // Sets analyze mode and barcode stream 63 // Sets analyze mode and barcode stream
64 barcodesController = StreamController.broadcast( 64 barcodesController = StreamController.broadcast(
65 - onListen: () => setAnalyzeMode(AnalyzeMode.barcode.index),  
66 - onCancel: () => setAnalyzeMode(AnalyzeMode.none.index), 65 + // onListen: () => setAnalyzeMode(AnalyzeMode.barcode.index),
  66 + // onCancel: () => setAnalyzeMode(AnalyzeMode.none.index),
67 ); 67 );
68 68
69 start(); 69 start();
@@ -94,20 +94,22 @@ class MobileScannerController { @@ -94,20 +94,22 @@ class MobileScannerController {
94 } 94 }
95 } 95 }
96 96
97 - void setAnalyzeMode(int mode) {  
98 - if (hashCode != _controllerHashcode) {  
99 - return;  
100 - }  
101 - methodChannel.invokeMethod('analyze', mode);  
102 - } 97 + // TODO: Add more analyzers like text analyzer
  98 + // void setAnalyzeMode(int mode) {
  99 + // if (hashCode != _controllerHashcode) {
  100 + // return;
  101 + // }
  102 + // methodChannel.invokeMethod('analyze', mode);
  103 + // }
103 104
104 // List<BarcodeFormats>? formats = _defaultBarcodeFormats, 105 // List<BarcodeFormats>? formats = _defaultBarcodeFormats,
105 /// Start barcode scanning. This will first check if the required permissions 106 /// Start barcode scanning. This will first check if the required permissions
106 /// are set. 107 /// are set.
107 Future<void> start() async { 108 Future<void> start() async {
  109 +
108 ensure('startAsync'); 110 ensure('startAsync');
109 111
110 - setAnalyzeMode(AnalyzeMode.barcode.index); 112 + // setAnalyzeMode(AnalyzeMode.barcode.index);
111 // Check authorization status 113 // Check authorization status
112 MobileScannerState state = 114 MobileScannerState state =
113 MobileScannerState.values[await methodChannel.invokeMethod('state')]; 115 MobileScannerState.values[await methodChannel.invokeMethod('state')];
@@ -132,8 +134,15 @@ class MobileScannerController { @@ -132,8 +134,15 @@ class MobileScannerController {
132 if (torchEnabled != null) arguments['torch'] = torchEnabled; 134 if (torchEnabled != null) arguments['torch'] = torchEnabled;
133 135
134 // Start the camera with arguments 136 // Start the camera with arguments
135 - final Map<String, dynamic>? startResult = await methodChannel 137 + Map<String, dynamic>? startResult = {};
  138 + try {
  139 + startResult = await methodChannel
136 .invokeMapMethod<String, dynamic>('start', arguments); 140 .invokeMapMethod<String, dynamic>('start', arguments);
  141 + } on PlatformException catch (error) {
  142 + debugPrint('${error.code}: ${error.message}');
  143 + // setAnalyzeMode(AnalyzeMode.none.index);
  144 + return;
  145 + }
137 146
138 if (startResult == null) { 147 if (startResult == null) {
139 throw PlatformException(code: 'INITIALIZATION ERROR'); 148 throw PlatformException(code: 'INITIALIZATION ERROR');
@@ -146,17 +155,32 @@ class MobileScannerController { @@ -146,17 +155,32 @@ class MobileScannerController {
146 hasTorch: hasTorch); 155 hasTorch: hasTorch);
147 } 156 }
148 157
149 - Future<void> stop() async => await methodChannel.invokeMethod('stop'); 158 + Future<void> stop() async {
  159 + try {
  160 + await methodChannel.invokeMethod('stop');
  161 + } on PlatformException catch (error) {
  162 + debugPrint('${error.code}: ${error.message}');
  163 + }
  164 + }
150 165
151 /// Switches the torch on or off. 166 /// Switches the torch on or off.
152 /// 167 ///
153 /// Only works if torch is available. 168 /// Only works if torch is available.
154 - void toggleTorch() { 169 + Future<void> toggleTorch() async {
155 ensure('toggleTorch'); 170 ensure('toggleTorch');
156 - if (!hasTorch) return; 171 + if (!hasTorch) {
  172 + debugPrint('Device has no torch/flash.');
  173 + return;
  174 + }
  175 +
157 TorchState state = 176 TorchState state =
158 torchState.value == TorchState.off ? TorchState.on : TorchState.off; 177 torchState.value == TorchState.off ? TorchState.on : TorchState.off;
159 - methodChannel.invokeMethod('torch', state.index); 178 +
  179 + try {
  180 + await methodChannel.invokeMethod('torch', state.index);
  181 + } on PlatformException catch (error) {
  182 + debugPrint('${error.code}: ${error.message}');
  183 + }
160 } 184 }
161 185
162 /// Switches the torch on or off. 186 /// Switches the torch on or off.
@@ -164,10 +188,15 @@ class MobileScannerController { @@ -164,10 +188,15 @@ class MobileScannerController {
164 /// Only works if torch is available. 188 /// Only works if torch is available.
165 Future<void> switchCamera() async { 189 Future<void> switchCamera() async {
166 ensure('switchCamera'); 190 ensure('switchCamera');
167 - await stop(); 191 + try {
  192 + await methodChannel.invokeMethod('stop');
  193 + } on PlatformException catch (error) {
  194 + debugPrint('${error.code}: camera is stopped! Please start before switching camera.');
  195 + return;
  196 + }
168 facing = 197 facing =
169 facing == CameraFacing.back ? CameraFacing.front : CameraFacing.back; 198 facing == CameraFacing.back ? CameraFacing.front : CameraFacing.back;
170 - start(); 199 + await start();
171 } 200 }
172 201
173 /// Disposes the controller and closes all listeners. 202 /// Disposes the controller and closes all listeners.
@@ -22,7 +22,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -22,7 +22,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
22 var latestBuffer: CVImageBuffer! 22 var latestBuffer: CVImageBuffer!
23 23
24 24
25 - var analyzeMode: Int = 0 25 +// var analyzeMode: Int = 0
26 var analyzing: Bool = false 26 var analyzing: Bool = false
27 var position = AVCaptureDevice.Position.back 27 var position = AVCaptureDevice.Position.back
28 28
@@ -52,9 +52,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -52,9 +52,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
52 case "start": 52 case "start":
53 start(call, result) 53 start(call, result)
54 case "torch": 54 case "torch":
55 - switchTorch(call, result)  
56 - case "analyze":  
57 - switchAnalyzeMode(call, result) 55 + toggleTorch(call, result)
  56 +// case "analyze":
  57 +// switchAnalyzeMode(call, result)
58 case "stop": 58 case "stop":
59 stop(result) 59 stop(result)
60 default: 60 default:
@@ -92,14 +92,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -92,14 +92,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
92 latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) 92 latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
93 registry.textureFrameAvailable(textureId) 93 registry.textureFrameAvailable(textureId)
94 94
95 - switch analyzeMode {  
96 - case 1: // barcode 95 +// switch analyzeMode {
  96 +// case 1: // barcode
97 97
98 // Limit the analyzer because the texture output will freeze otherwise 98 // Limit the analyzer because the texture output will freeze otherwise
99 if i / 10 == 1 { 99 if i / 10 == 1 {
100 i = 0 100 i = 0
101 } else { 101 } else {
102 - break 102 + return
103 } 103 }
104 let imageRequestHandler = VNImageRequestHandler( 104 let imageRequestHandler = VNImageRequestHandler(
105 cvPixelBuffer: latestBuffer, 105 cvPixelBuffer: latestBuffer,
@@ -129,9 +129,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -129,9 +129,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
129 print(error) 129 print(error)
130 } 130 }
131 131
132 - default: // none  
133 - break  
134 - } 132 +// default: // none
  133 +// break
  134 +// }
135 } 135 }
136 136
137 func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 137 func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
@@ -159,6 +159,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -159,6 +159,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
159 } 159 }
160 160
161 func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 161 func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  162 + if (device != nil) {
  163 + result(FlutterError(code: "MobileScanner",
  164 + message: "Called start() while already started!",
  165 + details: nil))
  166 + return
  167 + }
  168 +
162 textureId = registry.register(self) 169 textureId = registry.register(self)
163 captureSession = AVCaptureSession() 170 captureSession = AVCaptureSession()
164 171
@@ -222,7 +229,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -222,7 +229,13 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
222 result(answer) 229 result(answer)
223 } 230 }
224 231
225 - func switchTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 232 + func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  233 + if (device == nil) {
  234 + result(FlutterError(code: "MobileScanner",
  235 + message: "Called toggleTorch() while stopped!",
  236 + details: nil))
  237 + return
  238 + }
226 do { 239 do {
227 try device.lockForConfiguration() 240 try device.lockForConfiguration()
228 device.torchMode = call.arguments as! Int == 1 ? .on : .off 241 device.torchMode = call.arguments as! Int == 1 ? .on : .off
@@ -233,12 +246,18 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -233,12 +246,18 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
233 } 246 }
234 } 247 }
235 248
236 - func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {  
237 - analyzeMode = call.arguments as! Int  
238 - result(nil)  
239 - } 249 +// func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  250 +// analyzeMode = call.arguments as! Int
  251 +// result(nil)
  252 +// }
240 253
241 func stop(_ result: FlutterResult) { 254 func stop(_ result: FlutterResult) {
  255 + if (device == nil) {
  256 + result(FlutterError(code: "MobileScanner",
  257 + message: "Called stop() while already stopped!",
  258 + details: nil))
  259 + return
  260 + }
242 captureSession.stopRunning() 261 captureSession.stopRunning()
243 for input in captureSession.inputs { 262 for input in captureSession.inputs {
244 captureSession.removeInput(input) 263 captureSession.removeInput(input)
@@ -249,7 +268,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -249,7 +268,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
249 device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) 268 device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode))
250 registry.unregisterTexture(textureId) 269 registry.unregisterTexture(textureId)
251 270
252 - analyzeMode = 0 271 +// analyzeMode = 0
253 latestBuffer = nil 272 latestBuffer = nil
254 captureSession = nil 273 captureSession = nil
255 device = nil 274 device = nil