- Keep UI state after boxing completion for operator review instead of auto-reset - Show real work order number instead of hardcoded "本次扫码" in assigned list - Add double-vibration feedback when re-scanning already completed zongpai - Change completed status banner and dot from green to red for clearer warning Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
99 lines
2.9 KiB
Dart
99 lines
2.9 KiB
Dart
import 'package:vibration/vibration.dart';
|
|
import 'package:pad_scanner/services/sound_service.dart';
|
|
|
|
/// Unified feedback events used across the app.
|
|
enum FeedbackEvent {
|
|
scanValid, // Valid barcode scanned → beep + short vibrate
|
|
scanInvalid, // Invalid barcode → error sound + short vibrate
|
|
submitSuccess, // Submit succeeded → success sound + long vibrate
|
|
submitFailure, // Submit failed → failure sound + short vibrate
|
|
duplicateError, // Duplicate (shelf) → alert loop + continuous vibrate
|
|
networkError, // Network error → failure sound + short vibrate
|
|
duplicateBoxNo, // Duplicate box no → failure sound + short vibrate
|
|
alreadyCompleted, // Zongpai already boxed → beep + double vibrate
|
|
modeSwitch, // Mode toggle → beep only
|
|
paichanSwitch, // Paichan changed → beep + short vibrate
|
|
}
|
|
|
|
/// Centralized feedback dispatcher.
|
|
///
|
|
/// Each page manages its own bottom status bar state; this service handles
|
|
/// the sound, vibration channels only.
|
|
class FeedbackService {
|
|
final SoundService _soundService;
|
|
|
|
FeedbackService({SoundService? soundService})
|
|
: _soundService = soundService ?? SoundService();
|
|
|
|
/// Trigger feedback for the given [event].
|
|
Future<void> trigger(FeedbackEvent event) async {
|
|
switch (event) {
|
|
case FeedbackEvent.scanValid:
|
|
_soundService.playBeep();
|
|
_vibrateShort();
|
|
|
|
case FeedbackEvent.scanInvalid:
|
|
_soundService.playError();
|
|
_vibrateShort();
|
|
|
|
case FeedbackEvent.submitSuccess:
|
|
_soundService.playSuccess();
|
|
_vibrateLong();
|
|
|
|
case FeedbackEvent.submitFailure:
|
|
_soundService.playFailure();
|
|
_vibrateShort();
|
|
|
|
case FeedbackEvent.duplicateError:
|
|
_soundService.playAlertLoop();
|
|
_vibratePattern();
|
|
|
|
case FeedbackEvent.networkError:
|
|
_soundService.playFailure();
|
|
_vibrateShort();
|
|
|
|
case FeedbackEvent.duplicateBoxNo:
|
|
_soundService.playFailure();
|
|
_vibrateShort();
|
|
|
|
case FeedbackEvent.alreadyCompleted:
|
|
_soundService.playBeep();
|
|
_vibrateDouble();
|
|
|
|
case FeedbackEvent.modeSwitch:
|
|
_soundService.playBeep();
|
|
|
|
case FeedbackEvent.paichanSwitch:
|
|
_soundService.playBeep();
|
|
_vibrateShort();
|
|
}
|
|
}
|
|
|
|
/// Stop any ongoing alert (e.g. when duplicate dialog is closed).
|
|
Future<void> stopAlert() async {
|
|
_soundService.stopAlert();
|
|
}
|
|
|
|
void _vibrateShort() {
|
|
Vibration.vibrate(duration: 100);
|
|
}
|
|
|
|
void _vibrateLong() {
|
|
Vibration.vibrate(duration: 200);
|
|
}
|
|
|
|
/// Continuous vibration pattern: 3 bursts to signal critical error.
|
|
void _vibratePattern() {
|
|
Vibration.vibrate(pattern: [0, 300, 100, 300, 100, 300]);
|
|
}
|
|
|
|
/// Two short bursts to signal an already-completed item.
|
|
void _vibrateDouble() {
|
|
Vibration.vibrate(pattern: [0, 200, 100, 200]);
|
|
}
|
|
|
|
Future<void> dispose() async {
|
|
await _soundService.dispose();
|
|
}
|
|
}
|