mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 05:26:47 +00:00
Adds audio feedback
This commit is contained in:
parent
3761f4090f
commit
b293806fe3
@ -1,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
|
|
||||||
ext.kotlin_version = '1.3.61'
|
ext.kotlin_version = '1.4.21'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
## 0.1.3 - February 2021
|
## 0.1.3 - March 2021
|
||||||
---
|
---
|
||||||
|
|
||||||
- Adds ability to toggle "star" status for Part
|
- Adds ability to toggle "star" status for Part
|
||||||
@ -6,6 +6,7 @@
|
|||||||
- User permissions are now queried from the InvenTree server
|
- User permissions are now queried from the InvenTree server
|
||||||
- Any "unauthorized" actions are now not displayed
|
- Any "unauthorized" actions are now not displayed
|
||||||
- Uses server-side pagination, providing a significant increase in UI performance
|
- 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
|
## 0.1.2 - February 2021
|
||||||
---
|
---
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:InvenTree/widget/dialogs.dart';
|
import 'package:InvenTree/widget/dialogs.dart';
|
||||||
import 'package:InvenTree/widget/snacks.dart';
|
import 'package:InvenTree/widget/snacks.dart';
|
||||||
|
import 'package:audioplayers/audio_cache.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
@ -38,6 +39,16 @@ class BarcodeHandler {
|
|||||||
QRViewController _controller;
|
QRViewController _controller;
|
||||||
BuildContext _context;
|
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) {
|
Future<void> onBarcodeMatched(Map<String, dynamic> data) {
|
||||||
// Called when the server "matches" a barcode
|
// Called when the server "matches" a barcode
|
||||||
// Override this function
|
// Override this function
|
||||||
@ -46,6 +57,9 @@ class BarcodeHandler {
|
|||||||
Future<void> onBarcodeUnknown(Map<String, dynamic> data) {
|
Future<void> onBarcodeUnknown(Map<String, dynamic> data) {
|
||||||
// Called when the server does not know about a barcode
|
// Called when the server does not know about a barcode
|
||||||
// Override this function
|
// Override this function
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).barcodeNoMatch,
|
I18N.of(OneContext().context).barcodeNoMatch,
|
||||||
success: false,
|
success: false,
|
||||||
@ -54,6 +68,9 @@ class BarcodeHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onBarcodeUnhandled(Map<String, dynamic> data) {
|
Future<void> onBarcodeUnhandled(Map<String, dynamic> data) {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
// Called when the server returns an unhandled response
|
// Called when the server returns an unhandled response
|
||||||
showServerError(I18N.of(OneContext().context).responseUnknown, data.toString());
|
showServerError(I18N.of(OneContext().context).responseUnknown, data.toString());
|
||||||
|
|
||||||
@ -119,6 +136,8 @@ class BarcodeScanHandler extends BarcodeHandler {
|
|||||||
@override
|
@override
|
||||||
Future<void> onBarcodeUnknown(Map<String, dynamic> data) {
|
Future<void> onBarcodeUnknown(Map<String, dynamic> data) {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).barcodeNoMatch,
|
I18N.of(OneContext().context).barcodeNoMatch,
|
||||||
icon: FontAwesomeIcons.exclamationCircle,
|
icon: FontAwesomeIcons.exclamationCircle,
|
||||||
@ -139,6 +158,9 @@ class BarcodeScanHandler extends BarcodeHandler {
|
|||||||
pk = data['stocklocation']['pk'] as int ?? null;
|
pk = data['stocklocation']['pk'] as int ?? null;
|
||||||
|
|
||||||
if (pk != null) {
|
if (pk != null) {
|
||||||
|
|
||||||
|
successTone();
|
||||||
|
|
||||||
InvenTreeStockLocation().get(_context, pk).then((var loc) {
|
InvenTreeStockLocation().get(_context, pk).then((var loc) {
|
||||||
if (loc is InvenTreeStockLocation) {
|
if (loc is InvenTreeStockLocation) {
|
||||||
Navigator.of(_context).pop();
|
Navigator.of(_context).pop();
|
||||||
@ -146,6 +168,9 @@ class BarcodeScanHandler extends BarcodeHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).invalidStockLocation,
|
I18N.of(OneContext().context).invalidStockLocation,
|
||||||
success: false
|
success: false
|
||||||
@ -157,11 +182,17 @@ class BarcodeScanHandler extends BarcodeHandler {
|
|||||||
pk = data['stockitem']['pk'] as int ?? null;
|
pk = data['stockitem']['pk'] as int ?? null;
|
||||||
|
|
||||||
if (pk != null) {
|
if (pk != null) {
|
||||||
|
|
||||||
|
successTone();
|
||||||
|
|
||||||
InvenTreeStockItem().get(_context, pk).then((var item) {
|
InvenTreeStockItem().get(_context, pk).then((var item) {
|
||||||
Navigator.of(_context).pop();
|
Navigator.of(_context).pop();
|
||||||
Navigator.push(_context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
Navigator.push(_context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).invalidStockItem,
|
I18N.of(OneContext().context).invalidStockItem,
|
||||||
success: false
|
success: false
|
||||||
@ -172,17 +203,26 @@ class BarcodeScanHandler extends BarcodeHandler {
|
|||||||
pk = data['part']['pk'] as int ?? null;
|
pk = data['part']['pk'] as int ?? null;
|
||||||
|
|
||||||
if (pk != null) {
|
if (pk != null) {
|
||||||
|
|
||||||
|
successTone();
|
||||||
|
|
||||||
InvenTreePart().get(_context, pk).then((var part) {
|
InvenTreePart().get(_context, pk).then((var part) {
|
||||||
Navigator.of(_context).pop();
|
Navigator.of(_context).pop();
|
||||||
Navigator.push(_context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
Navigator.push(_context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).invalidPart,
|
I18N.of(OneContext().context).invalidPart,
|
||||||
success: false
|
success: false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).barcodeUnknown,
|
I18N.of(OneContext().context).barcodeUnknown,
|
||||||
success: false,
|
success: false,
|
||||||
@ -220,7 +260,10 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onBarcodeMatched(Map<String, dynamic> data) {
|
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(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).barcodeInUse,
|
I18N.of(OneContext().context).barcodeInUse,
|
||||||
icon: FontAwesomeIcons.qrcode,
|
icon: FontAwesomeIcons.qrcode,
|
||||||
@ -245,6 +288,8 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
|
|||||||
).then((result) {
|
).then((result) {
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
// Close the barcode scanner
|
// Close the barcode scanner
|
||||||
_controller.dispose();
|
_controller.dispose();
|
||||||
Navigator.of(_context).pop();
|
Navigator.of(_context).pop();
|
||||||
@ -256,6 +301,8 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
successTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).barcodeNotAssigned,
|
I18N.of(OneContext().context).barcodeNotAssigned,
|
||||||
success: false,
|
success: false,
|
||||||
@ -294,6 +341,9 @@ class StockItemScanIntoLocationHandler extends BarcodeHandler {
|
|||||||
final result = await item.transferStock(location);
|
final result = await item.transferStock(location);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
||||||
|
successTone();
|
||||||
|
|
||||||
// Close the scanner
|
// Close the scanner
|
||||||
_controller.dispose();
|
_controller.dispose();
|
||||||
Navigator.of(_context).pop();
|
Navigator.of(_context).pop();
|
||||||
@ -303,12 +353,18 @@ class StockItemScanIntoLocationHandler extends BarcodeHandler {
|
|||||||
success: true,
|
success: true,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).barcodeScanIntoLocationFailure,
|
I18N.of(OneContext().context).barcodeScanIntoLocationFailure,
|
||||||
success: false
|
success: false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).invalidStockLocation,
|
I18N.of(OneContext().context).invalidStockLocation,
|
||||||
success: false,
|
success: false,
|
||||||
@ -341,26 +397,37 @@ class StockLocationScanInItemsHandler extends BarcodeHandler {
|
|||||||
final InvenTreeStockItem item = await InvenTreeStockItem().get(_context, item_id);
|
final InvenTreeStockItem item = await InvenTreeStockItem().get(_context, item_id);
|
||||||
|
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).invalidStockItem,
|
I18N.of(OneContext().context).invalidStockItem,
|
||||||
success: false,
|
success: false,
|
||||||
);
|
);
|
||||||
} else if (item.locationId == location.pk) {
|
} else if (item.locationId == location.pk) {
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).itemInLocation,
|
I18N
|
||||||
|
.of(OneContext().context)
|
||||||
|
.itemInLocation,
|
||||||
success: true
|
success: true
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
else {
|
|
||||||
final result = await item.transferStock(location.pk);
|
final result = await item.transferStock(location.pk);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
||||||
|
successTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).barcodeScanIntoLocationSuccess,
|
I18N.of(OneContext().context).barcodeScanIntoLocationSuccess,
|
||||||
success: true
|
success: true
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).barcodeScanIntoLocationFailure,
|
I18N.of(OneContext().context).barcodeScanIntoLocationFailure,
|
||||||
success: false
|
success: false
|
||||||
@ -368,6 +435,9 @@ class StockLocationScanInItemsHandler extends BarcodeHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
failureTone();
|
||||||
|
|
||||||
// Does not match a valid stock item!
|
// Does not match a valid stock item!
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
I18N.of(OneContext().context).invalidStockItem,
|
I18N.of(OneContext().context).invalidStockItem,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
|
||||||
import 'package:InvenTree/widget/snacks.dart';
|
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/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.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;
|
title = I18N.of(OneContext().context).serverError;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - Play audio notification
|
// Play a sound
|
||||||
|
AudioCache player = AudioCache();
|
||||||
|
player.play("sounds/server_error.mp3");
|
||||||
|
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
title,
|
title,
|
||||||
|
@ -22,6 +22,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0-nullsafety.1"
|
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:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -39,6 +39,7 @@ dependencies:
|
|||||||
sembast: ^2.4.9 # NoSQL data storage
|
sembast: ^2.4.9 # NoSQL data storage
|
||||||
one_context: ^0.5.0 # Dialogs without requiring context
|
one_context: ^0.5.0 # Dialogs without requiring context
|
||||||
infinite_scroll_pagination: ^2.3.0 # Let the server do all the work!
|
infinite_scroll_pagination: ^2.3.0 # Let the server do all the work!
|
||||||
|
audioplayers:
|
||||||
path:
|
path:
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user