diff --git a/lib/mobile_scanner.dart b/lib/mobile_scanner.dart index 347a90993..d5bd591ef 100644 --- a/lib/mobile_scanner.dart +++ b/lib/mobile_scanner.dart @@ -11,7 +11,8 @@ export 'src/enums/phone_type.dart'; export 'src/enums/torch_state.dart'; export 'src/mobile_scanner.dart'; export 'src/mobile_scanner_controller.dart'; -export 'src/mobile_scanner_exception.dart'; +export 'src/mobile_scanner_exception.dart' + hide PermissionRequestPendingException; export 'src/mobile_scanner_platform_interface.dart'; export 'src/objects/address.dart'; export 'src/objects/barcode.dart'; diff --git a/lib/src/mobile_scanner_controller.dart b/lib/src/mobile_scanner_controller.dart index 91ec82416..e29ff300b 100644 --- a/lib/src/mobile_scanner_controller.dart +++ b/lib/src/mobile_scanner_controller.dart @@ -242,6 +242,13 @@ class MobileScannerController extends ValueNotifier { ); } + // Permission was denied, do nothing. + // When the controller is stopped, + // the error is reset so the permission can be requested again if possible. + if (value.error?.errorCode == MobileScannerErrorCode.permissionDenied) { + return; + } + // Do nothing if the camera is already running. if (value.isRunning) { return; @@ -280,9 +287,6 @@ class MobileScannerController extends ValueNotifier { ); } } on MobileScannerException catch (error) { - // TODO: if the error code is `controllerAlreadyInitialized` ignore the error - // TODO: update the error reporting from the native side to report proper error codes. - // The initialization finished with an error. // To avoid stale values, reset the output size, // torch state and zoom scale to the defaults. @@ -297,6 +301,8 @@ class MobileScannerController extends ValueNotifier { zoomScale: 1.0, ); } + } on PermissionRequestPendingException catch (_) { + // If a permission request was already pending, do nothing. } } diff --git a/lib/src/mobile_scanner_exception.dart b/lib/src/mobile_scanner_exception.dart index cca9bafef..a35c16720 100644 --- a/lib/src/mobile_scanner_exception.dart +++ b/lib/src/mobile_scanner_exception.dart @@ -39,3 +39,10 @@ class MobileScannerErrorDetails { /// The error message from the [PlatformException]. final String? message; } + +/// This class represents an exception that is thrown +/// when the scanner was (re)started while a permission request was pending. +/// +/// This exception type is only used internally, +/// and is not part of the public API. +class PermissionRequestPendingException implements Exception {} diff --git a/lib/src/web/mobile_scanner_web.dart b/lib/src/web/mobile_scanner_web.dart index baf167688..e3ada8c8d 100644 --- a/lib/src/web/mobile_scanner_web.dart +++ b/lib/src/web/mobile_scanner_web.dart @@ -38,6 +38,12 @@ class MobileScannerWeb extends MobileScannerPlatform { /// The container div element for the camera view. late HTMLDivElement _divElement; + /// The flag that keeps track of whether a permission request is in progress. + /// + /// On the web, a permission request triggers a dialog, that in turn triggers a lifecycle change. + /// While the permission request is in progress, any attempts at (re)starting the camera should be ignored. + bool _permissionRequestInProgress = false; + /// The stream controller for the media track settings stream. /// /// Currently, only the facing mode setting can be supported, @@ -180,12 +186,17 @@ class MobileScannerWeb extends MobileScannerPlatform { } try { + _permissionRequestInProgress = true; + // Retrieving the media devices requests the camera permission. final MediaStream videoStream = await window.navigator.mediaDevices.getUserMedia(constraints).toDart; + _permissionRequestInProgress = false; + return videoStream; } on DOMException catch (error, stackTrace) { + _permissionRequestInProgress = false; final String errorMessage = error.toString(); MobileScannerErrorCode errorCode = MobileScannerErrorCode.genericError; @@ -245,15 +256,11 @@ class MobileScannerWeb extends MobileScannerPlatform { @override Future start(StartOptions startOptions) async { - // TODO: ignore double starts in the controller. - if (_barcodeReader != null) { - throw const MobileScannerException( - errorCode: MobileScannerErrorCode.controllerAlreadyInitialized, - errorDetails: MobileScannerErrorDetails( - message: - 'The scanner was already started. Call stop() before calling start() again.', - ), - ); + // If the permission request has not yet completed, + // the camera view is not ready yet. + // Prevent the permission popup from triggering a restart of the scanner. + if (_permissionRequestInProgress) { + throw PermissionRequestPendingException(); } _barcodeReader = ZXingBarcodeReader(); @@ -263,7 +270,6 @@ class MobileScannerWeb extends MobileScannerPlatform { ); if (_barcodeReader?.isScanning ?? false) { - // TODO: ignore double starts in the controller. throw const MobileScannerException( errorCode: MobileScannerErrorCode.controllerAlreadyInitialized, errorDetails: MobileScannerErrorDetails(