diff --git a/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerController.kt b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerController.kt index ded237f..7d84eec 100644 --- a/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerController.kt +++ b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerController.kt @@ -381,8 +381,6 @@ class BarcodeScannerController(private val activity: Activity, messenger: Binary if (cameraParams?.get("scanner_type") == "mrz") { - Log.i("scanner_native", "#####################################################################################################") - val mrz: String? = MrzUtil.extractMRZ(visionText.textBlocks, mrzResult!!) if (mrz != null) { @@ -421,6 +419,8 @@ class BarcodeScannerController(private val activity: Activity, messenger: Binary if (points?.isNotEmpty() == true) { + Log.i("native_scanner", "Extract MRZ done with result $mrz") + var x = points!![0].x var y = points!![0].y var width = points!![1].x - points!![0].x @@ -456,8 +456,31 @@ class BarcodeScannerController(private val activity: Activity, messenger: Binary imageProxy.image?.close() imageProxy.close() + } else { + + Log.i("native_scanner", "Extract MRZ done with result $mrz but image is not loaded yet") + + eventSink?.success(mapOf( + "progress" to 90, + )) + + } + + } else { + + var progress = 5 + if (mrzResult!!.size == 1) { + progress = 25 + } else if (mrzResult!!.size == 2) { + progress = 75 } + Log.i("native_scanner", "Extract MRZ progress with current $mrzResult (progress $progress)") + + eventSink?.success(mapOf( + "progress" to progress, + )) + } } else { diff --git a/android/src/main/kotlin/be/freedelity/barcode_scanner/util/MrzUtil.kt b/android/src/main/kotlin/be/freedelity/barcode_scanner/util/MrzUtil.kt index ccaf139..96738b4 100644 --- a/android/src/main/kotlin/be/freedelity/barcode_scanner/util/MrzUtil.kt +++ b/android/src/main/kotlin/be/freedelity/barcode_scanner/util/MrzUtil.kt @@ -48,10 +48,14 @@ object MrzUtil { mrzLines.forEach { line -> - val text = line.text.replace("«", "<").replace(" ", "").uppercase().trim() + var text = line.text.replace("«", "<").replace(" ", "").uppercase().trim() if (text.matches("^[A-Z0-9<]*$".toRegex())) { + while (text.matches("<<<[KC]".toRegex())) { + text = text.replaceFirst("<<<[KC]".toRegex(), "<<<<") + } + if (((mrzResult.size < 3 && text.length == 30) || mrzResult.size < 2 && (text.length == 36 || text.length == 44))) { if (!mrzResult.any { res -> res.substring(0, 20) == text.substring(0, 20) } && (mrzResult.isEmpty() || mrzResult.first().length == text.length)) { @@ -78,8 +82,6 @@ object MrzUtil { val map = mutableMapOf() val missed = mutableListOf() - Log.i("native_scanner_res", "result : $mrzResult") - mrzResult.forEachIndexed{ index, it -> if (mrzResult.size == 3) { @@ -111,6 +113,7 @@ object MrzUtil { } } } else { + var result = "${map[0]}\n${map[1]}" if (mrzResult.size == 3) result += "\n${map[2]}" return result diff --git a/example/lib/main.dart b/example/lib/main.dart index 20af90f..3db43db 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -59,11 +59,13 @@ class MyDemoApp extends StatefulWidget { class _MyDemoAppState extends State { + int? progress; bool withOverlay = true; - ScannerType scannerType = ScannerType.barcode; + ScannerType scannerType = ScannerType.mrz; @override Widget build(BuildContext context) { + return Scaffold( appBar: AppBar(title: Text('Scanner ${scannerType.name} example'), actions: [ PopupMenuButton( @@ -117,83 +119,106 @@ class _MyDemoAppState extends State { ], ), ]), - body: Builder(builder: (builderContext) { - Widget child = BarcodeScannerWidget( - scannerType: ScannerType.mrz, - onBarcodeDetected: (barcode) async { - await showDialog( - context: builderContext, - builder: (dialogContext) { - return Align( - alignment: Alignment.center, - child: Card( + body: Stack( + children: [ + Positioned.fill( + child: Builder(builder: (builderContext) { + Widget child = BarcodeScannerWidget( + scannerType: ScannerType.mrz, + onBarcodeDetected: (barcode) async { + await showDialog( + context: builderContext, + builder: (dialogContext) { + return Align( + alignment: Alignment.center, + child: Card( + margin: const EdgeInsets.all(24), + child: Container( + padding: const EdgeInsets.all(16), + child: Column(mainAxisSize: MainAxisSize.min, children: [Text('barcode : ${barcode.value}'), Text('format : ${barcode.format.name}'), ElevatedButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Close dialog'))])))); + }); + }, + onTextDetected: (String text) async { + await showDialog( + context: builderContext, + builder: (dialogContext) { + return Align( + alignment: Alignment.center, + child: Card( margin: const EdgeInsets.all(24), child: Container( - padding: const EdgeInsets.all(16), - child: Column(mainAxisSize: MainAxisSize.min, children: [Text('barcode : ${barcode.value}'), Text('format : ${barcode.format.name}'), ElevatedButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Close dialog'))])))); - }); - }, - onTextDetected: (String text) async { - await showDialog( - context: builderContext, - builder: (dialogContext) { - return Align( - alignment: Alignment.center, - child: Card( - margin: const EdgeInsets.all(24), - child: Container( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('text : \n$text'), - ElevatedButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Close dialog')), - ], - ), - ), - ), - ); - }, - ); - }, - onMrzDetected: (String text, Uint8List bytes) { - showDialog( - context: builderContext, - builder: (dialogContext) { - return Align( - alignment: Alignment.center, - child: Card( - margin: const EdgeInsets.all(24), - child: Container( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text(text), - Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Image.memory(bytes), + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('text : \n$text'), + ElevatedButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Close dialog')), + ], + ), ), - ElevatedButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Close dialog')), - ], - ), - ), - ), - ); - }, - ); - }, - onError: (dynamic error) { - debugPrint('$error'); - }, - ); + ), + ); + }, + ); + }, + onScanProgress: (int? progress) => setState(() => this.progress = progress), + onMrzDetected: (String text, Uint8List bytes) { + setState(() => progress = null); + showDialog( + context: builderContext, + builder: (dialogContext) { + return Align( + alignment: Alignment.center, + child: Card( + margin: const EdgeInsets.all(24), + child: Container( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(text), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Image.memory(bytes), + ), + ElevatedButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Close dialog')), + ], + ), + ), + ), + ); + }, + ); + }, + onError: (dynamic error) { + debugPrint('$error'); + }, + ); + + if (withOverlay) { + return buildWithOverlay(builderContext, child); + } - if (withOverlay) { - return buildWithOverlay(builderContext, child); - } + return child; - return child; - })); + }), + ), + progress == null ? Container() : Positioned( + bottom: 0, left: 0, right: 0, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16), + color: Colors.white, + child: Row( + children: [ + const Expanded(child: LinearProgressIndicator()), + const SizedBox(width: 16,), + Text('$progress %'), + ], + ), + ), + ), + ], + )); } buildWithOverlay(BuildContext builderContext, Widget scannerWidget) { diff --git a/lib/barcode_scanner.widget.dart b/lib/barcode_scanner.widget.dart index 9edc9ea..9defbf6 100644 --- a/lib/barcode_scanner.widget.dart +++ b/lib/barcode_scanner.widget.dart @@ -32,11 +32,14 @@ class BarcodeScannerWidget extends StatefulWidget { /// This function will be called when a barcode is detected. final Function(Barcode barcode)? onBarcodeDetected; - /// This function will be called when a bloc of text or a MRZ is detected. + /// This function will be called when a bloc of text is detected. final Function(String textResult)? onTextDetected; + /// This function will be called when a bloc MRZ is detected. final Function(String mrz, Uint8List image)? onMrzDetected; + final Function(int? progress)? onScanProgress; + final Function(dynamic error) onError; const BarcodeScannerWidget( @@ -48,6 +51,7 @@ class BarcodeScannerWidget extends StatefulWidget { this.onBarcodeDetected, this.onTextDetected, this.onMrzDetected, + this.onScanProgress, required this.onError, }) : assert(onBarcodeDetected != null || onTextDetected != null), @@ -76,9 +80,17 @@ class _BarcodeScannerWidgetState extends State { }; eventSubscription = eventChannel.receiveBroadcastStream().listen((dynamic event) async { + + if (widget.onScanProgress != null) { + widget.onScanProgress!(event["progress"] as int?); + } + if (widget.onBarcodeDetected != null && widget.scannerType == ScannerType.barcode) { + final format = BarcodeFormat.unserialize(event['format']); + if (format != null && event['barcode'] != null) { + await BarcodeScanner.stopScanner(); await widget.onBarcodeDetected!(Barcode(format: format, value: event['barcode'] as String)); @@ -86,21 +98,27 @@ class _BarcodeScannerWidgetState extends State { if (!widget.stopScanOnBarcodeDetected) { BarcodeScanner.startScanner(); } - } else { + + } else if (event["progress"] == null) { widget.onError(const FormatException('Barcode not found')); } + } else if (widget.onTextDetected != null && widget.scannerType == ScannerType.text) { + if (event['text'] != null) { await widget.onTextDetected!(event['text'] as String); - } else { + } else if (event["progress"] == null) { widget.onError(const FormatException('Text not found')); } + } else if (widget.onMrzDetected != null && widget.scannerType == ScannerType.mrz) { + if (event['mrz'] != null && event["img"] != null) { await widget.onMrzDetected!(event['mrz'] as String, Uint8List.fromList(event["img"])); - } else { + } else if (event["progress"] == null) { widget.onError(const FormatException('MRZ not found')); } + } }, onError: (dynamic error) { widget.onError(error);