From fb80029c0ee60b1eef533a51e6fa47c2d4220728 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 22 Feb 2023 00:52:26 +1100 Subject: [PATCH] Barcode camera fix (#270) * Bug fix for multiple barcode scans - Do not resume scan until action is performed - Add in delay before re-starting camera * Change release notes to 0.10.1 * linting fixes --- assets/release_notes.md | 3 +- lib/barcode.dart | 146 +++++++++++++++++++++++----------------- 2 files changed, 88 insertions(+), 61 deletions(-) diff --git a/assets/release_notes.md b/assets/release_notes.md index 3e6cee73..97fe1c2f 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -1,10 +1,11 @@ ## InvenTree App Release Notes --- -### 0.11.0 - +### 0.10.1 - February 2023 --- - Add support for attachments on Companies +- Fix duplicate scanning of barcodes ### 0.10.0 - February 2023 --- diff --git a/lib/barcode.dart b/lib/barcode.dart index 3c77cd66..a0306315 100644 --- a/lib/barcode.dart +++ b/lib/barcode.dart @@ -58,8 +58,6 @@ class BarcodeHandler { String getOverlayText(BuildContext context) => "Barcode Overlay"; - QRViewController? _controller; - Future onBarcodeMatched(Map data) async { // Called when the server "matches" a barcode // Override this function @@ -78,21 +76,18 @@ class BarcodeHandler { ); } - Future onBarcodeUnhandled(Map data) async { - + // Called when the server returns an unhandled response + Future onBarcodeUnhandled(Map data) async { barcodeFailureTone(); - - // Called when the server returns an unhandled response showServerError("barcode/", L10().responseUnknown, data.toString()); - - _controller?.resumeCamera(); } /* * Base function to capture and process barcode data. + * + * Returns true only if the barcode scanner should remain open */ Future processBarcode(QRViewController? _controller, String barcode, {String url = "barcode/"}) async { - this._controller = _controller; debug("Scanned barcode data: '${barcode}'"); @@ -122,8 +117,6 @@ class BarcodeHandler { debug("Barcode scan response" + response.data.toString()); - _controller?.resumeCamera(); - Map data = response.asMap(); // Handle strange response from the server @@ -188,53 +181,55 @@ class BarcodeScanHandler extends BarcodeHandler { * Response when a "Part" instance is scanned */ Future handlePart(int pk) async { - InvenTreePart().get(pk).then((var part) { - // Dismiss the barcode scanner - OneContext().pop(); - if (part is InvenTreePart) { - OneContext().push(MaterialPageRoute(builder: (context) => PartDetailWidget(part))); - } - }); + var part = await InvenTreePart().get(pk); + + if (part is InvenTreePart) { + OneContext().pop(); + OneContext().push(MaterialPageRoute(builder: (context) => PartDetailWidget(part))); + } } /* * Response when a "StockItem" instance is scanned */ Future handleStockItem(int pk) async { - InvenTreeStockItem().get(pk).then((var item) { + + var item = await InvenTreeStockItem().get(pk); + + if (item is InvenTreeStockItem) { OneContext().pop(); - if (item is InvenTreeStockItem) { - OneContext().push(MaterialPageRoute( + OneContext().push(MaterialPageRoute( builder: (context) => StockDetailWidget(item))); - } - }); + } } /* * Response when a "StockLocation" instance is scanned */ Future handleStockLocation(int pk) async { - InvenTreeStockLocation().get(pk).then((var loc) { - if (loc is InvenTreeStockLocation) { - OneContext().pop(); - OneContext().navigator.push(MaterialPageRoute( - builder: (context) => LocationDisplayWidget(loc))); - } - }); + + var loc = await InvenTreeStockLocation().get(pk); + + if (loc is InvenTreeStockLocation) { + OneContext().pop(); + OneContext().navigator.push(MaterialPageRoute( + builder: (context) => LocationDisplayWidget(loc))); + } } /* * Response when a "SupplierPart" instance is scanned */ Future handleSupplierPart(int pk) async { - InvenTreeSupplierPart().get(pk).then((var supplierpart) { - OneContext().pop(); - if (supplierpart is InvenTreeSupplierPart) { - OneContext().push(MaterialPageRoute(builder: (context) => SupplierPartDetailWidget(supplierpart))); - } - }); + var supplierpart = await InvenTreeSupplierPart().get(pk); + + if (supplierpart is InvenTreeSupplierPart) { + OneContext().pop(); + OneContext().push(MaterialPageRoute( + builder: (context) => SupplierPartDetailWidget(supplierpart))); + } } @override @@ -270,19 +265,19 @@ class BarcodeScanHandler extends BarcodeHandler { switch (model) { case "part": - handlePart(pk); + await handlePart(pk); return; case "stockitem": - handleStockItem(pk); + await handleStockItem(pk); return; case "stocklocation": - handleStockLocation(pk); + await handleStockLocation(pk); return; case "supplierpart": - handleSupplierPart(pk); + await handleSupplierPart(pk); return; default: - // Fall through to failure state + // Fall through to failure state break; } } @@ -339,9 +334,8 @@ class BarcodeScanStockLocationHandler extends BarcodeHandler { if (result && OneContext.hasContext) { OneContext().pop(); + return; } - - return; } } @@ -386,13 +380,12 @@ class BarcodeScanStockItemHandler extends BarcodeHandler { barcodeSuccessTone(); - final bool result = await onItemScanned(_item); + bool result = await onItemScanned(_item); if (result && OneContext.hasContext) { OneContext().pop(); + return; } - - return; } } @@ -410,7 +403,6 @@ class BarcodeScanStockItemHandler extends BarcodeHandler { // Re-implement this for particular subclass return false; } - } @@ -480,10 +472,10 @@ class StockLocationScanInItemsHandler extends BarcodeScanStockItemHandler { } } - showSnackIcon( - result ? L10().barcodeScanIntoLocationSuccess : L10().barcodeScanIntoLocationFailure, - success: result - ); + showSnackIcon( + result ? L10().barcodeScanIntoLocationSuccess : L10().barcodeScanIntoLocationFailure, + success: result + ); // We always return false here, to ensure the barcode scan dialog remains open return false; @@ -641,6 +633,8 @@ class _QRViewState extends State { bool flash_status = false; + bool currently_processing = false; + Future updateFlashStatus() async { final bool? status = await _controller?.getFlashStatus(); @@ -658,21 +652,53 @@ class _QRViewState extends State { void reassemble() { super.reassemble(); - if (Platform.isAndroid) { - _controller!.pauseCamera(); - } + if (mounted) { + if (Platform.isAndroid) { + _controller!.pauseCamera(); + } - _controller!.resumeCamera(); + _controller!.resumeCamera(); + } } + /* Callback function when the Barcode scanner view is initially created */ void _onViewCreated(BuildContext context, QRViewController controller) { _controller = controller; - controller.scannedDataStream.listen((barcode) { - _controller?.pauseCamera(); - if (barcode.code != null) { - widget._handler.processBarcode(_controller, barcode.code ?? ""); - } + controller.scannedDataStream.listen((barcode) { + handleBarcode(barcode.code); + }); + } + + /* Handle scanned data */ + Future handleBarcode(String? data) async { + + // Empty or missing data, or we have navigated away + if (!mounted || data == null || data.isEmpty) { + return; + } + + // Currently processing a barcode - return! + if (currently_processing) { + return; + } + + setState(() { + currently_processing = true; + }); + + // Pause camera functionality until we are done processing + _controller?.pauseCamera(); + + // processBarcode returns true if the scanner window is to remain open + widget._handler.processBarcode(_controller, data).then((value) { + // Re-start the process after some delay + Future.delayed(Duration(milliseconds: 500)).then((value) { + if (mounted) { + _controller?.resumeCamera(); + currently_processing = false; + } + }); }); }