2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-27 21:16:48 +00:00

Adds audio feedback

This commit is contained in:
Oliver Walters 2021-03-02 22:22:16 +11:00
parent 3761f4090f
commit b293806fe3
6 changed files with 93 additions and 10 deletions

View File

@ -1,6 +1,6 @@
buildscript {
ext.kotlin_version = '1.3.61'
ext.kotlin_version = '1.4.21'
repositories {
google()

View File

@ -1,4 +1,4 @@
## 0.1.3 - February 2021
## 0.1.3 - March 2021
---
- Adds ability to toggle "star" status for Part
@ -6,6 +6,7 @@
- User permissions are now queried from the InvenTree server
- Any "unauthorized" actions are now not displayed
- Uses server-side pagination, providing a significant increase in UI performance
- Adds audio feedback for server errors and barcode scanning
## 0.1.2 - February 2021
---

View File

@ -1,5 +1,6 @@
import 'package:InvenTree/widget/dialogs.dart';
import 'package:InvenTree/widget/snacks.dart';
import 'package:audioplayers/audio_cache.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@ -38,6 +39,16 @@ class BarcodeHandler {
QRViewController _controller;
BuildContext _context;
void successTone() {
AudioCache player = AudioCache();
player.play("sounds/barcode_scan.mp3");
}
void failureTone() {
AudioCache player = AudioCache();
player.play("sounds/barcode_error.mp3");
}
Future<void> onBarcodeMatched(Map<String, dynamic> data) {
// Called when the server "matches" a barcode
// Override this function
@ -46,6 +57,9 @@ class BarcodeHandler {
Future<void> onBarcodeUnknown(Map<String, dynamic> data) {
// Called when the server does not know about a barcode
// Override this function
failureTone();
showSnackIcon(
I18N.of(OneContext().context).barcodeNoMatch,
success: false,
@ -54,6 +68,9 @@ class BarcodeHandler {
}
Future<void> onBarcodeUnhandled(Map<String, dynamic> data) {
failureTone();
// Called when the server returns an unhandled response
showServerError(I18N.of(OneContext().context).responseUnknown, data.toString());
@ -119,6 +136,8 @@ class BarcodeScanHandler extends BarcodeHandler {
@override
Future<void> onBarcodeUnknown(Map<String, dynamic> data) {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).barcodeNoMatch,
icon: FontAwesomeIcons.exclamationCircle,
@ -139,6 +158,9 @@ class BarcodeScanHandler extends BarcodeHandler {
pk = data['stocklocation']['pk'] as int ?? null;
if (pk != null) {
successTone();
InvenTreeStockLocation().get(_context, pk).then((var loc) {
if (loc is InvenTreeStockLocation) {
Navigator.of(_context).pop();
@ -146,6 +168,9 @@ class BarcodeScanHandler extends BarcodeHandler {
}
});
} else {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).invalidStockLocation,
success: false
@ -157,11 +182,17 @@ class BarcodeScanHandler extends BarcodeHandler {
pk = data['stockitem']['pk'] as int ?? null;
if (pk != null) {
successTone();
InvenTreeStockItem().get(_context, pk).then((var item) {
Navigator.of(_context).pop();
Navigator.push(_context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
});
} else {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).invalidStockItem,
success: false
@ -172,17 +203,26 @@ class BarcodeScanHandler extends BarcodeHandler {
pk = data['part']['pk'] as int ?? null;
if (pk != null) {
successTone();
InvenTreePart().get(_context, pk).then((var part) {
Navigator.of(_context).pop();
Navigator.push(_context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
});
} else {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).invalidPart,
success: false
);
}
} else {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).barcodeUnknown,
success: false,
@ -220,7 +260,10 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
@override
Future<void> onBarcodeMatched(Map<String, dynamic> data) {
// If the barcode is known, we can't asisgn it to the stock item!
failureTone();
// If the barcode is known, we can't assign it to the stock item!
showSnackIcon(
I18N.of(OneContext().context).barcodeInUse,
icon: FontAwesomeIcons.qrcode,
@ -245,6 +288,8 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
).then((result) {
if (result) {
failureTone();
// Close the barcode scanner
_controller.dispose();
Navigator.of(_context).pop();
@ -256,6 +301,8 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
);
} else {
successTone();
showSnackIcon(
I18N.of(OneContext().context).barcodeNotAssigned,
success: false,
@ -294,6 +341,9 @@ class StockItemScanIntoLocationHandler extends BarcodeHandler {
final result = await item.transferStock(location);
if (result) {
successTone();
// Close the scanner
_controller.dispose();
Navigator.of(_context).pop();
@ -303,12 +353,18 @@ class StockItemScanIntoLocationHandler extends BarcodeHandler {
success: true,
);
} else {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).barcodeScanIntoLocationFailure,
success: false
);
}
} else {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).invalidStockLocation,
success: false,
@ -341,26 +397,37 @@ class StockLocationScanInItemsHandler extends BarcodeHandler {
final InvenTreeStockItem item = await InvenTreeStockItem().get(_context, item_id);
if (item == null) {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).invalidStockItem,
success: false,
);
} else if (item.locationId == location.pk) {
showSnackIcon(
I18N.of(OneContext().context).itemInLocation,
success: true
);
}
failureTone();
else {
showSnackIcon(
I18N
.of(OneContext().context)
.itemInLocation,
success: true
);
} else {
final result = await item.transferStock(location.pk);
if (result) {
successTone();
showSnackIcon(
I18N.of(OneContext().context).barcodeScanIntoLocationSuccess,
success: true
);
} else {
failureTone();
showSnackIcon(
I18N.of(OneContext().context).barcodeScanIntoLocationFailure,
success: false
@ -368,6 +435,9 @@ class StockLocationScanInItemsHandler extends BarcodeHandler {
}
}
} else {
failureTone();
// Does not match a valid stock item!
showSnackIcon(
I18N.of(OneContext().context).invalidStockItem,

View File

@ -1,5 +1,7 @@
import 'package:InvenTree/widget/snacks.dart';
import 'package:audioplayers/audio_cache.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@ -113,7 +115,9 @@ Future<void> showServerError(String title, String description) async {
title = I18N.of(OneContext().context).serverError;
}
// TODO - Play audio notification
// Play a sound
AudioCache player = AudioCache();
player.play("sounds/server_error.mp3");
showSnackIcon(
title,

View File

@ -22,6 +22,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0-nullsafety.1"
audioplayers:
dependency: "direct main"
description:
name: audioplayers
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.4"
boolean_selector:
dependency: transitive
description:

View File

@ -39,6 +39,7 @@ dependencies:
sembast: ^2.4.9 # NoSQL data storage
one_context: ^0.5.0 # Dialogs without requiring context
infinite_scroll_pagination: ^2.3.0 # Let the server do all the work!
audioplayers:
path:
dev_dependencies: