From 8cb5dd20f05a37bd3c3bfbd699552539eff148b6 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 14 Nov 2023 07:39:06 +1100 Subject: [PATCH] Barcode Scanning Updates (#448) * Add new setting for controlling manual barcode scan * Adds ability to pause and resume scanning with button - Camera is still "live" during this * Add UI elements * Change scan setting - "Continuous" scanning - Enabled by default * Update release notes * Scanner updates - Use "hold to pause" in continuous scan - Use "tap to pause" in single scan * Improve barcode scanning options - Allow tap-to-pause or hold-to-pause - More obvious user interactions * Ensure consistent icon placement * Remove separate setting for barcode pause mode --- assets/release_notes.md | 3 + lib/barcode/camera_controller.dart | 141 +++++++++++++++++++++-------- lib/l10n/app_en.arb | 12 +++ lib/preferences.dart | 1 + lib/settings/barcode_settings.dart | 16 ++++ pubspec.yaml | 2 +- 6 files changed, 134 insertions(+), 41 deletions(-) diff --git a/assets/release_notes.md b/assets/release_notes.md index dc12be15..544c49dd 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -2,8 +2,11 @@ --- - Adds support for Sales Orders +- Adds option to pause and resume barcode scanning with camera +- Adds option for "single shot" barcode scanning with camera - Fixes bug when removing entire quantity of a stock item + ### 0.13.0 - October 2023 --- diff --git a/lib/barcode/camera_controller.dart b/lib/barcode/camera_controller.dart index 94031e8d..9947c1a6 100644 --- a/lib/barcode/camera_controller.dart +++ b/lib/barcode/camera_controller.dart @@ -1,5 +1,8 @@ import "dart:io"; import "package:flutter/material.dart"; +import "package:font_awesome_flutter/font_awesome_flutter.dart"; +import "package:inventree/app_colors.dart"; +import "package:inventree/preferences.dart"; import "package:qr_code_scanner/qr_code_scanner.dart"; @@ -13,30 +16,54 @@ import "package:inventree/barcode/controller.dart"; * Under the hood it uses the qr_code_scanner package. */ class CameraBarcodeController extends InvenTreeBarcodeController { - - const CameraBarcodeController(BarcodeHandler handler, {Key? key}) : super(handler, key: key); + const CameraBarcodeController(BarcodeHandler handler, {Key? key}) + : super(handler, key: key); @override State createState() => _CameraBarcodeControllerState(); - } - class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { - _CameraBarcodeControllerState() : super(); QRViewController? _controller; bool flash_status = false; + bool single_scanning = false; + bool scanning_paused = false; + + Future _loadSettings() async { + bool _single = await InvenTreeSettingsManager() + .getBool(INV_BARCODE_SCAN_SINGLE, false); + + if (mounted) { + setState(() { + single_scanning = _single; + scanning_paused = false; + }); + } + } + /* Callback function when the Barcode scanner view is initially created */ void _onViewCreated(BuildContext context, QRViewController controller) { _controller = controller; controller.scannedDataStream.listen((barcode) { - handleBarcodeData(barcode.code); + if (!scanning_paused) { + handleBarcodeData(barcode.code).then((value) => { + // If in single-scanning mode, pause after successful scan + if (single_scanning && mounted) + { + setState(() { + scanning_paused = true; + }) + } + }); + } }); + + _loadSettings(); } // In order to get hot reload to work we need to pause the camera if the platform @@ -71,7 +98,6 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { @override Future resumeScan() async { - // Do not attempt to resume if the widget is not mounted if (!mounted) { return; @@ -97,17 +123,25 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { @override Widget build(BuildContext context) { + Widget actionIcon = + FaIcon(FontAwesomeIcons.circlePause, color: COLOR_WARNING, size: 64); + + if (scanning_paused) { + actionIcon = + FaIcon(FontAwesomeIcons.circlePlay, color: COLOR_ACTION, size: 64); + } + + String info_text = scanning_paused ? L10().barcodeScanPaused : L10().barcodeScanPause; return Scaffold( appBar: AppBar( title: Text(L10().scanBarcode), actions: [ IconButton( - icon: Icon(Icons.flip_camera_android), - onPressed: () { - _controller?.flipCamera(); - } - ), + icon: Icon(Icons.flip_camera_android), + onPressed: () { + _controller?.flipCamera(); + }), IconButton( icon: flash_status ? Icon(Icons.flash_off) : Icon(Icons.flash_on), onPressed: () { @@ -117,44 +151,71 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { ) ], ), - body: Stack( - children: [ - Column( - children: [ - Expanded( - child: QRView( + body: GestureDetector( + onTapDown: (details) async { + setState(() { + scanning_paused = !scanning_paused; + }); + }, + onLongPressEnd: (details) async { + if (mounted) { + setState(() { + scanning_paused = false; + }); + } + }, + child: Stack( + children: [ + Column(children: [ + Expanded( + child: QRView( key: barcodeControllerKey, onQRViewCreated: (QRViewController controller) { _onViewCreated(context, controller); }, overlay: QrScannerOverlayShape( - borderColor: Colors.red, + borderColor: + scanning_paused ? COLOR_WARNING : COLOR_ACTION, borderRadius: 10, borderLength: 30, borderWidth: 10, cutOutSize: 300, ), - ) - ) - ] - ), - Center( - child: Column( - children: [ - Spacer(), - Padding( - child: Text(widget.handler.getOverlayText(context), - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.white), - ), - padding: EdgeInsets.all(20), + )) + ]), + Center( + child: Column(children: [ + Padding( + child: Text( + widget.handler.getOverlayText(context), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, color: Colors.white), ), - ] - ) - ) - ], - ) - ); + padding: EdgeInsets.all(25)), + Padding( + child: CircularProgressIndicator( + value: scanning_paused ? 0 : null), + padding: EdgeInsets.all(40), + ), + Spacer(), + SizedBox( + child: Center( + child: actionIcon, + ), + width: 100, + height: 150, + ), + Padding( + child: Text(info_text, + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + )), + padding: EdgeInsets.all(25), + ), + ])) + ], + ))); } } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index c47b495b..4ca99a65 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -115,6 +115,12 @@ "barcodeReceivePart": "Scan barcode to receive part", "@barcodeReceivePart": {}, + "barcodeScanPaused": "Barcode scanning paused", + "@barodeScanPaused": {}, + + "barcodeScanPause": "Tap or hold to pause scanning", + "@barcodeScanPause": {}, + "barcodeScanAssign": "Scan to assign barcode", "@barcodeScanAssign": {}, @@ -139,6 +145,12 @@ "barcodeScanLocation": "Scan stock location", "@barcodeScanLocation": {}, + "barcodeScanSingle": "Single Scan Mode", + "@barcodeScanSingle": {}, + + "barcodeScanSingleDetail": "Pause barcode scanner after each scan", + "@barcodeScanSingleDetail": {}, + "barcodeScanIntoLocationSuccess": "Scanned into location", "@barcodeScanIntoLocationSuccess": {}, diff --git a/lib/preferences.dart b/lib/preferences.dart index 581a5fd8..19e4ffbe 100644 --- a/lib/preferences.dart +++ b/lib/preferences.dart @@ -42,6 +42,7 @@ const String INV_STRICT_HTTPS = "strictHttps"; // Barcode settings const String INV_BARCODE_SCAN_DELAY = "barcodeScanDelay"; const String INV_BARCODE_SCAN_TYPE = "barcodeScanType"; +const String INV_BARCODE_SCAN_SINGLE = "barcodeScanSingle"; // Barcode scanner types const int BARCODE_CONTROLLER_CAMERA = 0; diff --git a/lib/settings/barcode_settings.dart b/lib/settings/barcode_settings.dart index 44dfb7f3..bb202b23 100644 --- a/lib/settings/barcode_settings.dart +++ b/lib/settings/barcode_settings.dart @@ -18,6 +18,7 @@ class _InvenTreeBarcodeSettingsState extends State loadSettings() async { barcodeScanDelay = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_DELAY, 500) as int; barcodeScanType = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_TYPE, BARCODE_CONTROLLER_CAMERA) as int; + barcodeScanSingle = await InvenTreeSettingsManager().getBool(INV_BARCODE_SCAN_SINGLE, false); if (mounted) { setState(() { @@ -153,6 +155,20 @@ class _InvenTreeBarcodeSettingsState extends State=2.19.5 <3.13.0"