Committed by
GitHub
Merge pull request #1086 from navaronbracke/fix_already_started_bug
fix: Ignore additional calls to start() when the controller is starting
Showing
25 changed files
with
271 additions
and
167 deletions
| 1 | ## NEXT | 1 | ## NEXT |
| 2 | 2 | ||
| 3 | +Improvements: | ||
| 3 | * [MacOS] Added the corners and size information to barcode results. | 4 | * [MacOS] Added the corners and size information to barcode results. |
| 4 | * [MacOS] Added support for `analyzeImage`. | 5 | * [MacOS] Added support for `analyzeImage`. |
| 5 | * [MacOS] Added a Privacy Manifest. | 6 | * [MacOS] Added a Privacy Manifest. |
| 6 | * [web] Added the size information to barcode results. | 7 | * [web] Added the size information to barcode results. |
| 7 | * Added support for barcode formats to image analysis. | 8 | * Added support for barcode formats to image analysis. |
| 9 | +* Updated the scanner to report any scanning errors that were encountered during processing. | ||
| 10 | +* Introduced a new getter `hasCameraPermission` for the `MobileScannerState`. | ||
| 11 | +* Fixed a bug in the lifecycle handling sample. Now instead of checking `isInitialized`, | ||
| 12 | +the sample recommends using `hasCameraPermission`, which also guards against camera permission errors. | ||
| 13 | + | ||
| 14 | +Bugs fixed: | ||
| 15 | +* Fixed a bug that would cause the scanner to emit an error when it was already started. Now it ignores any calls to start while it is starting. | ||
| 16 | +* [MacOS] Fixed a bug that prevented the `anaylzeImage()` sample from working properly. | ||
| 8 | 17 | ||
| 9 | ## 5.2.3 | 18 | ## 5.2.3 |
| 10 | 19 |
| @@ -127,7 +127,7 @@ class MyState extends State<MyStatefulWidget> with WidgetsBindingObserver { | @@ -127,7 +127,7 @@ class MyState extends State<MyStatefulWidget> with WidgetsBindingObserver { | ||
| 127 | void didChangeAppLifecycleState(AppLifecycleState state) { | 127 | void didChangeAppLifecycleState(AppLifecycleState state) { |
| 128 | // If the controller is not ready, do not try to start or stop it. | 128 | // If the controller is not ready, do not try to start or stop it. |
| 129 | // Permission dialogs can trigger lifecycle changes before the controller is ready. | 129 | // Permission dialogs can trigger lifecycle changes before the controller is ready. |
| 130 | - if (!controller.value.isInitialized) { | 130 | + if (!controller.value.hasCameraPermission) { |
| 131 | return; | 131 | return; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| @@ -192,4 +192,4 @@ Future<void> dispose() async { | @@ -192,4 +192,4 @@ Future<void> dispose() async { | ||
| 192 | To display the camera preview, pass the controller to a `MobileScanner` widget. | 192 | To display the camera preview, pass the controller to a `MobileScanner` widget. |
| 193 | 193 | ||
| 194 | See the [examples](example/README.md) for runnable examples of various usages, | 194 | See the [examples](example/README.md) for runnable examples of various usages, |
| 195 | -such as the basic usage, applying a scan window, or retrieving images from the barcodes. | 195 | +such as the basic usage, applying a scan window, or retrieving images from the barcodes. |
| @@ -18,6 +18,12 @@ class BarcodeHandler(binaryMessenger: BinaryMessenger) : EventChannel.StreamHand | @@ -18,6 +18,12 @@ class BarcodeHandler(binaryMessenger: BinaryMessenger) : EventChannel.StreamHand | ||
| 18 | eventChannel.setStreamHandler(this) | 18 | eventChannel.setStreamHandler(this) |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | + fun publishError(errorCode: String, errorMessage: String, errorDetails: Any?) { | ||
| 22 | + Handler(Looper.getMainLooper()).post { | ||
| 23 | + eventSink?.error(errorCode, errorMessage, errorDetails) | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + | ||
| 21 | fun publishEvent(event: Map<String, Any>) { | 27 | fun publishEvent(event: Map<String, Any>) { |
| 22 | Handler(Looper.getMainLooper()).post { | 28 | Handler(Looper.getMainLooper()).post { |
| 23 | eventSink?.success(event) | 29 | eventSink?.success(event) |
| @@ -10,6 +10,7 @@ import androidx.camera.core.ExperimentalGetImage | @@ -10,6 +10,7 @@ import androidx.camera.core.ExperimentalGetImage | ||
| 10 | import com.google.mlkit.vision.barcode.BarcodeScannerOptions | 10 | import com.google.mlkit.vision.barcode.BarcodeScannerOptions |
| 11 | import dev.steenbakker.mobile_scanner.objects.BarcodeFormats | 11 | import dev.steenbakker.mobile_scanner.objects.BarcodeFormats |
| 12 | import dev.steenbakker.mobile_scanner.objects.DetectionSpeed | 12 | import dev.steenbakker.mobile_scanner.objects.DetectionSpeed |
| 13 | +import dev.steenbakker.mobile_scanner.objects.MobileScannerErrorCodes | ||
| 13 | import io.flutter.plugin.common.BinaryMessenger | 14 | import io.flutter.plugin.common.BinaryMessenger |
| 14 | import io.flutter.plugin.common.MethodCall | 15 | import io.flutter.plugin.common.MethodCall |
| 15 | import io.flutter.plugin.common.MethodChannel | 16 | import io.flutter.plugin.common.MethodChannel |
| @@ -28,7 +29,7 @@ class MobileScannerHandler( | @@ -28,7 +29,7 @@ class MobileScannerHandler( | ||
| 28 | 29 | ||
| 29 | private val analyzeImageErrorCallback: AnalyzerErrorCallback = { | 30 | private val analyzeImageErrorCallback: AnalyzerErrorCallback = { |
| 30 | Handler(Looper.getMainLooper()).post { | 31 | Handler(Looper.getMainLooper()).post { |
| 31 | - analyzerResult?.error("MobileScanner", it, null) | 32 | + analyzerResult?.error(MobileScannerErrorCodes.BARCODE_ERROR, it, null) |
| 32 | analyzerResult = null | 33 | analyzerResult = null |
| 33 | } | 34 | } |
| 34 | } | 35 | } |
| @@ -65,10 +66,7 @@ class MobileScannerHandler( | @@ -65,10 +66,7 @@ class MobileScannerHandler( | ||
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | private val errorCallback: MobileScannerErrorCallback = {error: String -> | 68 | private val errorCallback: MobileScannerErrorCallback = {error: String -> |
| 68 | - barcodeHandler.publishEvent(mapOf( | ||
| 69 | - "name" to "error", | ||
| 70 | - "data" to error, | ||
| 71 | - )) | 69 | + barcodeHandler.publishError(MobileScannerErrorCodes.BARCODE_ERROR, error, null) |
| 72 | } | 70 | } |
| 73 | 71 | ||
| 74 | private var methodChannel: MethodChannel? = null | 72 | private var methodChannel: MethodChannel? = null |
| @@ -106,21 +104,21 @@ class MobileScannerHandler( | @@ -106,21 +104,21 @@ class MobileScannerHandler( | ||
| 106 | 104 | ||
| 107 | @ExperimentalGetImage | 105 | @ExperimentalGetImage |
| 108 | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { | 106 | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { |
| 109 | - if (mobileScanner == null) { | ||
| 110 | - result.error("MobileScanner", "Called ${call.method} before initializing.", null) | ||
| 111 | - return | ||
| 112 | - } | ||
| 113 | when (call.method) { | 107 | when (call.method) { |
| 114 | "state" -> result.success(permissions.hasCameraPermission(activity)) | 108 | "state" -> result.success(permissions.hasCameraPermission(activity)) |
| 115 | "request" -> permissions.requestPermission( | 109 | "request" -> permissions.requestPermission( |
| 116 | activity, | 110 | activity, |
| 117 | addPermissionListener, | 111 | addPermissionListener, |
| 118 | object: MobileScannerPermissions.ResultCallback { | 112 | object: MobileScannerPermissions.ResultCallback { |
| 119 | - override fun onResult(errorCode: String?, errorDescription: String?) { | 113 | + override fun onResult(errorCode: String?) { |
| 120 | when(errorCode) { | 114 | when(errorCode) { |
| 121 | null -> result.success(true) | 115 | null -> result.success(true) |
| 122 | - MobileScannerPermissions.CAMERA_ACCESS_DENIED -> result.success(false) | ||
| 123 | - else -> result.error(errorCode, errorDescription, null) | 116 | + MobileScannerErrorCodes.CAMERA_ACCESS_DENIED -> result.success(false) |
| 117 | + MobileScannerErrorCodes.CAMERA_PERMISSIONS_REQUEST_ONGOING -> result.error( | ||
| 118 | + MobileScannerErrorCodes.CAMERA_PERMISSIONS_REQUEST_ONGOING, | ||
| 119 | + MobileScannerErrorCodes.CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE, null) | ||
| 120 | + else -> result.error( | ||
| 121 | + MobileScannerErrorCodes.GENERIC_ERROR, MobileScannerErrorCodes.GENERIC_ERROR_MESSAGE, null) | ||
| 124 | } | 122 | } |
| 125 | } | 123 | } |
| 126 | }) | 124 | }) |
| @@ -185,29 +183,29 @@ class MobileScannerHandler( | @@ -185,29 +183,29 @@ class MobileScannerHandler( | ||
| 185 | when (it) { | 183 | when (it) { |
| 186 | is AlreadyStarted -> { | 184 | is AlreadyStarted -> { |
| 187 | result.error( | 185 | result.error( |
| 188 | - "MobileScanner", | ||
| 189 | - "Called start() while already started", | 186 | + MobileScannerErrorCodes.ALREADY_STARTED_ERROR, |
| 187 | + MobileScannerErrorCodes.ALREADY_STARTED_ERROR_MESSAGE, | ||
| 190 | null | 188 | null |
| 191 | ) | 189 | ) |
| 192 | } | 190 | } |
| 193 | is CameraError -> { | 191 | is CameraError -> { |
| 194 | result.error( | 192 | result.error( |
| 195 | - "MobileScanner", | ||
| 196 | - "Error occurred when setting up camera!", | 193 | + MobileScannerErrorCodes.CAMERA_ERROR, |
| 194 | + MobileScannerErrorCodes.CAMERA_ERROR_MESSAGE, | ||
| 197 | null | 195 | null |
| 198 | ) | 196 | ) |
| 199 | } | 197 | } |
| 200 | is NoCamera -> { | 198 | is NoCamera -> { |
| 201 | result.error( | 199 | result.error( |
| 202 | - "MobileScanner", | ||
| 203 | - "No camera found or failed to open camera!", | 200 | + MobileScannerErrorCodes.NO_CAMERA_ERROR, |
| 201 | + MobileScannerErrorCodes.NO_CAMERA_ERROR_MESSAGE, | ||
| 204 | null | 202 | null |
| 205 | ) | 203 | ) |
| 206 | } | 204 | } |
| 207 | else -> { | 205 | else -> { |
| 208 | result.error( | 206 | result.error( |
| 209 | - "MobileScanner", | ||
| 210 | - "Unknown error occurred.", | 207 | + MobileScannerErrorCodes.GENERIC_ERROR, |
| 208 | + MobileScannerErrorCodes.GENERIC_ERROR_MESSAGE, | ||
| 211 | null | 209 | null |
| 212 | ) | 210 | ) |
| 213 | } | 211 | } |
| @@ -252,9 +250,11 @@ class MobileScannerHandler( | @@ -252,9 +250,11 @@ class MobileScannerHandler( | ||
| 252 | mobileScanner!!.setScale(call.arguments as Double) | 250 | mobileScanner!!.setScale(call.arguments as Double) |
| 253 | result.success(null) | 251 | result.success(null) |
| 254 | } catch (e: ZoomWhenStopped) { | 252 | } catch (e: ZoomWhenStopped) { |
| 255 | - result.error("MobileScanner", "Called setScale() while stopped!", null) | 253 | + result.error( |
| 254 | + MobileScannerErrorCodes.SET_SCALE_WHEN_STOPPED_ERROR, MobileScannerErrorCodes.SET_SCALE_WHEN_STOPPED_ERROR_MESSAGE, null) | ||
| 256 | } catch (e: ZoomNotInRange) { | 255 | } catch (e: ZoomNotInRange) { |
| 257 | - result.error("MobileScanner", "Scale should be within 0 and 1", null) | 256 | + result.error( |
| 257 | + MobileScannerErrorCodes.GENERIC_ERROR, MobileScannerErrorCodes.INVALID_ZOOM_SCALE_ERROR_MESSAGE, null) | ||
| 258 | } | 258 | } |
| 259 | } | 259 | } |
| 260 | 260 | ||
| @@ -263,7 +263,8 @@ class MobileScannerHandler( | @@ -263,7 +263,8 @@ class MobileScannerHandler( | ||
| 263 | mobileScanner!!.resetScale() | 263 | mobileScanner!!.resetScale() |
| 264 | result.success(null) | 264 | result.success(null) |
| 265 | } catch (e: ZoomWhenStopped) { | 265 | } catch (e: ZoomWhenStopped) { |
| 266 | - result.error("MobileScanner", "Called resetScale() while stopped!", null) | 266 | + result.error( |
| 267 | + MobileScannerErrorCodes.SET_SCALE_WHEN_STOPPED_ERROR, MobileScannerErrorCodes.SET_SCALE_WHEN_STOPPED_ERROR_MESSAGE, null) | ||
| 267 | } | 268 | } |
| 268 | } | 269 | } |
| 269 | 270 |
| @@ -5,6 +5,7 @@ import android.app.Activity | @@ -5,6 +5,7 @@ import android.app.Activity | ||
| 5 | import android.content.pm.PackageManager | 5 | import android.content.pm.PackageManager |
| 6 | import androidx.core.app.ActivityCompat | 6 | import androidx.core.app.ActivityCompat |
| 7 | import androidx.core.content.ContextCompat | 7 | import androidx.core.content.ContextCompat |
| 8 | +import dev.steenbakker.mobile_scanner.objects.MobileScannerErrorCodes | ||
| 8 | import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener | 9 | import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener |
| 9 | 10 | ||
| 10 | /** | 11 | /** |
| @@ -12,11 +13,6 @@ import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener | @@ -12,11 +13,6 @@ import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener | ||
| 12 | */ | 13 | */ |
| 13 | class MobileScannerPermissions { | 14 | class MobileScannerPermissions { |
| 14 | companion object { | 15 | companion object { |
| 15 | - const val CAMERA_ACCESS_DENIED = "CameraAccessDenied" | ||
| 16 | - const val CAMERA_ACCESS_DENIED_MESSAGE = "Camera access permission was denied." | ||
| 17 | - const val CAMERA_PERMISSIONS_REQUEST_ONGOING = "CameraPermissionsRequestOngoing" | ||
| 18 | - const val CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE = "Another request is ongoing and multiple requests cannot be handled at once." | ||
| 19 | - | ||
| 20 | /** | 16 | /** |
| 21 | * When the application's activity is [androidx.fragment.app.FragmentActivity], requestCode can only use the lower 16 bits. | 17 | * When the application's activity is [androidx.fragment.app.FragmentActivity], requestCode can only use the lower 16 bits. |
| 22 | * @see androidx.fragment.app.FragmentActivity.validateRequestPermissionsRequestCode | 18 | * @see androidx.fragment.app.FragmentActivity.validateRequestPermissionsRequestCode |
| @@ -25,7 +21,7 @@ class MobileScannerPermissions { | @@ -25,7 +21,7 @@ class MobileScannerPermissions { | ||
| 25 | } | 21 | } |
| 26 | 22 | ||
| 27 | interface ResultCallback { | 23 | interface ResultCallback { |
| 28 | - fun onResult(errorCode: String?, errorDescription: String?) | 24 | + fun onResult(errorCode: String?) |
| 29 | } | 25 | } |
| 30 | 26 | ||
| 31 | private var listener: RequestPermissionsResultListener? = null | 27 | private var listener: RequestPermissionsResultListener? = null |
| @@ -53,14 +49,13 @@ class MobileScannerPermissions { | @@ -53,14 +49,13 @@ class MobileScannerPermissions { | ||
| 53 | addPermissionListener: (RequestPermissionsResultListener) -> Unit, | 49 | addPermissionListener: (RequestPermissionsResultListener) -> Unit, |
| 54 | callback: ResultCallback) { | 50 | callback: ResultCallback) { |
| 55 | if (ongoing) { | 51 | if (ongoing) { |
| 56 | - callback.onResult( | ||
| 57 | - CAMERA_PERMISSIONS_REQUEST_ONGOING, CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE) | 52 | + callback.onResult(MobileScannerErrorCodes.CAMERA_PERMISSIONS_REQUEST_ONGOING) |
| 58 | return | 53 | return |
| 59 | } | 54 | } |
| 60 | 55 | ||
| 61 | if(hasCameraPermission(activity) == 1) { | 56 | if(hasCameraPermission(activity) == 1) { |
| 62 | // Permissions already exist. Call the callback with success. | 57 | // Permissions already exist. Call the callback with success. |
| 63 | - callback.onResult(null, null) | 58 | + callback.onResult(null) |
| 64 | return | 59 | return |
| 65 | } | 60 | } |
| 66 | 61 | ||
| @@ -68,10 +63,10 @@ class MobileScannerPermissions { | @@ -68,10 +63,10 @@ class MobileScannerPermissions { | ||
| 68 | // Keep track of the listener, so that it can be unregistered later. | 63 | // Keep track of the listener, so that it can be unregistered later. |
| 69 | listener = MobileScannerPermissionsListener( | 64 | listener = MobileScannerPermissionsListener( |
| 70 | object: ResultCallback { | 65 | object: ResultCallback { |
| 71 | - override fun onResult(errorCode: String?, errorDescription: String?) { | 66 | + override fun onResult(errorCode: String?) { |
| 72 | ongoing = false | 67 | ongoing = false |
| 73 | listener = null | 68 | listener = null |
| 74 | - callback.onResult(errorCode, errorDescription) | 69 | + callback.onResult(errorCode) |
| 75 | } | 70 | } |
| 76 | } | 71 | } |
| 77 | ) | 72 | ) |
| 1 | package dev.steenbakker.mobile_scanner | 1 | package dev.steenbakker.mobile_scanner |
| 2 | 2 | ||
| 3 | import android.content.pm.PackageManager | 3 | import android.content.pm.PackageManager |
| 4 | +import dev.steenbakker.mobile_scanner.objects.MobileScannerErrorCodes | ||
| 4 | import io.flutter.plugin.common.PluginRegistry | 5 | import io.flutter.plugin.common.PluginRegistry |
| 5 | 6 | ||
| 6 | /** | 7 | /** |
| @@ -29,11 +30,9 @@ internal class MobileScannerPermissionsListener( | @@ -29,11 +30,9 @@ internal class MobileScannerPermissionsListener( | ||
| 29 | // grantResults could be empty if the permissions request with the user is interrupted | 30 | // grantResults could be empty if the permissions request with the user is interrupted |
| 30 | // https://developer.android.com/reference/android/app/Activity#onRequestPermissionsResult(int,%20java.lang.String[],%20int[]) | 31 | // https://developer.android.com/reference/android/app/Activity#onRequestPermissionsResult(int,%20java.lang.String[],%20int[]) |
| 31 | if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) { | 32 | if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) { |
| 32 | - resultCallback.onResult( | ||
| 33 | - MobileScannerPermissions.CAMERA_ACCESS_DENIED, | ||
| 34 | - MobileScannerPermissions.CAMERA_ACCESS_DENIED_MESSAGE) | 33 | + resultCallback.onResult(MobileScannerErrorCodes.CAMERA_ACCESS_DENIED) |
| 35 | } else { | 34 | } else { |
| 36 | - resultCallback.onResult(null, null) | 35 | + resultCallback.onResult(null) |
| 37 | } | 36 | } |
| 38 | 37 | ||
| 39 | return true | 38 | return true |
| @@ -488,14 +488,14 @@ | @@ -488,14 +488,14 @@ | ||
| 488 | CODE_SIGN_IDENTITY = "Apple Development"; | 488 | CODE_SIGN_IDENTITY = "Apple Development"; |
| 489 | CODE_SIGN_STYLE = Automatic; | 489 | CODE_SIGN_STYLE = Automatic; |
| 490 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | 490 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; |
| 491 | - DEVELOPMENT_TEAM = 75Y2P2WSQQ; | 491 | + DEVELOPMENT_TEAM = ""; |
| 492 | ENABLE_BITCODE = NO; | 492 | ENABLE_BITCODE = NO; |
| 493 | INFOPLIST_FILE = Runner/Info.plist; | 493 | INFOPLIST_FILE = Runner/Info.plist; |
| 494 | LD_RUNPATH_SEARCH_PATHS = ( | 494 | LD_RUNPATH_SEARCH_PATHS = ( |
| 495 | "$(inherited)", | 495 | "$(inherited)", |
| 496 | "@executable_path/Frameworks", | 496 | "@executable_path/Frameworks", |
| 497 | ); | 497 | ); |
| 498 | - PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner"; | 498 | + PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner-example"; |
| 499 | PRODUCT_NAME = "$(TARGET_NAME)"; | 499 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 500 | PROVISIONING_PROFILE_SPECIFIER = ""; | 500 | PROVISIONING_PROFILE_SPECIFIER = ""; |
| 501 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | 501 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; |
| @@ -670,14 +670,14 @@ | @@ -670,14 +670,14 @@ | ||
| 670 | CODE_SIGN_IDENTITY = "Apple Development"; | 670 | CODE_SIGN_IDENTITY = "Apple Development"; |
| 671 | CODE_SIGN_STYLE = Automatic; | 671 | CODE_SIGN_STYLE = Automatic; |
| 672 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | 672 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; |
| 673 | - DEVELOPMENT_TEAM = 75Y2P2WSQQ; | 673 | + DEVELOPMENT_TEAM = ""; |
| 674 | ENABLE_BITCODE = NO; | 674 | ENABLE_BITCODE = NO; |
| 675 | INFOPLIST_FILE = Runner/Info.plist; | 675 | INFOPLIST_FILE = Runner/Info.plist; |
| 676 | LD_RUNPATH_SEARCH_PATHS = ( | 676 | LD_RUNPATH_SEARCH_PATHS = ( |
| 677 | "$(inherited)", | 677 | "$(inherited)", |
| 678 | "@executable_path/Frameworks", | 678 | "@executable_path/Frameworks", |
| 679 | ); | 679 | ); |
| 680 | - PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner"; | 680 | + PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner-example"; |
| 681 | PRODUCT_NAME = "$(TARGET_NAME)"; | 681 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 682 | PROVISIONING_PROFILE_SPECIFIER = ""; | 682 | PROVISIONING_PROFILE_SPECIFIER = ""; |
| 683 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | 683 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; |
| @@ -696,14 +696,14 @@ | @@ -696,14 +696,14 @@ | ||
| 696 | CODE_SIGN_IDENTITY = "Apple Development"; | 696 | CODE_SIGN_IDENTITY = "Apple Development"; |
| 697 | CODE_SIGN_STYLE = Automatic; | 697 | CODE_SIGN_STYLE = Automatic; |
| 698 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | 698 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; |
| 699 | - DEVELOPMENT_TEAM = 75Y2P2WSQQ; | 699 | + DEVELOPMENT_TEAM = ""; |
| 700 | ENABLE_BITCODE = NO; | 700 | ENABLE_BITCODE = NO; |
| 701 | INFOPLIST_FILE = Runner/Info.plist; | 701 | INFOPLIST_FILE = Runner/Info.plist; |
| 702 | LD_RUNPATH_SEARCH_PATHS = ( | 702 | LD_RUNPATH_SEARCH_PATHS = ( |
| 703 | "$(inherited)", | 703 | "$(inherited)", |
| 704 | "@executable_path/Frameworks", | 704 | "@executable_path/Frameworks", |
| 705 | ); | 705 | ); |
| 706 | - PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner"; | 706 | + PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner-example"; |
| 707 | PRODUCT_NAME = "$(TARGET_NAME)"; | 707 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 708 | PROVISIONING_PROFILE_SPECIFIER = ""; | 708 | PROVISIONING_PROFILE_SPECIFIER = ""; |
| 709 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | 709 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; |
| @@ -22,7 +22,14 @@ class _BarcodeScannerAnalyzeImageState | @@ -22,7 +22,14 @@ class _BarcodeScannerAnalyzeImageState | ||
| 22 | final XFile? file = | 22 | final XFile? file = |
| 23 | await ImagePicker().pickImage(source: ImageSource.gallery); | 23 | await ImagePicker().pickImage(source: ImageSource.gallery); |
| 24 | 24 | ||
| 25 | - if (!mounted || file == null) { | 25 | + if (!mounted) { |
| 26 | + return; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + if (file == null) { | ||
| 30 | + setState(() { | ||
| 31 | + _barcodeCapture = null; | ||
| 32 | + }); | ||
| 26 | return; | 33 | return; |
| 27 | } | 34 | } |
| 28 | 35 | ||
| @@ -43,7 +50,7 @@ class _BarcodeScannerAnalyzeImageState | @@ -43,7 +50,7 @@ class _BarcodeScannerAnalyzeImageState | ||
| 43 | 50 | ||
| 44 | if (_barcodeCapture != null) { | 51 | if (_barcodeCapture != null) { |
| 45 | label = Text( | 52 | label = Text( |
| 46 | - _barcodeCapture?.barcodes.firstOrNull?.displayValue ?? | 53 | + _barcodeCapture?.barcodes.firstOrNull?.rawValue ?? |
| 47 | 'No barcode detected', | 54 | 'No barcode detected', |
| 48 | ); | 55 | ); |
| 49 | } | 56 | } |
| @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate { | @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate { | ||
| 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { | 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { |
| 7 | return true | 7 | return true |
| 8 | } | 8 | } |
| 9 | + | ||
| 10 | + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { | ||
| 11 | + return true | ||
| 12 | + } | ||
| 9 | } | 13 | } |
| @@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
| 21 | <meta name="description" content="Demonstrates how to use the mobile_scanner plugin."> | 21 | <meta name="description" content="Demonstrates how to use the mobile_scanner plugin."> |
| 22 | 22 | ||
| 23 | <!-- iOS meta tags & icons --> | 23 | <!-- iOS meta tags & icons --> |
| 24 | - <meta name="apple-mobile-web-app-capable" content="yes"> | 24 | + <meta name="mobile-web-app-capable" content="yes"> |
| 25 | <meta name="apple-mobile-web-app-status-bar-style" content="black"> | 25 | <meta name="apple-mobile-web-app-status-bar-style" content="black"> |
| 26 | <meta name="apple-mobile-web-app-title" content="mobile_scanner_example"> | 26 | <meta name="apple-mobile-web-app-title" content="mobile_scanner_example"> |
| 27 | <link rel="apple-touch-icon" href="icons/Icon-192.png"> | 27 | <link rel="apple-touch-icon" href="icons/Icon-192.png"> |
| @@ -19,6 +19,12 @@ public class BarcodeHandler: NSObject, FlutterStreamHandler { | @@ -19,6 +19,12 @@ public class BarcodeHandler: NSObject, FlutterStreamHandler { | ||
| 19 | eventChannel.setStreamHandler(self) | 19 | eventChannel.setStreamHandler(self) |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | + func publishError(_ error: FlutterError) { | ||
| 23 | + DispatchQueue.main.async { | ||
| 24 | + self.eventSink?(error) | ||
| 25 | + } | ||
| 26 | + } | ||
| 27 | + | ||
| 22 | func publishEvent(_ event: [String: Any?]) { | 28 | func publishEvent(_ event: [String: Any?]) { |
| 23 | DispatchQueue.main.async { | 29 | DispatchQueue.main.async { |
| 24 | self.eventSink?(event) | 30 | self.eventSink?(event) |
| @@ -20,7 +20,7 @@ struct MobileScannerErrorCodes { | @@ -20,7 +20,7 @@ struct MobileScannerErrorCodes { | ||
| 20 | // because it uses the error message from the undelying error. | 20 | // because it uses the error message from the undelying error. |
| 21 | static let BARCODE_ERROR = "MOBILE_SCANNER_BARCODE_ERROR" | 21 | static let BARCODE_ERROR = "MOBILE_SCANNER_BARCODE_ERROR" |
| 22 | // The error code 'CAMERA_ERROR' does not have an error message, | 22 | // The error code 'CAMERA_ERROR' does not have an error message, |
| 23 | - // because it uses the error message from the underlying error. | 23 | + // because it uses the error message from the underlying error. |
| 24 | static let CAMERA_ERROR = "MOBILE_SCANNER_CAMERA_ERROR" | 24 | static let CAMERA_ERROR = "MOBILE_SCANNER_CAMERA_ERROR" |
| 25 | static let GENERIC_ERROR = "MOBILE_SCANNER_GENERIC_ERROR" | 25 | static let GENERIC_ERROR = "MOBILE_SCANNER_GENERIC_ERROR" |
| 26 | static let GENERIC_ERROR_MESSAGE = "An unknown error occurred." | 26 | static let GENERIC_ERROR_MESSAGE = "An unknown error occurred." |
| @@ -42,7 +42,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -42,7 +42,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 42 | init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) { | 42 | init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) { |
| 43 | self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in | 43 | self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in |
| 44 | if error != nil { | 44 | if error != nil { |
| 45 | - barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription]) | 45 | + barcodeHandler.publishError( |
| 46 | + FlutterError(code: MobileScannerErrorCodes.BARCODE_ERROR, | ||
| 47 | + message: error?.localizedDescription, | ||
| 48 | + details: nil)) | ||
| 46 | return | 49 | return |
| 47 | } | 50 | } |
| 48 | 51 | ||
| @@ -150,20 +153,20 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -150,20 +153,20 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 150 | } | 153 | } |
| 151 | } | 154 | } |
| 152 | } catch MobileScannerError.alreadyStarted { | 155 | } catch MobileScannerError.alreadyStarted { |
| 153 | - result(FlutterError(code: "MobileScanner", | ||
| 154 | - message: "Called start() while already started!", | 156 | + result(FlutterError(code: MobileScannerErrorCodes.ALREADY_STARTED_ERROR, |
| 157 | + message: MobileScannerErrorCodes.ALREADY_STARTED_ERROR_MESSAGE, | ||
| 155 | details: nil)) | 158 | details: nil)) |
| 156 | } catch MobileScannerError.noCamera { | 159 | } catch MobileScannerError.noCamera { |
| 157 | - result(FlutterError(code: "MobileScanner", | ||
| 158 | - message: "No camera found or failed to open camera!", | 160 | + result(FlutterError(code: MobileScannerErrorCodes.NO_CAMERA_ERROR, |
| 161 | + message: MobileScannerErrorCodes.NO_CAMERA_ERROR_MESSAGE, | ||
| 159 | details: nil)) | 162 | details: nil)) |
| 160 | } catch MobileScannerError.cameraError(let error) { | 163 | } catch MobileScannerError.cameraError(let error) { |
| 161 | - result(FlutterError(code: "MobileScanner", | ||
| 162 | - message: "Error occured when setting up camera!", | ||
| 163 | - details: error)) | 164 | + result(FlutterError(code: MobileScannerErrorCodes.CAMERA_ERROR, |
| 165 | + message: error.localizedDescription, | ||
| 166 | + details: nil)) | ||
| 164 | } catch { | 167 | } catch { |
| 165 | - result(FlutterError(code: "MobileScanner", | ||
| 166 | - message: "Unknown error occured.", | 168 | + result(FlutterError(code: MobileScannerErrorCodes.GENERIC_ERROR, |
| 169 | + message: MobileScannerErrorCodes.GENERIC_ERROR_MESSAGE, | ||
| 167 | details: nil)) | 170 | details: nil)) |
| 168 | } | 171 | } |
| 169 | } | 172 | } |
| @@ -186,25 +189,25 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -186,25 +189,25 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 186 | private func setScale(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 189 | private func setScale(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 187 | let scale = call.arguments as? CGFloat | 190 | let scale = call.arguments as? CGFloat |
| 188 | if (scale == nil) { | 191 | if (scale == nil) { |
| 189 | - result(FlutterError(code: "MobileScanner", | ||
| 190 | - message: "You must provide a scale when calling setScale!", | ||
| 191 | - details: nil)) | 192 | + result(FlutterError(code: MobileScannerErrorCodes.GENERIC_ERROR, |
| 193 | + message: MobileScannerErrorCodes.INVALID_ZOOM_SCALE_ERROR_MESSAGE, | ||
| 194 | + details: "The invalid zoom scale was nil.")) | ||
| 192 | return | 195 | return |
| 193 | } | 196 | } |
| 194 | do { | 197 | do { |
| 195 | try mobileScanner.setScale(scale!) | 198 | try mobileScanner.setScale(scale!) |
| 196 | result(nil) | 199 | result(nil) |
| 197 | } catch MobileScannerError.zoomWhenStopped { | 200 | } catch MobileScannerError.zoomWhenStopped { |
| 198 | - result(FlutterError(code: "MobileScanner", | ||
| 199 | - message: "Called setScale() while stopped!", | 201 | + result(FlutterError(code: MobileScannerErrorCodes.SET_SCALE_WHEN_STOPPED_ERROR, |
| 202 | + message: MobileScannerErrorCodes.SET_SCALE_WHEN_STOPPED_ERROR_MESSAGE, | ||
| 200 | details: nil)) | 203 | details: nil)) |
| 201 | } catch MobileScannerError.zoomError(let error) { | 204 | } catch MobileScannerError.zoomError(let error) { |
| 202 | - result(FlutterError(code: "MobileScanner", | ||
| 203 | - message: "Error while zooming.", | ||
| 204 | - details: error)) | 205 | + result(FlutterError(code: MobileScannerErrorCodes.GENERIC_ERROR, |
| 206 | + message: error.localizedDescription, | ||
| 207 | + details: nil)) | ||
| 205 | } catch { | 208 | } catch { |
| 206 | - result(FlutterError(code: "MobileScanner", | ||
| 207 | - message: "Error while zooming.", | 209 | + result(FlutterError(code: MobileScannerErrorCodes.GENERIC_ERROR, |
| 210 | + message: MobileScannerErrorCodes.GENERIC_ERROR_MESSAGE, | ||
| 208 | details: nil)) | 211 | details: nil)) |
| 209 | } | 212 | } |
| 210 | } | 213 | } |
| @@ -215,16 +218,16 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -215,16 +218,16 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 215 | try mobileScanner.resetScale() | 218 | try mobileScanner.resetScale() |
| 216 | result(nil) | 219 | result(nil) |
| 217 | } catch MobileScannerError.zoomWhenStopped { | 220 | } catch MobileScannerError.zoomWhenStopped { |
| 218 | - result(FlutterError(code: "MobileScanner", | ||
| 219 | - message: "Called resetScale() while stopped!", | 221 | + result(FlutterError(code: MobileScannerErrorCodes.SET_SCALE_WHEN_STOPPED_ERROR, |
| 222 | + message: MobileScannerErrorCodes.SET_SCALE_WHEN_STOPPED_ERROR_MESSAGE, | ||
| 220 | details: nil)) | 223 | details: nil)) |
| 221 | } catch MobileScannerError.zoomError(let error) { | 224 | } catch MobileScannerError.zoomError(let error) { |
| 222 | - result(FlutterError(code: "MobileScanner", | ||
| 223 | - message: "Error while zooming.", | ||
| 224 | - details: error)) | 225 | + result(FlutterError(code: MobileScannerErrorCodes.GENERIC_ERROR, |
| 226 | + message: error.localizedDescription, | ||
| 227 | + details: nil)) | ||
| 225 | } catch { | 228 | } catch { |
| 226 | - result(FlutterError(code: "MobileScanner", | ||
| 227 | - message: "Error while zooming.", | 229 | + result(FlutterError(code: MobileScannerErrorCodes.GENERIC_ERROR, |
| 230 | + message: MobileScannerErrorCodes.GENERIC_ERROR_MESSAGE, | ||
| 228 | details: nil)) | 231 | details: nil)) |
| 229 | } | 232 | } |
| 230 | } | 233 | } |
| @@ -266,7 +269,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -266,7 +269,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 266 | barcodeScannerOptions: scannerOptions, callback: { barcodes, error in | 269 | barcodeScannerOptions: scannerOptions, callback: { barcodes, error in |
| 267 | if error != nil { | 270 | if error != nil { |
| 268 | DispatchQueue.main.async { | 271 | DispatchQueue.main.async { |
| 269 | - result(FlutterError(code: "MobileScanner", | 272 | + result(FlutterError(code: MobileScannerErrorCodes.BARCODE_ERROR, |
| 270 | message: error?.localizedDescription, | 273 | message: error?.localizedDescription, |
| 271 | details: nil)) | 274 | details: nil)) |
| 272 | } | 275 | } |
| @@ -11,8 +11,7 @@ export 'src/enums/phone_type.dart'; | @@ -11,8 +11,7 @@ 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' | ||
| 15 | - hide PermissionRequestPendingException; | 14 | +export 'src/mobile_scanner_exception.dart'; |
| 16 | export 'src/mobile_scanner_platform_interface.dart'; | 15 | export 'src/mobile_scanner_platform_interface.dart'; |
| 17 | export 'src/objects/address.dart'; | 16 | export 'src/objects/address.dart'; |
| 18 | export 'src/objects/barcode.dart'; | 17 | export 'src/objects/barcode.dart'; |
| @@ -16,6 +16,14 @@ import 'package:mobile_scanner/src/objects/start_options.dart'; | @@ -16,6 +16,14 @@ import 'package:mobile_scanner/src/objects/start_options.dart'; | ||
| 16 | 16 | ||
| 17 | /// An implementation of [MobileScannerPlatform] that uses method channels. | 17 | /// An implementation of [MobileScannerPlatform] that uses method channels. |
| 18 | class MethodChannelMobileScanner extends MobileScannerPlatform { | 18 | class MethodChannelMobileScanner extends MobileScannerPlatform { |
| 19 | + /// The name of the barcode event that is sent when a barcode is scanned. | ||
| 20 | + @visibleForTesting | ||
| 21 | + static const String kBarcodeEventName = 'barcode'; | ||
| 22 | + | ||
| 23 | + /// The name of the error event that is sent when a barcode scan error occurs. | ||
| 24 | + @visibleForTesting | ||
| 25 | + static const String kBarcodeErrorEventName = 'MOBILE_SCANNER_BARCODE_ERROR'; | ||
| 26 | + | ||
| 19 | /// The method channel used to interact with the native platform. | 27 | /// The method channel used to interact with the native platform. |
| 20 | @visibleForTesting | 28 | @visibleForTesting |
| 21 | final methodChannel = const MethodChannel( | 29 | final methodChannel = const MethodChannel( |
| @@ -79,6 +87,19 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | @@ -79,6 +87,19 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | ||
| 79 | ); | 87 | ); |
| 80 | } | 88 | } |
| 81 | 89 | ||
| 90 | + /// Parse a [MobileScannerBarcodeException] from the given [error] and [stackTrace], and throw it. | ||
| 91 | + /// | ||
| 92 | + /// If the error is not a [PlatformException], | ||
| 93 | + /// with [kBarcodeErrorEventName] as [PlatformException.code], the error is rethrown as-is. | ||
| 94 | + Never _parseBarcodeError(Object error, StackTrace stackTrace) { | ||
| 95 | + if (error case PlatformException(:final String code, :final String? message) | ||
| 96 | + when code == kBarcodeErrorEventName) { | ||
| 97 | + throw MobileScannerBarcodeException(message); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + Error.throwWithStackTrace(error, stackTrace); | ||
| 101 | + } | ||
| 102 | + | ||
| 82 | /// Request permission to access the camera. | 103 | /// Request permission to access the camera. |
| 83 | /// | 104 | /// |
| 84 | /// Throws a [MobileScannerException] if the permission is not granted. | 105 | /// Throws a [MobileScannerException] if the permission is not granted. |
| @@ -121,9 +142,12 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | @@ -121,9 +142,12 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | ||
| 121 | 142 | ||
| 122 | @override | 143 | @override |
| 123 | Stream<BarcodeCapture?> get barcodesStream { | 144 | Stream<BarcodeCapture?> get barcodesStream { |
| 145 | + // Handle incoming barcode events. | ||
| 146 | + // The error events are transformed to `MobileScannerBarcodeException` where possible. | ||
| 124 | return eventsStream | 147 | return eventsStream |
| 125 | - .where((event) => event['name'] == 'barcode') | ||
| 126 | - .map((event) => _parseBarcode(event)); | 148 | + .where((e) => e['name'] == kBarcodeEventName) |
| 149 | + .map((event) => _parseBarcode(event)) | ||
| 150 | + .handleError(_parseBarcodeError); | ||
| 127 | } | 151 | } |
| 128 | 152 | ||
| 129 | @override | 153 | @override |
| @@ -145,21 +169,30 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | @@ -145,21 +169,30 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | ||
| 145 | String path, { | 169 | String path, { |
| 146 | List<BarcodeFormat> formats = const <BarcodeFormat>[], | 170 | List<BarcodeFormat> formats = const <BarcodeFormat>[], |
| 147 | }) async { | 171 | }) async { |
| 148 | - final Map<Object?, Object?>? result = | ||
| 149 | - await methodChannel.invokeMapMethod<Object?, Object?>( | ||
| 150 | - 'analyzeImage', | ||
| 151 | - { | ||
| 152 | - 'filePath': path, | ||
| 153 | - 'formats': formats.isEmpty | ||
| 154 | - ? null | ||
| 155 | - : [ | ||
| 156 | - for (final BarcodeFormat format in formats) | ||
| 157 | - if (format != BarcodeFormat.unknown) format.rawValue, | ||
| 158 | - ], | ||
| 159 | - }, | ||
| 160 | - ); | 172 | + try { |
| 173 | + final Map<Object?, Object?>? result = | ||
| 174 | + await methodChannel.invokeMapMethod<Object?, Object?>( | ||
| 175 | + 'analyzeImage', | ||
| 176 | + { | ||
| 177 | + 'filePath': path, | ||
| 178 | + 'formats': formats.isEmpty | ||
| 179 | + ? null | ||
| 180 | + : [ | ||
| 181 | + for (final BarcodeFormat format in formats) | ||
| 182 | + if (format != BarcodeFormat.unknown) format.rawValue, | ||
| 183 | + ], | ||
| 184 | + }, | ||
| 185 | + ); | ||
| 186 | + | ||
| 187 | + return _parseBarcode(result); | ||
| 188 | + } on PlatformException catch (error) { | ||
| 189 | + // Handle any errors from analyze image requests. | ||
| 190 | + if (error.code == kBarcodeErrorEventName) { | ||
| 191 | + throw MobileScannerBarcodeException(error.message); | ||
| 192 | + } | ||
| 161 | 193 | ||
| 162 | - return _parseBarcode(result); | 194 | + return null; |
| 195 | + } | ||
| 163 | } | 196 | } |
| 164 | 197 | ||
| 165 | @override | 198 | @override |
| @@ -35,6 +35,12 @@ class MobileScanner extends StatefulWidget { | @@ -35,6 +35,12 @@ class MobileScanner extends StatefulWidget { | ||
| 35 | 35 | ||
| 36 | /// The function that signals when new codes were detected by the [controller]. | 36 | /// The function that signals when new codes were detected by the [controller]. |
| 37 | /// If null, use the controller.barcodes stream directly to capture barcodes. | 37 | /// If null, use the controller.barcodes stream directly to capture barcodes. |
| 38 | + /// | ||
| 39 | + /// This method does not receive any [MobileScannerBarcodeException]s | ||
| 40 | + /// that are emitted by the scanner. | ||
| 41 | + /// | ||
| 42 | + /// To handle both [BarcodeCapture]s and [MobileScannerBarcodeException]s, | ||
| 43 | + /// use the [MobileScannerController.barcodes] stream directly. | ||
| 38 | final void Function(BarcodeCapture barcodes)? onDetect; | 44 | final void Function(BarcodeCapture barcodes)? onDetect; |
| 39 | 45 | ||
| 40 | /// The error builder for the camera preview. | 46 | /// The error builder for the camera preview. |
| @@ -102,6 +102,9 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | @@ -102,6 +102,9 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | ||
| 102 | StreamController.broadcast(); | 102 | StreamController.broadcast(); |
| 103 | 103 | ||
| 104 | /// Get the stream of scanned barcodes. | 104 | /// Get the stream of scanned barcodes. |
| 105 | + /// | ||
| 106 | + /// If an error occurred during the detection of a barcode, | ||
| 107 | + /// a [MobileScannerBarcodeException] error is emitted to the stream. | ||
| 105 | Stream<BarcodeCapture> get barcodes => _barcodesController.stream; | 108 | Stream<BarcodeCapture> get barcodes => _barcodesController.stream; |
| 106 | 109 | ||
| 107 | StreamSubscription<BarcodeCapture?>? _barcodesSubscription; | 110 | StreamSubscription<BarcodeCapture?>? _barcodesSubscription; |
| @@ -121,14 +124,25 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | @@ -121,14 +124,25 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | ||
| 121 | } | 124 | } |
| 122 | 125 | ||
| 123 | void _setupListeners() { | 126 | void _setupListeners() { |
| 124 | - _barcodesSubscription = MobileScannerPlatform.instance.barcodesStream | ||
| 125 | - .listen((BarcodeCapture? barcode) { | ||
| 126 | - if (_barcodesController.isClosed || barcode == null) { | ||
| 127 | - return; | ||
| 128 | - } | ||
| 129 | - | ||
| 130 | - _barcodesController.add(barcode); | ||
| 131 | - }); | 127 | + _barcodesSubscription = |
| 128 | + MobileScannerPlatform.instance.barcodesStream.listen( | ||
| 129 | + (BarcodeCapture? barcode) { | ||
| 130 | + if (_barcodesController.isClosed || barcode == null) { | ||
| 131 | + return; | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + _barcodesController.add(barcode); | ||
| 135 | + }, | ||
| 136 | + onError: (Object error) { | ||
| 137 | + if (_barcodesController.isClosed) { | ||
| 138 | + return; | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + _barcodesController.addError(error); | ||
| 142 | + }, | ||
| 143 | + // Errors are handled gracefully by forwarding them. | ||
| 144 | + cancelOnError: false, | ||
| 145 | + ); | ||
| 132 | 146 | ||
| 133 | _torchStateSubscription = MobileScannerPlatform.instance.torchStateStream | 147 | _torchStateSubscription = MobileScannerPlatform.instance.torchStateStream |
| 134 | .listen((TorchState torchState) { | 148 | .listen((TorchState torchState) { |
| @@ -177,6 +191,9 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | @@ -177,6 +191,9 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | ||
| 177 | /// This is only supported on Android, iOS and MacOS. | 191 | /// This is only supported on Android, iOS and MacOS. |
| 178 | /// | 192 | /// |
| 179 | /// Returns the [BarcodeCapture] that was found in the image. | 193 | /// Returns the [BarcodeCapture] that was found in the image. |
| 194 | + /// | ||
| 195 | + /// If an error occurred during the analysis of the image, | ||
| 196 | + /// a [MobileScannerBarcodeException] error is thrown. | ||
| 180 | Future<BarcodeCapture?> analyzeImage(String path) { | 197 | Future<BarcodeCapture?> analyzeImage(String path) { |
| 181 | return MobileScannerPlatform.instance.analyzeImage(path); | 198 | return MobileScannerPlatform.instance.analyzeImage(path); |
| 182 | } | 199 | } |
| @@ -246,13 +263,6 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | @@ -246,13 +263,6 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | ||
| 246 | ); | 263 | ); |
| 247 | } | 264 | } |
| 248 | 265 | ||
| 249 | - // Permission was denied, do nothing. | ||
| 250 | - // When the controller is stopped, | ||
| 251 | - // the error is reset so the permission can be requested again if possible. | ||
| 252 | - if (value.error?.errorCode == MobileScannerErrorCode.permissionDenied) { | ||
| 253 | - return; | ||
| 254 | - } | ||
| 255 | - | ||
| 256 | // Do nothing if the camera is already running. | 266 | // Do nothing if the camera is already running. |
| 257 | if (value.isRunning) { | 267 | if (value.isRunning) { |
| 258 | return; | 268 | return; |
| @@ -292,6 +302,13 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | @@ -292,6 +302,13 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | ||
| 292 | ); | 302 | ); |
| 293 | } | 303 | } |
| 294 | } on MobileScannerException catch (error) { | 304 | } on MobileScannerException catch (error) { |
| 305 | + // If the controller is already initialized, ignore the error. | ||
| 306 | + // Starting the controller while it is already started, or in the process of starting, is redundant. | ||
| 307 | + if (error.errorCode == | ||
| 308 | + MobileScannerErrorCode.controllerAlreadyInitialized) { | ||
| 309 | + return; | ||
| 310 | + } | ||
| 311 | + | ||
| 295 | // The initialization finished with an error. | 312 | // The initialization finished with an error. |
| 296 | // To avoid stale values, reset the output size, | 313 | // To avoid stale values, reset the output size, |
| 297 | // torch state and zoom scale to the defaults. | 314 | // torch state and zoom scale to the defaults. |
| @@ -306,8 +323,6 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | @@ -306,8 +323,6 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | ||
| 306 | zoomScale: 1.0, | 323 | zoomScale: 1.0, |
| 307 | ); | 324 | ); |
| 308 | } | 325 | } |
| 309 | - } on PermissionRequestPendingException catch (_) { | ||
| 310 | - // If a permission request was already pending, do nothing. | ||
| 311 | } | 326 | } |
| 312 | } | 327 | } |
| 313 | 328 |
| @@ -40,13 +40,6 @@ class MobileScannerErrorDetails { | @@ -40,13 +40,6 @@ class MobileScannerErrorDetails { | ||
| 40 | final String? message; | 40 | final String? message; |
| 41 | } | 41 | } |
| 42 | 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 {} | ||
| 49 | - | ||
| 50 | /// This class represents an exception thrown by the [MobileScannerController] | 43 | /// This class represents an exception thrown by the [MobileScannerController] |
| 51 | /// when a barcode scanning error occurs when processing an input frame. | 44 | /// when a barcode scanning error occurs when processing an input frame. |
| 52 | class MobileScannerBarcodeException implements Exception { | 45 | class MobileScannerBarcodeException implements Exception { |
| @@ -39,12 +39,6 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -39,12 +39,6 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 39 | /// The container div element for the camera view. | 39 | /// The container div element for the camera view. |
| 40 | late HTMLDivElement _divElement; | 40 | late HTMLDivElement _divElement; |
| 41 | 41 | ||
| 42 | - /// The flag that keeps track of whether a permission request is in progress. | ||
| 43 | - /// | ||
| 44 | - /// On the web, a permission request triggers a dialog, that in turn triggers a lifecycle change. | ||
| 45 | - /// While the permission request is in progress, any attempts at (re)starting the camera should be ignored. | ||
| 46 | - bool _permissionRequestInProgress = false; | ||
| 47 | - | ||
| 48 | /// The stream controller for the media track settings stream. | 42 | /// The stream controller for the media track settings stream. |
| 49 | /// | 43 | /// |
| 50 | /// Currently, only the facing mode setting can be supported, | 44 | /// Currently, only the facing mode setting can be supported, |
| @@ -199,17 +193,12 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -199,17 +193,12 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 199 | } | 193 | } |
| 200 | 194 | ||
| 201 | try { | 195 | try { |
| 202 | - _permissionRequestInProgress = true; | ||
| 203 | - | ||
| 204 | // Retrieving the media devices requests the camera permission. | 196 | // Retrieving the media devices requests the camera permission. |
| 205 | final MediaStream videoStream = | 197 | final MediaStream videoStream = |
| 206 | await window.navigator.mediaDevices.getUserMedia(constraints).toDart; | 198 | await window.navigator.mediaDevices.getUserMedia(constraints).toDart; |
| 207 | 199 | ||
| 208 | - _permissionRequestInProgress = false; | ||
| 209 | - | ||
| 210 | return videoStream; | 200 | return videoStream; |
| 211 | } on DOMException catch (error, stackTrace) { | 201 | } on DOMException catch (error, stackTrace) { |
| 212 | - _permissionRequestInProgress = false; | ||
| 213 | final String errorMessage = error.toString(); | 202 | final String errorMessage = error.toString(); |
| 214 | 203 | ||
| 215 | MobileScannerErrorCode errorCode = MobileScannerErrorCode.genericError; | 204 | MobileScannerErrorCode errorCode = MobileScannerErrorCode.genericError; |
| @@ -272,11 +261,13 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -272,11 +261,13 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 272 | 261 | ||
| 273 | @override | 262 | @override |
| 274 | Future<MobileScannerViewAttributes> start(StartOptions startOptions) async { | 263 | Future<MobileScannerViewAttributes> start(StartOptions startOptions) async { |
| 275 | - // If the permission request has not yet completed, | ||
| 276 | - // the camera view is not ready yet. | ||
| 277 | - // Prevent the permission popup from triggering a restart of the scanner. | ||
| 278 | - if (_permissionRequestInProgress) { | ||
| 279 | - throw PermissionRequestPendingException(); | 264 | + if (_barcodeReader != null) { |
| 265 | + throw const MobileScannerException( | ||
| 266 | + errorCode: MobileScannerErrorCode.controllerAlreadyInitialized, | ||
| 267 | + errorDetails: MobileScannerErrorDetails( | ||
| 268 | + message: 'The scanner was already started.', | ||
| 269 | + ), | ||
| 270 | + ); | ||
| 280 | } | 271 | } |
| 281 | 272 | ||
| 282 | _barcodeReader = ZXingBarcodeReader(); | 273 | _barcodeReader = ZXingBarcodeReader(); |
| @@ -285,16 +276,6 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -285,16 +276,6 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 285 | alternateScriptUrl: _alternateScriptUrl, | 276 | alternateScriptUrl: _alternateScriptUrl, |
| 286 | ); | 277 | ); |
| 287 | 278 | ||
| 288 | - if (_barcodeReader?.isScanning ?? false) { | ||
| 289 | - throw const MobileScannerException( | ||
| 290 | - errorCode: MobileScannerErrorCode.controllerAlreadyInitialized, | ||
| 291 | - errorDetails: MobileScannerErrorDetails( | ||
| 292 | - message: | ||
| 293 | - 'The scanner was already started. Call stop() before calling start() again.', | ||
| 294 | - ), | ||
| 295 | - ); | ||
| 296 | - } | ||
| 297 | - | ||
| 298 | // Request camera permissions and prepare the video stream. | 279 | // Request camera permissions and prepare the video stream. |
| 299 | final MediaStream videoStream = await _prepareVideoStream( | 280 | final MediaStream videoStream = await _prepareVideoStream( |
| 300 | startOptions.cameraDirection, | 281 | startOptions.cameraDirection, |
| @@ -341,6 +322,15 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -341,6 +322,15 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 341 | 322 | ||
| 342 | _barcodesController.add(barcode); | 323 | _barcodesController.add(barcode); |
| 343 | }, | 324 | }, |
| 325 | + onError: (Object error) { | ||
| 326 | + if (_barcodesController.isClosed) { | ||
| 327 | + return; | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + _barcodesController.addError(error); | ||
| 331 | + }, | ||
| 332 | + // Errors are handled gracefully by forwarding them. | ||
| 333 | + cancelOnError: false, | ||
| 344 | ); | 334 | ); |
| 345 | 335 | ||
| 346 | final bool hasTorch = await _barcodeReader?.hasTorch() ?? false; | 336 | final bool hasTorch = await _barcodeReader?.hasTorch() ?? false; |
| @@ -2,7 +2,9 @@ import 'dart:async'; | @@ -2,7 +2,9 @@ import 'dart:async'; | ||
| 2 | import 'dart:js_interop'; | 2 | import 'dart:js_interop'; |
| 3 | import 'dart:ui'; | 3 | import 'dart:ui'; |
| 4 | 4 | ||
| 5 | +import 'package:flutter/foundation.dart'; | ||
| 5 | import 'package:mobile_scanner/src/enums/barcode_format.dart'; | 6 | import 'package:mobile_scanner/src/enums/barcode_format.dart'; |
| 7 | +import 'package:mobile_scanner/src/mobile_scanner_exception.dart'; | ||
| 6 | import 'package:mobile_scanner/src/objects/barcode_capture.dart'; | 8 | import 'package:mobile_scanner/src/objects/barcode_capture.dart'; |
| 7 | import 'package:mobile_scanner/src/objects/start_options.dart'; | 9 | import 'package:mobile_scanner/src/objects/start_options.dart'; |
| 8 | import 'package:mobile_scanner/src/web/barcode_reader.dart'; | 10 | import 'package:mobile_scanner/src/web/barcode_reader.dart'; |
| @@ -10,12 +12,18 @@ import 'package:mobile_scanner/src/web/javascript_map.dart'; | @@ -10,12 +12,18 @@ import 'package:mobile_scanner/src/web/javascript_map.dart'; | ||
| 10 | import 'package:mobile_scanner/src/web/media_track_constraints_delegate.dart'; | 12 | import 'package:mobile_scanner/src/web/media_track_constraints_delegate.dart'; |
| 11 | import 'package:mobile_scanner/src/web/zxing/result.dart'; | 13 | import 'package:mobile_scanner/src/web/zxing/result.dart'; |
| 12 | import 'package:mobile_scanner/src/web/zxing/zxing_browser_multi_format_reader.dart'; | 14 | import 'package:mobile_scanner/src/web/zxing/zxing_browser_multi_format_reader.dart'; |
| 15 | +import 'package:mobile_scanner/src/web/zxing/zxing_exception.dart'; | ||
| 13 | import 'package:web/web.dart' as web; | 16 | import 'package:web/web.dart' as web; |
| 14 | 17 | ||
| 15 | /// A barcode reader implementation that uses the ZXing library. | 18 | /// A barcode reader implementation that uses the ZXing library. |
| 16 | final class ZXingBarcodeReader extends BarcodeReader { | 19 | final class ZXingBarcodeReader extends BarcodeReader { |
| 17 | ZXingBarcodeReader(); | 20 | ZXingBarcodeReader(); |
| 18 | 21 | ||
| 22 | + /// ZXing reports an error with this message if the code could not be detected. | ||
| 23 | + @visibleForTesting | ||
| 24 | + static const String kNoCodeDetectedErrorMessage = | ||
| 25 | + 'No MultiFormat Readers were able to detect the code.'; | ||
| 26 | + | ||
| 19 | /// The listener for media track settings changes. | 27 | /// The listener for media track settings changes. |
| 20 | void Function(web.MediaTrackSettings)? _onMediaTrackSettingsChanged; | 28 | void Function(web.MediaTrackSettings)? _onMediaTrackSettingsChanged; |
| 21 | 29 | ||
| @@ -98,16 +106,24 @@ final class ZXingBarcodeReader extends BarcodeReader { | @@ -98,16 +106,24 @@ final class ZXingBarcodeReader extends BarcodeReader { | ||
| 98 | _reader?.decodeContinuously.callAsFunction( | 106 | _reader?.decodeContinuously.callAsFunction( |
| 99 | _reader, | 107 | _reader, |
| 100 | _reader?.videoElement, | 108 | _reader?.videoElement, |
| 101 | - (Result? result, JSAny? error) { | ||
| 102 | - if (controller.isClosed || result == null) { | 109 | + (Result? result, ZXingException? error) { |
| 110 | + if (controller.isClosed) { | ||
| 103 | return; | 111 | return; |
| 104 | } | 112 | } |
| 105 | 113 | ||
| 106 | - controller.add( | ||
| 107 | - BarcodeCapture( | ||
| 108 | - barcodes: [result.toBarcode], | ||
| 109 | - ), | ||
| 110 | - ); | 114 | + // Skip the event if no code was detected. |
| 115 | + if (error != null && error.message != kNoCodeDetectedErrorMessage) { | ||
| 116 | + controller.addError(MobileScannerBarcodeException(error.message)); | ||
| 117 | + return; | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + if (result != null) { | ||
| 121 | + controller.add( | ||
| 122 | + BarcodeCapture( | ||
| 123 | + barcodes: [result.toBarcode], | ||
| 124 | + ), | ||
| 125 | + ); | ||
| 126 | + } | ||
| 111 | }.toJS, | 127 | }.toJS, |
| 112 | ); | 128 | ); |
| 113 | }; | 129 | }; |
lib/src/web/zxing/zxing_exception.dart
0 → 100644
| 1 | +import 'dart:js_interop'; | ||
| 2 | + | ||
| 3 | +/// The JS static interop class for the Result class in the ZXing library. | ||
| 4 | +/// | ||
| 5 | +/// See also: https://github.com/zxing-js/library/blob/master/src/core/Exception.ts | ||
| 6 | +@JS('ZXing.Exception') | ||
| 7 | +extension type ZXingException._(JSObject _) implements JSObject { | ||
| 8 | + /// The error message of the exception, if any. | ||
| 9 | + external String? get message; | ||
| 10 | +} |
| @@ -14,7 +14,7 @@ struct MobileScannerErrorCodes { | @@ -14,7 +14,7 @@ struct MobileScannerErrorCodes { | ||
| 14 | // because it uses the error message from the undelying error. | 14 | // because it uses the error message from the undelying error. |
| 15 | static let BARCODE_ERROR = "MOBILE_SCANNER_BARCODE_ERROR" | 15 | static let BARCODE_ERROR = "MOBILE_SCANNER_BARCODE_ERROR" |
| 16 | // The error code 'CAMERA_ERROR' does not have an error message, | 16 | // The error code 'CAMERA_ERROR' does not have an error message, |
| 17 | - // because it uses the error message from the underlying error. | 17 | + // because it uses the error message from the underlying error. |
| 18 | static let CAMERA_ERROR = "MOBILE_SCANNER_CAMERA_ERROR" | 18 | static let CAMERA_ERROR = "MOBILE_SCANNER_CAMERA_ERROR" |
| 19 | static let NO_CAMERA_ERROR = "MOBILE_SCANNER_NO_CAMERA_ERROR" | 19 | static let NO_CAMERA_ERROR = "MOBILE_SCANNER_NO_CAMERA_ERROR" |
| 20 | static let NO_CAMERA_ERROR_MESSAGE = "No cameras available." | 20 | static let NO_CAMERA_ERROR_MESSAGE = "No cameras available." |
| @@ -131,7 +131,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -131,7 +131,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 131 | 131 | ||
| 132 | if error != nil { | 132 | if error != nil { |
| 133 | DispatchQueue.main.async { | 133 | DispatchQueue.main.async { |
| 134 | - self?.sink?(FlutterError(code: "MobileScanner", message: error?.localizedDescription, details: nil)) | 134 | + self?.sink?(FlutterError( |
| 135 | + code: MobileScannerErrorCodes.BARCODE_ERROR, | ||
| 136 | + message: error?.localizedDescription, details: nil)) | ||
| 135 | } | 137 | } |
| 136 | return | 138 | return |
| 137 | } | 139 | } |
| @@ -180,9 +182,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -180,9 +182,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 180 | } | 182 | } |
| 181 | 183 | ||
| 182 | try imageRequestHandler.perform([barcodeRequest]) | 184 | try imageRequestHandler.perform([barcodeRequest]) |
| 183 | - } catch let e { | 185 | + } catch let error { |
| 184 | DispatchQueue.main.async { | 186 | DispatchQueue.main.async { |
| 185 | - self?.sink?(FlutterError(code: "MobileScanner", message: e.localizedDescription, details: nil)) | 187 | + self?.sink?(FlutterError( |
| 188 | + code: MobileScannerErrorCodes.BARCODE_ERROR, | ||
| 189 | + message: error.localizedDescription, details: nil)) | ||
| 186 | } | 190 | } |
| 187 | } | 191 | } |
| 188 | } | 192 | } |
| @@ -262,8 +266,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -262,8 +266,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 262 | 266 | ||
| 263 | func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 267 | func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 264 | if (device != nil || captureSession != nil) { | 268 | if (device != nil || captureSession != nil) { |
| 265 | - result(FlutterError(code: "MobileScanner", | ||
| 266 | - message: "Called start() while already started!", | 269 | + result(FlutterError(code: MobileScannerErrorCodes.ALREADY_STARTED_ERROR, |
| 270 | + message: MobileScannerErrorCodes.ALREADY_STARTED_ERROR_MESSAGE, | ||
| 267 | details: nil)) | 271 | details: nil)) |
| 268 | return | 272 | return |
| 269 | } | 273 | } |
| @@ -294,8 +298,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -294,8 +298,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 294 | } | 298 | } |
| 295 | 299 | ||
| 296 | if (device == nil) { | 300 | if (device == nil) { |
| 297 | - result(FlutterError(code: "MobileScanner", | ||
| 298 | - message: "No camera found or failed to open camera!", | 301 | + result(FlutterError(code: MobileScannerErrorCodes.NO_CAMERA_ERROR, |
| 302 | + message: MobileScannerErrorCodes.NO_CAMERA_ERROR_MESSAGE, | ||
| 299 | details: nil)) | 303 | details: nil)) |
| 300 | return | 304 | return |
| 301 | } | 305 | } |
| @@ -313,7 +317,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -313,7 +317,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 313 | let input = try AVCaptureDeviceInput(device: device) | 317 | let input = try AVCaptureDeviceInput(device: device) |
| 314 | captureSession!.addInput(input) | 318 | captureSession!.addInput(input) |
| 315 | } catch { | 319 | } catch { |
| 316 | - result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil)) | 320 | + result(FlutterError( |
| 321 | + code: MobileScannerErrorCodes.CAMERA_ERROR, | ||
| 322 | + message: error.localizedDescription, details: nil)) | ||
| 317 | return | 323 | return |
| 318 | } | 324 | } |
| 319 | captureSession!.sessionPreset = AVCaptureSession.Preset.photo | 325 | captureSession!.sessionPreset = AVCaptureSession.Preset.photo |
| @@ -476,8 +482,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -476,8 +482,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 476 | 482 | ||
| 477 | if error != nil { | 483 | if error != nil { |
| 478 | DispatchQueue.main.async { | 484 | DispatchQueue.main.async { |
| 479 | - // TODO: fix error code | ||
| 480 | - result(FlutterError(code: "MobileScanner", message: error?.localizedDescription, details: nil)) | 485 | + result(FlutterError( |
| 486 | + code: MobileScannerErrorCodes.BARCODE_ERROR, | ||
| 487 | + message: error?.localizedDescription, details: nil)) | ||
| 481 | } | 488 | } |
| 482 | return | 489 | return |
| 483 | } | 490 | } |
| @@ -502,10 +509,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -502,10 +509,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 502 | } | 509 | } |
| 503 | 510 | ||
| 504 | try imageRequestHandler.perform([barcodeRequest]) | 511 | try imageRequestHandler.perform([barcodeRequest]) |
| 505 | - } catch let e { | ||
| 506 | - // TODO: fix error code | 512 | + } catch let error { |
| 507 | DispatchQueue.main.async { | 513 | DispatchQueue.main.async { |
| 508 | - result(FlutterError(code: "MobileScanner", message: e.localizedDescription, details: nil)) | 514 | + result(FlutterError( |
| 515 | + code: MobileScannerErrorCodes.BARCODE_ERROR, | ||
| 516 | + message: error.localizedDescription, details: nil)) | ||
| 509 | } | 517 | } |
| 510 | } | 518 | } |
| 511 | } | 519 | } |
-
Please register or login to post a comment