Committed by
GitHub
Merge pull request #1177 from navaronbracke/macos_extras
fix: support formats for analyze image
Showing
18 changed files
with
356 additions
and
146 deletions
| 1 | +## NEXT | ||
| 2 | + | ||
| 3 | +* [MacOS] Added the corners and size information to barcode results. | ||
| 4 | +* [MacOS] Added support for `analyzeImage`. | ||
| 5 | +* [web] Added the size information to barcode results. | ||
| 6 | +* Added support for barcode formats to image analysis. | ||
| 7 | + | ||
| 1 | ## 5.2.3 | 8 | ## 5.2.3 |
| 2 | 9 | ||
| 3 | Deprecations: | 10 | Deprecations: |
| @@ -35,8 +35,8 @@ See the example app for detailed implementation information. | @@ -35,8 +35,8 @@ See the example app for detailed implementation information. | ||
| 35 | 35 | ||
| 36 | | Features | Android | iOS | macOS | Web | | 36 | | Features | Android | iOS | macOS | Web | |
| 37 | |------------------------|--------------------|--------------------|----------------------|-----| | 37 | |------------------------|--------------------|--------------------|----------------------|-----| |
| 38 | -| analyzeImage (Gallery) | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | | ||
| 39 | -| returnImage | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | | 38 | +| analyzeImage (Gallery) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | |
| 39 | +| returnImage | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | | ||
| 40 | | scanWindow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | | 40 | | scanWindow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | |
| 41 | 41 | ||
| 42 | ## Platform Support | 42 | ## Platform Support |
| @@ -83,8 +83,8 @@ Ensure that you granted camera permission in XCode -> Signing & Capabilities: | @@ -83,8 +83,8 @@ Ensure that you granted camera permission in XCode -> Signing & Capabilities: | ||
| 83 | 83 | ||
| 84 | ## Web | 84 | ## Web |
| 85 | 85 | ||
| 86 | -As of version 5.0.0 adding the library to the `index.html` is no longer required, | ||
| 87 | -as the library is automatically loaded on first use. | 86 | +As of version 5.0.0 adding the barcode scanning library script to the `index.html` is no longer required, |
| 87 | +as the script is automatically loaded on first use. | ||
| 88 | 88 | ||
| 89 | ### Providing a mirror for the barcode scanning library | 89 | ### Providing a mirror for the barcode scanning library |
| 90 | 90 |
| @@ -67,7 +67,7 @@ dependencies { | @@ -67,7 +67,7 @@ dependencies { | ||
| 67 | def useUnbundled = project.findProperty('dev.steenbakker.mobile_scanner.useUnbundled') ?: false | 67 | def useUnbundled = project.findProperty('dev.steenbakker.mobile_scanner.useUnbundled') ?: false |
| 68 | if (useUnbundled.toBoolean()) { | 68 | if (useUnbundled.toBoolean()) { |
| 69 | // Dynamically downloaded model via Google Play Services | 69 | // Dynamically downloaded model via Google Play Services |
| 70 | - implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.0' | 70 | + implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1' |
| 71 | } else { | 71 | } else { |
| 72 | // Bundled model in app | 72 | // Bundled model in app |
| 73 | implementation 'com.google.mlkit:barcode-scanning:17.2.0' | 73 | implementation 'com.google.mlkit:barcode-scanning:17.2.0' |
| @@ -77,8 +77,8 @@ dependencies { | @@ -77,8 +77,8 @@ dependencies { | ||
| 77 | // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 | 77 | // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 |
| 78 | implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22")) | 78 | implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22")) |
| 79 | 79 | ||
| 80 | - implementation 'androidx.camera:camera-lifecycle:1.3.3' | ||
| 81 | - implementation 'androidx.camera:camera-camera2:1.3.3' | 80 | + implementation 'androidx.camera:camera-lifecycle:1.3.4' |
| 81 | + implementation 'androidx.camera:camera-camera2:1.3.4' | ||
| 82 | 82 | ||
| 83 | testImplementation 'org.jetbrains.kotlin:kotlin-test' | 83 | testImplementation 'org.jetbrains.kotlin:kotlin-test' |
| 84 | testImplementation 'org.mockito:mockito-core:5.12.0' | 84 | testImplementation 'org.mockito:mockito-core:5.12.0' |
| @@ -151,28 +151,16 @@ class MobileScannerHandler( | @@ -151,28 +151,16 @@ class MobileScannerHandler( | ||
| 151 | null | 151 | null |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | - var barcodeScannerOptions: BarcodeScannerOptions? = null | ||
| 155 | - if (formats != null) { | ||
| 156 | - val formatsList: MutableList<Int> = mutableListOf() | ||
| 157 | - for (formatValue in formats) { | ||
| 158 | - formatsList.add(BarcodeFormats.fromRawValue(formatValue).intValue) | ||
| 159 | - } | ||
| 160 | - barcodeScannerOptions = if (formatsList.size == 1) { | ||
| 161 | - BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first()) | ||
| 162 | - .build() | ||
| 163 | - } else { | ||
| 164 | - BarcodeScannerOptions.Builder().setBarcodeFormats( | ||
| 165 | - formatsList.first(), | ||
| 166 | - *formatsList.subList(1, formatsList.size).toIntArray() | ||
| 167 | - ).build() | ||
| 168 | - } | ||
| 169 | - } | 154 | + val barcodeScannerOptions: BarcodeScannerOptions? = buildBarcodeScannerOptions(formats) |
| 170 | 155 | ||
| 171 | val position = | 156 | val position = |
| 172 | if (facing == 0) CameraSelector.DEFAULT_FRONT_CAMERA else CameraSelector.DEFAULT_BACK_CAMERA | 157 | if (facing == 0) CameraSelector.DEFAULT_FRONT_CAMERA else CameraSelector.DEFAULT_BACK_CAMERA |
| 173 | 158 | ||
| 174 | - val detectionSpeed: DetectionSpeed = if (speed == 0) DetectionSpeed.NO_DUPLICATES | ||
| 175 | - else if (speed ==1) DetectionSpeed.NORMAL else DetectionSpeed.UNRESTRICTED | 159 | + val detectionSpeed: DetectionSpeed = when (speed) { |
| 160 | + 0 -> DetectionSpeed.NO_DUPLICATES | ||
| 161 | + 1 -> DetectionSpeed.NORMAL | ||
| 162 | + else -> DetectionSpeed.UNRESTRICTED | ||
| 163 | + } | ||
| 176 | 164 | ||
| 177 | mobileScanner!!.start( | 165 | mobileScanner!!.start( |
| 178 | barcodeScannerOptions, | 166 | barcodeScannerOptions, |
| @@ -243,13 +231,13 @@ class MobileScannerHandler( | @@ -243,13 +231,13 @@ class MobileScannerHandler( | ||
| 243 | 231 | ||
| 244 | private fun analyzeImage(call: MethodCall, result: MethodChannel.Result) { | 232 | private fun analyzeImage(call: MethodCall, result: MethodChannel.Result) { |
| 245 | analyzerResult = result | 233 | analyzerResult = result |
| 246 | - val uri = Uri.fromFile(File(call.arguments.toString())) | ||
| 247 | 234 | ||
| 248 | - // TODO: parse options from the method call | ||
| 249 | - // See https://github.com/juliansteenbakker/mobile_scanner/issues/1069 | 235 | + val formats: List<Int>? = call.argument<List<Int>>("formats") |
| 236 | + val filePath: String = call.argument<String>("filePath")!! | ||
| 237 | + | ||
| 250 | mobileScanner!!.analyzeImage( | 238 | mobileScanner!!.analyzeImage( |
| 251 | - uri, | ||
| 252 | - null, | 239 | + Uri.fromFile(File(filePath)), |
| 240 | + buildBarcodeScannerOptions(formats), | ||
| 253 | analyzeImageSuccessCallback, | 241 | analyzeImageSuccessCallback, |
| 254 | analyzeImageErrorCallback) | 242 | analyzeImageErrorCallback) |
| 255 | } | 243 | } |
| @@ -284,4 +272,26 @@ class MobileScannerHandler( | @@ -284,4 +272,26 @@ class MobileScannerHandler( | ||
| 284 | 272 | ||
| 285 | result.success(null) | 273 | result.success(null) |
| 286 | } | 274 | } |
| 275 | + | ||
| 276 | + private fun buildBarcodeScannerOptions(formats: List<Int>?): BarcodeScannerOptions? { | ||
| 277 | + if (formats == null) { | ||
| 278 | + return null | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + val formatsList: MutableList<Int> = mutableListOf() | ||
| 282 | + | ||
| 283 | + for (formatValue in formats) { | ||
| 284 | + formatsList.add(BarcodeFormats.fromRawValue(formatValue).intValue) | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + if (formatsList.size == 1) { | ||
| 288 | + return BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first()) | ||
| 289 | + .build() | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + return BarcodeScannerOptions.Builder().setBarcodeFormats( | ||
| 293 | + formatsList.first(), | ||
| 294 | + *formatsList.subList(1, formatsList.size).toIntArray() | ||
| 295 | + ).build() | ||
| 296 | + } | ||
| 287 | } | 297 | } |
| 1 | +import 'package:flutter/foundation.dart'; | ||
| 2 | +import 'package:flutter/material.dart'; | ||
| 3 | +import 'package:image_picker/image_picker.dart'; | ||
| 4 | +import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 5 | + | ||
| 6 | +class BarcodeScannerAnalyzeImage extends StatefulWidget { | ||
| 7 | + const BarcodeScannerAnalyzeImage({super.key}); | ||
| 8 | + | ||
| 9 | + @override | ||
| 10 | + State<BarcodeScannerAnalyzeImage> createState() => | ||
| 11 | + _BarcodeScannerAnalyzeImageState(); | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +class _BarcodeScannerAnalyzeImageState | ||
| 15 | + extends State<BarcodeScannerAnalyzeImage> { | ||
| 16 | + final MobileScannerController _controller = MobileScannerController(); | ||
| 17 | + | ||
| 18 | + BarcodeCapture? _barcodeCapture; | ||
| 19 | + | ||
| 20 | + Future<void> _analyzeImageFromFile() async { | ||
| 21 | + try { | ||
| 22 | + final XFile? file = | ||
| 23 | + await ImagePicker().pickImage(source: ImageSource.gallery); | ||
| 24 | + | ||
| 25 | + if (!mounted || file == null) { | ||
| 26 | + return; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + final BarcodeCapture? barcodeCapture = | ||
| 30 | + await _controller.analyzeImage(file.path); | ||
| 31 | + | ||
| 32 | + if (mounted) { | ||
| 33 | + setState(() { | ||
| 34 | + _barcodeCapture = barcodeCapture; | ||
| 35 | + }); | ||
| 36 | + } | ||
| 37 | + } catch (_) {} | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @override | ||
| 41 | + Widget build(BuildContext context) { | ||
| 42 | + Widget label = const Text('Pick a file to detect barcode'); | ||
| 43 | + | ||
| 44 | + if (_barcodeCapture != null) { | ||
| 45 | + label = Text( | ||
| 46 | + _barcodeCapture?.barcodes.firstOrNull?.displayValue ?? | ||
| 47 | + 'No barcode detected', | ||
| 48 | + ); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + return Scaffold( | ||
| 52 | + appBar: AppBar(title: const Text('Analyze image from file')), | ||
| 53 | + body: Column( | ||
| 54 | + children: [ | ||
| 55 | + Expanded( | ||
| 56 | + child: Center( | ||
| 57 | + child: ElevatedButton( | ||
| 58 | + onPressed: kIsWeb ? null : _analyzeImageFromFile, | ||
| 59 | + child: kIsWeb | ||
| 60 | + ? const Text('Analyze image is not supported on web') | ||
| 61 | + : const Text('Choose file'), | ||
| 62 | + ), | ||
| 63 | + ), | ||
| 64 | + ), | ||
| 65 | + Expanded(child: Center(child: label)), | ||
| 66 | + ], | ||
| 67 | + ), | ||
| 68 | + ); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + @override | ||
| 72 | + void dispose() { | ||
| 73 | + _controller.dispose(); | ||
| 74 | + super.dispose(); | ||
| 75 | + } | ||
| 76 | +} |
| @@ -39,16 +39,16 @@ class _BarcodeScannerWithScanWindowState | @@ -39,16 +39,16 @@ class _BarcodeScannerWithScanWindowState | ||
| 39 | final scannedBarcode = barcodeCapture.barcodes.first; | 39 | final scannedBarcode = barcodeCapture.barcodes.first; |
| 40 | 40 | ||
| 41 | // No barcode corners, or size, or no camera preview size. | 41 | // No barcode corners, or size, or no camera preview size. |
| 42 | - if (scannedBarcode.corners.isEmpty || | ||
| 43 | - value.size.isEmpty || | ||
| 44 | - barcodeCapture.size.isEmpty) { | 42 | + if (value.size.isEmpty || |
| 43 | + scannedBarcode.size.isEmpty || | ||
| 44 | + scannedBarcode.corners.isEmpty) { | ||
| 45 | return const SizedBox(); | 45 | return const SizedBox(); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | return CustomPaint( | 48 | return CustomPaint( |
| 49 | painter: BarcodeOverlay( | 49 | painter: BarcodeOverlay( |
| 50 | barcodeCorners: scannedBarcode.corners, | 50 | barcodeCorners: scannedBarcode.corners, |
| 51 | - barcodeSize: barcodeCapture.size, | 51 | + barcodeSize: scannedBarcode.size, |
| 52 | boxFit: BoxFit.contain, | 52 | boxFit: BoxFit.contain, |
| 53 | cameraPreviewSize: value.size, | 53 | cameraPreviewSize: value.size, |
| 54 | ), | 54 | ), |
| 1 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
| 2 | +import 'package:mobile_scanner_example/barcode_scanner_analyze_image.dart'; | ||
| 2 | import 'package:mobile_scanner_example/barcode_scanner_controller.dart'; | 3 | import 'package:mobile_scanner_example/barcode_scanner_controller.dart'; |
| 3 | import 'package:mobile_scanner_example/barcode_scanner_listview.dart'; | 4 | import 'package:mobile_scanner_example/barcode_scanner_listview.dart'; |
| 4 | import 'package:mobile_scanner_example/barcode_scanner_pageview.dart'; | 5 | import 'package:mobile_scanner_example/barcode_scanner_pageview.dart'; |
| @@ -20,95 +21,75 @@ void main() { | @@ -20,95 +21,75 @@ void main() { | ||
| 20 | class MyHome extends StatelessWidget { | 21 | class MyHome extends StatelessWidget { |
| 21 | const MyHome({super.key}); | 22 | const MyHome({super.key}); |
| 22 | 23 | ||
| 23 | - @override | ||
| 24 | - Widget build(BuildContext context) { | ||
| 25 | - return Scaffold( | ||
| 26 | - appBar: AppBar(title: const Text('Mobile Scanner Example')), | ||
| 27 | - body: Center( | ||
| 28 | - child: Column( | ||
| 29 | - mainAxisAlignment: MainAxisAlignment.spaceAround, | ||
| 30 | - children: [ | ||
| 31 | - ElevatedButton( | ||
| 32 | - onPressed: () { | ||
| 33 | - Navigator.of(context).push( | ||
| 34 | - MaterialPageRoute( | ||
| 35 | - builder: (context) => const BarcodeScannerSimple(), | ||
| 36 | - ), | ||
| 37 | - ); | ||
| 38 | - }, | ||
| 39 | - child: const Text('MobileScanner Simple'), | ||
| 40 | - ), | ||
| 41 | - ElevatedButton( | 24 | + Widget _buildItem(BuildContext context, String label, Widget page) { |
| 25 | + return Padding( | ||
| 26 | + padding: const EdgeInsets.all(8.0), | ||
| 27 | + child: Center( | ||
| 28 | + child: ElevatedButton( | ||
| 42 | onPressed: () { | 29 | onPressed: () { |
| 43 | Navigator.of(context).push( | 30 | Navigator.of(context).push( |
| 44 | MaterialPageRoute( | 31 | MaterialPageRoute( |
| 45 | - builder: (context) => const BarcodeScannerListView(), | 32 | + builder: (context) => page, |
| 46 | ), | 33 | ), |
| 47 | ); | 34 | ); |
| 48 | }, | 35 | }, |
| 49 | - child: const Text('MobileScanner with ListView'), | 36 | + child: Text(label), |
| 50 | ), | 37 | ), |
| 51 | - ElevatedButton( | ||
| 52 | - onPressed: () { | ||
| 53 | - Navigator.of(context).push( | ||
| 54 | - MaterialPageRoute( | ||
| 55 | - builder: (context) => const BarcodeScannerWithController(), | ||
| 56 | ), | 38 | ), |
| 57 | ); | 39 | ); |
| 58 | - }, | ||
| 59 | - child: const Text('MobileScanner with Controller'), | ||
| 60 | - ), | ||
| 61 | - ElevatedButton( | ||
| 62 | - onPressed: () { | ||
| 63 | - Navigator.of(context).push( | ||
| 64 | - MaterialPageRoute( | ||
| 65 | - builder: (context) => const BarcodeScannerWithScanWindow(), | ||
| 66 | - ), | ||
| 67 | - ); | ||
| 68 | - }, | ||
| 69 | - child: const Text('MobileScanner with ScanWindow'), | ||
| 70 | - ), | ||
| 71 | - ElevatedButton( | ||
| 72 | - onPressed: () { | ||
| 73 | - Navigator.of(context).push( | ||
| 74 | - MaterialPageRoute( | ||
| 75 | - builder: (context) => const BarcodeScannerReturningImage(), | ||
| 76 | - ), | ||
| 77 | - ); | ||
| 78 | - }, | ||
| 79 | - child: const Text( | ||
| 80 | - 'MobileScanner with Controller (returning image)', | ||
| 81 | - ), | ||
| 82 | - ), | ||
| 83 | - ElevatedButton( | ||
| 84 | - onPressed: () { | ||
| 85 | - Navigator.of(context).push( | ||
| 86 | - MaterialPageRoute( | ||
| 87 | - builder: (context) => const BarcodeScannerWithZoom(), | ||
| 88 | - ), | ||
| 89 | - ); | ||
| 90 | - }, | ||
| 91 | - child: const Text('MobileScanner with zoom slider'), | ||
| 92 | - ), | ||
| 93 | - ElevatedButton( | ||
| 94 | - onPressed: () { | ||
| 95 | - Navigator.of(context).push( | ||
| 96 | - MaterialPageRoute( | ||
| 97 | - builder: (context) => const BarcodeScannerPageView(), | ||
| 98 | - ), | ||
| 99 | - ); | ||
| 100 | - }, | ||
| 101 | - child: const Text('MobileScanner pageView'), | ||
| 102 | - ), | ||
| 103 | - ElevatedButton( | ||
| 104 | - onPressed: () { | ||
| 105 | - Navigator.of(context).push( | ||
| 106 | - MaterialPageRoute( | ||
| 107 | - builder: (context) => BarcodeScannerWithOverlay(), | ||
| 108 | - ), | ||
| 109 | - ); | ||
| 110 | - }, | ||
| 111 | - child: const Text('MobileScanner with Overlay'), | 40 | + } |
| 41 | + | ||
| 42 | + @override | ||
| 43 | + Widget build(BuildContext context) { | ||
| 44 | + return Scaffold( | ||
| 45 | + appBar: AppBar(title: const Text('Mobile Scanner Example')), | ||
| 46 | + body: Center( | ||
| 47 | + child: ListView( | ||
| 48 | + children: [ | ||
| 49 | + _buildItem( | ||
| 50 | + context, | ||
| 51 | + 'MobileScanner Simple', | ||
| 52 | + const BarcodeScannerSimple(), | ||
| 53 | + ), | ||
| 54 | + _buildItem( | ||
| 55 | + context, | ||
| 56 | + 'MobileScanner with ListView', | ||
| 57 | + const BarcodeScannerListView(), | ||
| 58 | + ), | ||
| 59 | + _buildItem( | ||
| 60 | + context, | ||
| 61 | + 'MobileScanner with Controller', | ||
| 62 | + const BarcodeScannerWithController(), | ||
| 63 | + ), | ||
| 64 | + _buildItem( | ||
| 65 | + context, | ||
| 66 | + 'MobileScanner with ScanWindow', | ||
| 67 | + const BarcodeScannerWithScanWindow(), | ||
| 68 | + ), | ||
| 69 | + _buildItem( | ||
| 70 | + context, | ||
| 71 | + 'MobileScanner with Controller (return image)', | ||
| 72 | + const BarcodeScannerReturningImage(), | ||
| 73 | + ), | ||
| 74 | + _buildItem( | ||
| 75 | + context, | ||
| 76 | + 'MobileScanner with zoom slider', | ||
| 77 | + const BarcodeScannerWithZoom(), | ||
| 78 | + ), | ||
| 79 | + _buildItem( | ||
| 80 | + context, | ||
| 81 | + 'MobileScanner with PageView', | ||
| 82 | + const BarcodeScannerPageView(), | ||
| 83 | + ), | ||
| 84 | + _buildItem( | ||
| 85 | + context, | ||
| 86 | + 'MobileScanner with Overlay', | ||
| 87 | + const BarcodeScannerWithOverlay(), | ||
| 88 | + ), | ||
| 89 | + _buildItem( | ||
| 90 | + context, | ||
| 91 | + 'Analyze image from file', | ||
| 92 | + const BarcodeScannerAnalyzeImage(), | ||
| 112 | ), | 93 | ), |
| 113 | ], | 94 | ], |
| 114 | ), | 95 | ), |
| @@ -5,6 +5,8 @@ import 'package:mobile_scanner_example/scanner_button_widgets.dart'; | @@ -5,6 +5,8 @@ import 'package:mobile_scanner_example/scanner_button_widgets.dart'; | ||
| 5 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 5 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 6 | 6 | ||
| 7 | class BarcodeScannerWithOverlay extends StatefulWidget { | 7 | class BarcodeScannerWithOverlay extends StatefulWidget { |
| 8 | + const BarcodeScannerWithOverlay({super.key}); | ||
| 9 | + | ||
| 8 | @override | 10 | @override |
| 9 | _BarcodeScannerWithOverlayState createState() => | 11 | _BarcodeScannerWithOverlayState createState() => |
| 10 | _BarcodeScannerWithOverlayState(); | 12 | _BarcodeScannerWithOverlayState(); |
| @@ -22,8 +22,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -22,8 +22,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 22 | /// The selected camera | 22 | /// The selected camera |
| 23 | var device: AVCaptureDevice! | 23 | var device: AVCaptureDevice! |
| 24 | 24 | ||
| 25 | - /// Barcode scanner for results | ||
| 26 | - var scanner = BarcodeScanner.barcodeScanner() | 25 | + /// The long lived barcode scanner for scanning barcodes from a camera preview. |
| 26 | + var scanner: BarcodeScanner? = nil | ||
| 27 | 27 | ||
| 28 | /// Default position of camera | 28 | /// Default position of camera |
| 29 | var videoPosition: AVCaptureDevice.Position = AVCaptureDevice.Position.back | 29 | var videoPosition: AVCaptureDevice.Position = AVCaptureDevice.Position.back |
| @@ -146,7 +146,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -146,7 +146,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 146 | position: videoPosition | 146 | position: videoPosition |
| 147 | ) | 147 | ) |
| 148 | 148 | ||
| 149 | - scanner.process(image) { [self] barcodes, error in | 149 | + scanner?.process(image) { [self] barcodes, error in |
| 150 | imagesCurrentlyBeingProcessed = false | 150 | imagesCurrentlyBeingProcessed = false |
| 151 | 151 | ||
| 152 | if (detectionSpeed == DetectionSpeed.noDuplicates) { | 152 | if (detectionSpeed == DetectionSpeed.noDuplicates) { |
| @@ -314,6 +314,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -314,6 +314,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 314 | textureId = nil | 314 | textureId = nil |
| 315 | captureSession = nil | 315 | captureSession = nil |
| 316 | device = nil | 316 | device = nil |
| 317 | + scanner = nil | ||
| 317 | } | 318 | } |
| 318 | 319 | ||
| 319 | /// Toggle the torch. | 320 | /// Toggle the torch. |
| @@ -431,7 +432,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -431,7 +432,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 431 | } | 432 | } |
| 432 | 433 | ||
| 433 | /// Analyze a single image | 434 | /// Analyze a single image |
| 434 | - func analyzeImage(image: UIImage, position: AVCaptureDevice.Position, callback: @escaping BarcodeScanningCallback) { | 435 | + func analyzeImage(image: UIImage, position: AVCaptureDevice.Position, |
| 436 | + barcodeScannerOptions: BarcodeScannerOptions?, callback: @escaping BarcodeScanningCallback) { | ||
| 435 | let image = VisionImage(image: image) | 437 | let image = VisionImage(image: image) |
| 436 | image.orientation = imageOrientation( | 438 | image.orientation = imageOrientation( |
| 437 | deviceOrientation: UIDevice.current.orientation, | 439 | deviceOrientation: UIDevice.current.orientation, |
| @@ -439,6 +441,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -439,6 +441,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 439 | position: position | 441 | position: position |
| 440 | ) | 442 | ) |
| 441 | 443 | ||
| 444 | + let scanner: BarcodeScanner = barcodeScannerOptions != nil ? BarcodeScanner.barcodeScanner(options: barcodeScannerOptions!) : BarcodeScanner.barcodeScanner() | ||
| 445 | + | ||
| 442 | scanner.process(image, completion: callback) | 446 | scanner.process(image, completion: callback) |
| 443 | } | 447 | } |
| 444 | 448 |
| @@ -134,16 +134,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -134,16 +134,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 134 | self.mobileScanner.timeoutSeconds = Double(timeoutMs) / Double(1000) | 134 | self.mobileScanner.timeoutSeconds = Double(timeoutMs) / Double(1000) |
| 135 | MobileScannerPlugin.returnImage = returnImage | 135 | MobileScannerPlugin.returnImage = returnImage |
| 136 | 136 | ||
| 137 | - let formatList = formats.map { format in return BarcodeFormat(rawValue: format)} | ||
| 138 | - var barcodeOptions: BarcodeScannerOptions? = nil | ||
| 139 | - | ||
| 140 | - if (formatList.count != 0) { | ||
| 141 | - var barcodeFormats: BarcodeFormat = [] | ||
| 142 | - for index in formats { | ||
| 143 | - barcodeFormats.insert(BarcodeFormat(rawValue: index)) | ||
| 144 | - } | ||
| 145 | - barcodeOptions = BarcodeScannerOptions(formats: barcodeFormats) | ||
| 146 | - } | 137 | + let barcodeOptions: BarcodeScannerOptions? = buildBarcodeScannerOptions(formats) |
| 147 | 138 | ||
| 148 | let position = facing == 0 ? AVCaptureDevice.Position.front : .back | 139 | let position = facing == 0 ? AVCaptureDevice.Position.front : .back |
| 149 | let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)! | 140 | let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)! |
| @@ -262,7 +253,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -262,7 +253,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 262 | 253 | ||
| 263 | /// Analyzes a single image. | 254 | /// Analyzes a single image. |
| 264 | private func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 255 | private func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 265 | - let uiImage = UIImage(contentsOfFile: call.arguments as? String ?? "") | 256 | + let formats: Array<Int> = (call.arguments as! Dictionary<String, Any?>)["formats"] as? Array ?? [] |
| 257 | + let scannerOptions: BarcodeScannerOptions? = buildBarcodeScannerOptions(formats) | ||
| 258 | + let uiImage = UIImage(contentsOfFile: (call.arguments as! Dictionary<String, Any?>)["filePath"] as? String ?? "") | ||
| 266 | 259 | ||
| 267 | if (uiImage == nil) { | 260 | if (uiImage == nil) { |
| 268 | result(FlutterError(code: "MobileScanner", | 261 | result(FlutterError(code: "MobileScanner", |
| @@ -271,7 +264,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -271,7 +264,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 271 | return | 264 | return |
| 272 | } | 265 | } |
| 273 | 266 | ||
| 274 | - mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back, callback: { barcodes, error in | 267 | + mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back, |
| 268 | + barcodeScannerOptions: scannerOptions, callback: { barcodes, error in | ||
| 275 | if error != nil { | 269 | if error != nil { |
| 276 | DispatchQueue.main.async { | 270 | DispatchQueue.main.async { |
| 277 | result(FlutterError(code: "MobileScanner", | 271 | result(FlutterError(code: "MobileScanner", |
| @@ -297,4 +291,18 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -297,4 +291,18 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 297 | } | 291 | } |
| 298 | }) | 292 | }) |
| 299 | } | 293 | } |
| 294 | + | ||
| 295 | + private func buildBarcodeScannerOptions(_ formats: [Int]) -> BarcodeScannerOptions? { | ||
| 296 | + guard !formats.isEmpty else { | ||
| 297 | + return nil | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + var barcodeFormats: BarcodeFormat = [] | ||
| 301 | + | ||
| 302 | + for format in formats { | ||
| 303 | + barcodeFormats.insert(BarcodeFormat(rawValue: format)) | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | + return BarcodeScannerOptions(formats: barcodeFormats) | ||
| 307 | + } | ||
| 300 | } | 308 | } |
| @@ -3,6 +3,7 @@ import 'dart:async'; | @@ -3,6 +3,7 @@ import 'dart:async'; | ||
| 3 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; |
| 4 | import 'package:flutter/services.dart'; | 4 | import 'package:flutter/services.dart'; |
| 5 | import 'package:flutter/widgets.dart'; | 5 | import 'package:flutter/widgets.dart'; |
| 6 | +import 'package:mobile_scanner/src/enums/barcode_format.dart'; | ||
| 6 | import 'package:mobile_scanner/src/enums/mobile_scanner_authorization_state.dart'; | 7 | import 'package:mobile_scanner/src/enums/mobile_scanner_authorization_state.dart'; |
| 7 | import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart'; | 8 | import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart'; |
| 8 | import 'package:mobile_scanner/src/enums/torch_state.dart'; | 9 | import 'package:mobile_scanner/src/enums/torch_state.dart'; |
| @@ -140,11 +141,22 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | @@ -140,11 +141,22 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | ||
| 140 | } | 141 | } |
| 141 | 142 | ||
| 142 | @override | 143 | @override |
| 143 | - Future<BarcodeCapture?> analyzeImage(String path) async { | 144 | + Future<BarcodeCapture?> analyzeImage( |
| 145 | + String path, { | ||
| 146 | + List<BarcodeFormat> formats = const <BarcodeFormat>[], | ||
| 147 | + }) async { | ||
| 144 | final Map<Object?, Object?>? result = | 148 | final Map<Object?, Object?>? result = |
| 145 | await methodChannel.invokeMapMethod<Object?, Object?>( | 149 | await methodChannel.invokeMapMethod<Object?, Object?>( |
| 146 | 'analyzeImage', | 150 | 'analyzeImage', |
| 147 | - path, | 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 | + }, | ||
| 148 | ); | 160 | ); |
| 149 | 161 | ||
| 150 | return _parseBarcode(result); | 162 | return _parseBarcode(result); |
| @@ -174,7 +174,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | @@ -174,7 +174,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | ||
| 174 | /// | 174 | /// |
| 175 | /// The [path] points to a file on the device. | 175 | /// The [path] points to a file on the device. |
| 176 | /// | 176 | /// |
| 177 | - /// This is only supported on Android and iOS. | 177 | + /// This is only supported on Android, iOS and MacOS. |
| 178 | /// | 178 | /// |
| 179 | /// Returns the [BarcodeCapture] that was found in the image. | 179 | /// Returns the [BarcodeCapture] that was found in the image. |
| 180 | Future<BarcodeCapture?> analyzeImage(String path) { | 180 | Future<BarcodeCapture?> analyzeImage(String path) { |
| 1 | import 'package:flutter/widgets.dart'; | 1 | import 'package:flutter/widgets.dart'; |
| 2 | +import 'package:mobile_scanner/src/enums/barcode_format.dart'; | ||
| 2 | import 'package:mobile_scanner/src/enums/torch_state.dart'; | 3 | import 'package:mobile_scanner/src/enums/torch_state.dart'; |
| 3 | import 'package:mobile_scanner/src/method_channel/mobile_scanner_method_channel.dart'; | 4 | import 'package:mobile_scanner/src/method_channel/mobile_scanner_method_channel.dart'; |
| 4 | import 'package:mobile_scanner/src/mobile_scanner_view_attributes.dart'; | 5 | import 'package:mobile_scanner/src/mobile_scanner_view_attributes.dart'; |
| @@ -46,9 +47,15 @@ abstract class MobileScannerPlatform extends PlatformInterface { | @@ -46,9 +47,15 @@ abstract class MobileScannerPlatform extends PlatformInterface { | ||
| 46 | /// Analyze a local image file for barcodes. | 47 | /// Analyze a local image file for barcodes. |
| 47 | /// | 48 | /// |
| 48 | /// The [path] is the path to the file on disk. | 49 | /// The [path] is the path to the file on disk. |
| 50 | + /// The [formats] specify the barcode formats that should be detected. | ||
| 51 | + /// | ||
| 52 | + /// If [formats] is empty, all barcode formats will be detected. | ||
| 49 | /// | 53 | /// |
| 50 | /// Returns the barcodes that were found in the image. | 54 | /// Returns the barcodes that were found in the image. |
| 51 | - Future<BarcodeCapture?> analyzeImage(String path) { | 55 | + Future<BarcodeCapture?> analyzeImage( |
| 56 | + String path, { | ||
| 57 | + List<BarcodeFormat> formats = const <BarcodeFormat>[], | ||
| 58 | + }) { | ||
| 52 | throw UnimplementedError('analyzeImage() has not been implemented.'); | 59 | throw UnimplementedError('analyzeImage() has not been implemented.'); |
| 53 | } | 60 | } |
| 54 | 61 |
| @@ -152,7 +152,7 @@ class Barcode { | @@ -152,7 +152,7 @@ class Barcode { | ||
| 152 | /// This is null if the raw value is not available. | 152 | /// This is null if the raw value is not available. |
| 153 | final String? rawValue; | 153 | final String? rawValue; |
| 154 | 154 | ||
| 155 | - /// The size of the barcode bounding box. | 155 | + /// The normalized size of the barcode bounding box. |
| 156 | /// | 156 | /// |
| 157 | /// If the bounding box is unavailable, this will be [Size.zero]. | 157 | /// If the bounding box is unavailable, this will be [Size.zero]. |
| 158 | final Size size; | 158 | final Size size; |
| @@ -4,6 +4,7 @@ import 'dart:ui_web' as ui_web; | @@ -4,6 +4,7 @@ import 'dart:ui_web' as ui_web; | ||
| 4 | 4 | ||
| 5 | import 'package:flutter/widgets.dart'; | 5 | import 'package:flutter/widgets.dart'; |
| 6 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; | 6 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; |
| 7 | +import 'package:mobile_scanner/src/enums/barcode_format.dart'; | ||
| 7 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; | 8 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; |
| 8 | import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart'; | 9 | import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart'; |
| 9 | import 'package:mobile_scanner/src/enums/torch_state.dart'; | 10 | import 'package:mobile_scanner/src/enums/torch_state.dart'; |
| @@ -232,7 +233,10 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -232,7 +233,10 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 232 | } | 233 | } |
| 233 | 234 | ||
| 234 | @override | 235 | @override |
| 235 | - Future<BarcodeCapture?> analyzeImage(String path) { | 236 | + Future<BarcodeCapture?> analyzeImage( |
| 237 | + String path, { | ||
| 238 | + List<BarcodeFormat> formats = const <BarcodeFormat>[], | ||
| 239 | + }) { | ||
| 236 | throw UnsupportedError('analyzeImage() is not supported on the web.'); | 240 | throw UnsupportedError('analyzeImage() is not supported on the web.'); |
| 237 | } | 241 | } |
| 238 | 242 |
| @@ -75,13 +75,33 @@ extension type Result(JSObject _) implements JSObject { | @@ -75,13 +75,33 @@ extension type Result(JSObject _) implements JSObject { | ||
| 75 | 75 | ||
| 76 | /// Convert this result to a [Barcode]. | 76 | /// Convert this result to a [Barcode]. |
| 77 | Barcode get toBarcode { | 77 | Barcode get toBarcode { |
| 78 | + final List<Offset> corners = resultPoints; | ||
| 79 | + | ||
| 78 | return Barcode( | 80 | return Barcode( |
| 79 | - corners: resultPoints, | 81 | + corners: corners, |
| 80 | format: barcodeFormat, | 82 | format: barcodeFormat, |
| 81 | displayValue: text, | 83 | displayValue: text, |
| 82 | rawBytes: rawBytes, | 84 | rawBytes: rawBytes, |
| 83 | rawValue: text, | 85 | rawValue: text, |
| 86 | + size: _computeSize(corners), | ||
| 84 | type: BarcodeType.text, | 87 | type: BarcodeType.text, |
| 85 | ); | 88 | ); |
| 86 | } | 89 | } |
| 90 | + | ||
| 91 | + Size _computeSize(List<Offset> points) { | ||
| 92 | + if (points.length != 4) { | ||
| 93 | + return Size.zero; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + final Iterable<double> xCoords = points.map((p) => p.dx); | ||
| 97 | + final Iterable<double> yCoords = points.map((p) => p.dy); | ||
| 98 | + | ||
| 99 | + // Find the minimum and maximum x and y coordinates. | ||
| 100 | + final double xMin = xCoords.reduce((a, b) => a < b ? a : b); | ||
| 101 | + final double xMax = xCoords.reduce((a, b) => a > b ? a : b); | ||
| 102 | + final double yMin = yCoords.reduce((a, b) => a < b ? a : b); | ||
| 103 | + final double yMax = yCoords.reduce((a, b) => a > b ? a : b); | ||
| 104 | + | ||
| 105 | + return Size(xMax - xMin, yMax - yMin); | ||
| 106 | + } | ||
| 87 | } | 107 | } |
| @@ -138,11 +138,10 @@ final class ZXingBarcodeReader extends BarcodeReader { | @@ -138,11 +138,10 @@ final class ZXingBarcodeReader extends BarcodeReader { | ||
| 138 | required web.MediaStream videoStream, | 138 | required web.MediaStream videoStream, |
| 139 | }) async { | 139 | }) async { |
| 140 | final int detectionTimeoutMs = options.detectionTimeoutMs; | 140 | final int detectionTimeoutMs = options.detectionTimeoutMs; |
| 141 | - final List<BarcodeFormat> formats = options.formats; | ||
| 142 | - | ||
| 143 | - if (formats.contains(BarcodeFormat.unknown)) { | ||
| 144 | - formats.removeWhere((element) => element == BarcodeFormat.unknown); | ||
| 145 | - } | 141 | + final List<BarcodeFormat> formats = [ |
| 142 | + for (final BarcodeFormat format in options.formats) | ||
| 143 | + if (format != BarcodeFormat.unknown) format, | ||
| 144 | + ]; | ||
| 146 | 145 | ||
| 147 | _reader = ZXingBrowserMultiFormatReader( | 146 | _reader = ZXingBrowserMultiFormatReader( |
| 148 | _createReaderHints(formats), | 147 | _createReaderHints(formats), |
| @@ -71,6 +71,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -71,6 +71,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 71 | stop(result) | 71 | stop(result) |
| 72 | case "updateScanWindow": | 72 | case "updateScanWindow": |
| 73 | updateScanWindow(call, result) | 73 | updateScanWindow(call, result) |
| 74 | + case "analyzeImage": | ||
| 75 | + analyzeImage(call, result) | ||
| 74 | default: | 76 | default: |
| 75 | result(FlutterMethodNotImplemented) | 77 | result(FlutterMethodNotImplemented) |
| 76 | } | 78 | } |
| @@ -124,7 +126,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -124,7 +126,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 124 | VTCreateCGImageFromCVPixelBuffer(self!.latestBuffer, options: nil, imageOut: &cgImage) | 126 | VTCreateCGImageFromCVPixelBuffer(self!.latestBuffer, options: nil, imageOut: &cgImage) |
| 125 | let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage!) | 127 | let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage!) |
| 126 | do { | 128 | do { |
| 127 | - let barcodeRequest:VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in | 129 | + let barcodeRequest: VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in |
| 128 | self?.imagesCurrentlyBeingProcessed = false | 130 | self?.imagesCurrentlyBeingProcessed = false |
| 129 | 131 | ||
| 130 | if error != nil { | 132 | if error != nil { |
| @@ -452,6 +454,70 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -452,6 +454,70 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 452 | result(nil) | 454 | result(nil) |
| 453 | } | 455 | } |
| 454 | 456 | ||
| 457 | + func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 458 | + let argReader = MapArgumentReader(call.arguments as? [String: Any]) | ||
| 459 | + let symbologies:[VNBarcodeSymbology] = argReader.toSymbology() | ||
| 460 | + | ||
| 461 | + guard let filePath: String = argReader.string(key: "filePath") else { | ||
| 462 | + // TODO: fix error code | ||
| 463 | + result(FlutterError(code: "MobileScanner", | ||
| 464 | + message: "No image found in analyzeImage!", | ||
| 465 | + details: nil)) | ||
| 466 | + return | ||
| 467 | + } | ||
| 468 | + | ||
| 469 | + let fileUrl = URL(fileURLWithPath: filePath) | ||
| 470 | + | ||
| 471 | + guard let ciImage = CIImage(contentsOf: fileUrl) else { | ||
| 472 | + // TODO: fix error code | ||
| 473 | + result(FlutterError(code: "MobileScanner", | ||
| 474 | + message: "No image found in analyzeImage!", | ||
| 475 | + details: nil)) | ||
| 476 | + return | ||
| 477 | + } | ||
| 478 | + | ||
| 479 | + let imageRequestHandler = VNImageRequestHandler(ciImage: ciImage, orientation: CGImagePropertyOrientation.up, options: [:]) | ||
| 480 | + | ||
| 481 | + do { | ||
| 482 | + let barcodeRequest: VNDetectBarcodesRequest = VNDetectBarcodesRequest( | ||
| 483 | + completionHandler: { [] (request, error) in | ||
| 484 | + | ||
| 485 | + if error != nil { | ||
| 486 | + DispatchQueue.main.async { | ||
| 487 | + // TODO: fix error code | ||
| 488 | + result(FlutterError(code: "MobileScanner", message: error?.localizedDescription, details: nil)) | ||
| 489 | + } | ||
| 490 | + return | ||
| 491 | + } | ||
| 492 | + | ||
| 493 | + guard let barcodes: [VNBarcodeObservation] = request.results as? [VNBarcodeObservation] else { | ||
| 494 | + return | ||
| 495 | + } | ||
| 496 | + | ||
| 497 | + if barcodes.isEmpty { | ||
| 498 | + return | ||
| 499 | + } | ||
| 500 | + | ||
| 501 | + result([ | ||
| 502 | + "name": "barcode", | ||
| 503 | + "data": barcodes.map({ $0.toMap() }), | ||
| 504 | + ]) | ||
| 505 | + }) | ||
| 506 | + | ||
| 507 | + if !symbologies.isEmpty { | ||
| 508 | + // Add the symbologies the user wishes to support. | ||
| 509 | + barcodeRequest.symbologies = symbologies | ||
| 510 | + } | ||
| 511 | + | ||
| 512 | + try imageRequestHandler.perform([barcodeRequest]) | ||
| 513 | + } catch let e { | ||
| 514 | + // TODO: fix error code | ||
| 515 | + DispatchQueue.main.async { | ||
| 516 | + result(FlutterError(code: "MobileScanner", message: e.localizedDescription, details: nil)) | ||
| 517 | + } | ||
| 518 | + } | ||
| 519 | + } | ||
| 520 | + | ||
| 455 | // Observer for torch state | 521 | // Observer for torch state |
| 456 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { | 522 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { |
| 457 | switch keyPath { | 523 | switch keyPath { |
| @@ -542,10 +608,24 @@ extension CGImage { | @@ -542,10 +608,24 @@ extension CGImage { | ||
| 542 | } | 608 | } |
| 543 | 609 | ||
| 544 | extension VNBarcodeObservation { | 610 | extension VNBarcodeObservation { |
| 611 | + private func distanceBetween(_ p1: CGPoint, _ p2: CGPoint) -> CGFloat { | ||
| 612 | + return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2)) | ||
| 613 | + } | ||
| 614 | + | ||
| 545 | public func toMap() -> [String: Any?] { | 615 | public func toMap() -> [String: Any?] { |
| 546 | return [ | 616 | return [ |
| 547 | - "rawValue": self.payloadStringValue ?? "", | ||
| 548 | - "format": self.symbology.toInt ?? -1, | 617 | + "corners": [ |
| 618 | + ["x": topLeft.x, "y": topLeft.y], | ||
| 619 | + ["x": topRight.x, "y": topRight.y], | ||
| 620 | + ["x": bottomRight.x, "y": bottomRight.y], | ||
| 621 | + ["x": bottomLeft.x, "y": bottomLeft.y], | ||
| 622 | + ], | ||
| 623 | + "format": symbology.toInt ?? -1, | ||
| 624 | + "rawValue": payloadStringValue ?? "", | ||
| 625 | + "size": [ | ||
| 626 | + "width": distanceBetween(topLeft, topRight), | ||
| 627 | + "height": distanceBetween(topLeft, bottomLeft), | ||
| 628 | + ], | ||
| 549 | ] | 629 | ] |
| 550 | } | 630 | } |
| 551 | } | 631 | } |
| @@ -585,7 +665,7 @@ extension VNBarcodeSymbology { | @@ -585,7 +665,7 @@ extension VNBarcodeSymbology { | ||
| 585 | } | 665 | } |
| 586 | } | 666 | } |
| 587 | 667 | ||
| 588 | - var toInt:Int? { | 668 | + var toInt: Int? { |
| 589 | if #available(macOS 12.0, *) { | 669 | if #available(macOS 12.0, *) { |
| 590 | if(self == VNBarcodeSymbology.codabar){ | 670 | if(self == VNBarcodeSymbology.codabar){ |
| 591 | return 8 | 671 | return 8 |
-
Please register or login to post a comment