Navaron Bracke

fix pending permission request breaking the controller start sequence

@@ -11,7 +11,8 @@ export 'src/enums/phone_type.dart'; @@ -11,7 +11,8 @@ export 'src/enums/phone_type.dart';
11 export 'src/enums/torch_state.dart'; 11 export 'src/enums/torch_state.dart';
12 export 'src/mobile_scanner.dart'; 12 export 'src/mobile_scanner.dart';
13 export 'src/mobile_scanner_controller.dart'; 13 export 'src/mobile_scanner_controller.dart';
14 -export 'src/mobile_scanner_exception.dart'; 14 +export 'src/mobile_scanner_exception.dart'
  15 + hide PermissionRequestPendingException;
15 export 'src/mobile_scanner_platform_interface.dart'; 16 export 'src/mobile_scanner_platform_interface.dart';
16 export 'src/objects/address.dart'; 17 export 'src/objects/address.dart';
17 export 'src/objects/barcode.dart'; 18 export 'src/objects/barcode.dart';
@@ -290,6 +290,8 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { @@ -290,6 +290,8 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
290 zoomScale: 1.0, 290 zoomScale: 1.0,
291 ); 291 );
292 } 292 }
  293 + } on PermissionRequestPendingException catch (_) {
  294 + // If a permission request was already pending, do nothing.
293 } 295 }
294 } 296 }
295 297
@@ -39,3 +39,10 @@ class MobileScannerErrorDetails { @@ -39,3 +39,10 @@ class MobileScannerErrorDetails {
39 /// The error message from the [PlatformException]. 39 /// The error message from the [PlatformException].
40 final String? message; 40 final String? message;
41 } 41 }
  42 +
  43 +/// This class represents an exception that is thrown
  44 +/// when the scanner was (re)started while a permission request was pending.
  45 +///
  46 +/// This exception type is only used internally,
  47 +/// and is not part of the public API.
  48 +class PermissionRequestPendingException implements Exception {}
@@ -39,6 +39,18 @@ class MobileScannerWeb extends MobileScannerPlatform { @@ -39,6 +39,18 @@ class MobileScannerWeb extends MobileScannerPlatform {
39 /// This container element is used by the barcode reader. 39 /// This container element is used by the barcode reader.
40 HTMLDivElement? _divElement; 40 HTMLDivElement? _divElement;
41 41
  42 + /// This [Completer] is used to prevent additional calls to the [start] method.
  43 + ///
  44 + /// To handle lifecycle changes properly,
  45 + /// the scanner is stopped when the application is inactive,
  46 + /// and restarted when the application gains focus.
  47 + ///
  48 + /// However, when the camera permission is requested,
  49 + /// the application is put in the inactive state due to the permission popup gaining focus.
  50 + /// Thus, as long as the permission status is not known,
  51 + /// any calls to the [start] method are ignored.
  52 + Completer<void>? _cameraPermissionCompleter;
  53 +
42 /// The stream controller for the media track settings stream. 54 /// The stream controller for the media track settings stream.
43 final StreamController<MediaTrackSettings> _settingsController = 55 final StreamController<MediaTrackSettings> _settingsController =
44 StreamController.broadcast(); 56 StreamController.broadcast();
@@ -50,6 +62,11 @@ class MobileScannerWeb extends MobileScannerPlatform { @@ -50,6 +62,11 @@ class MobileScannerWeb extends MobileScannerPlatform {
50 MobileScannerPlatform.instance = MobileScannerWeb(); 62 MobileScannerPlatform.instance = MobileScannerWeb();
51 } 63 }
52 64
  65 + bool get _hasPendingPermissionRequest {
  66 + return _cameraPermissionCompleter != null &&
  67 + !_cameraPermissionCompleter!.isCompleted;
  68 + }
  69 +
53 @override 70 @override
54 Stream<BarcodeCapture?> get barcodesStream => _barcodesController.stream; 71 Stream<BarcodeCapture?> get barcodesStream => _barcodesController.stream;
55 72
@@ -174,6 +191,13 @@ class MobileScannerWeb extends MobileScannerPlatform { @@ -174,6 +191,13 @@ class MobileScannerWeb extends MobileScannerPlatform {
174 191
175 @override 192 @override
176 Future<MobileScannerViewAttributes> start(StartOptions startOptions) async { 193 Future<MobileScannerViewAttributes> start(StartOptions startOptions) async {
  194 + // If the permission request has not yet completed,
  195 + // the camera view is not ready yet.
  196 + // Prevent the permission popup from triggering a restart of the scanner.
  197 + if (_hasPendingPermissionRequest) {
  198 + throw PermissionRequestPendingException();
  199 + }
  200 +
177 await _barcodeReader.maybeLoadLibrary( 201 await _barcodeReader.maybeLoadLibrary(
178 alternateScriptUrl: _alternateScriptUrl, 202 alternateScriptUrl: _alternateScriptUrl,
179 ); 203 );