Se agrego el sdk para el pda de honeywell

This commit is contained in:
Alejandro Lara
2026-06-19 08:57:58 -06:00
parent 50fe9a9d36
commit d6db216d2d
21 changed files with 1038 additions and 77 deletions
+71 -18
View File
@@ -1,43 +1,92 @@
import 'dart:io';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:vibration/vibration.dart';
class FeedbackService {
FeedbackService() : _player = AudioPlayer() {
_player.setReleaseMode(ReleaseMode.stop);
FeedbackService() {
_configurePlayers();
}
final AudioPlayer _player;
final AudioPlayer _scanPlayer = AudioPlayer();
final AudioPlayer _successPlayer = AudioPlayer();
final AudioPlayer _warningPlayer = AudioPlayer();
static final AudioContext _loudContext = AudioContext(
android: AudioContextAndroid(
isSpeakerphoneOn: true,
audioMode: AndroidAudioMode.normal,
contentType: AndroidContentType.sonification,
usageType: AndroidUsageType.alarm,
audioFocus: AndroidAudioFocus.gain,
),
iOS: AudioContextIOS(
category: AVAudioSessionCategory.playback,
options: {
AVAudioSessionOptions.mixWithOthers,
AVAudioSessionOptions.duckOthers,
},
),
);
Future<void> _configurePlayers() async {
for (final player in [_scanPlayer, _successPlayer, _warningPlayer]) {
await player.setReleaseMode(ReleaseMode.stop);
await player.setPlayerMode(PlayerMode.lowLatency);
await player.setVolume(1.0);
await player.setAudioContext(_loudContext);
}
}
Future<void> onCodeDetected() async {
await Future.wait([
_play('sounds/scan.wav'),
_vibratePattern(const [0, 40]),
_play(_scanPlayer, 'sounds/scan.wav'),
_vibratePattern(const [0, 80]),
HapticFeedback.mediumImpact(),
]);
}
Future<void> onSuccess() async {
await Future.wait([
_play('sounds/success.wav'),
_vibratePattern(const [0, 90, 70, 140]),
_playSuccessSequence(),
_vibratePattern(const [0, 120, 60, 180]),
HapticFeedback.heavyImpact(),
]);
await HapticFeedback.heavyImpact();
}
Future<void> onWarning() async {
await Future.wait([
_play('sounds/warning.wav'),
_vibratePattern(const [0, 120, 80, 120, 80, 120]),
_playWarningSequence(),
_vibratePattern(const [0, 180, 80, 180, 80, 220]),
HapticFeedback.heavyImpact(),
]);
await HapticFeedback.mediumImpact();
}
Future<void> _play(String asset) async {
Future<void> _playSuccessSequence() async {
await _play(_successPlayer, 'sounds/success.wav');
if (!kIsWeb && Platform.isAndroid) {
await Future<void>.delayed(const Duration(milliseconds: 120));
await _play(_successPlayer, 'sounds/success.wav');
}
}
Future<void> _playWarningSequence() async {
for (var i = 0; i < 2; i++) {
await _play(_warningPlayer, 'sounds/warning.wav');
if (i == 0) {
await Future<void>.delayed(const Duration(milliseconds: 140));
}
}
}
Future<void> _play(AudioPlayer player, String asset) async {
try {
await _player.stop();
await _player.play(AssetSource(asset));
await player.stop();
await player.play(AssetSource(asset));
} catch (_) {
// Ignore audio errors on devices without speaker routing.
await SystemSound.play(SystemSoundType.alert);
}
}
@@ -51,7 +100,7 @@ class FeedbackService {
final intensities = pattern
.asMap()
.entries
.map((entry) => entry.key.isOdd ? 180 : 0)
.map((entry) => entry.key.isOdd ? 255 : 0)
.toList();
await Vibration.vibrate(
pattern: pattern,
@@ -62,11 +111,15 @@ class FeedbackService {
await Vibration.vibrate(pattern: pattern);
} catch (_) {
await HapticFeedback.selectionClick();
await HapticFeedback.heavyImpact();
}
}
Future<void> dispose() async {
await _player.dispose();
await Future.wait([
_scanPlayer.dispose(),
_successPlayer.dispose(),
_warningPlayer.dispose(),
]);
}
}
@@ -0,0 +1,62 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:qrscanner/services/scan_launch_service.dart';
typedef HardwareScanCallback = void Function(String rawValue);
/// Integración con el escáner físico Honeywell vía Intent API nativa (EDA51).
class HardwareScannerService {
HardwareScannerService._();
static final HardwareScannerService instance = HardwareScannerService._();
final _scanLaunch = ScanLaunchService.instance;
HardwareScanCallback? _onScan;
void Function(Exception error)? _onError;
StreamSubscription<String>? _scanSubscription;
bool _active = false;
bool get isActive => _active;
static Future<bool> isAvailable() => instance._scanLaunch.isSupported();
Future<bool> activate({
required HardwareScanCallback onScan,
void Function(Exception error)? onError,
}) async {
if (kIsWeb || !Platform.isAndroid) return false;
_onScan = onScan;
_onError = onError;
await _scanLaunch.claimScanner();
await _scanSubscription?.cancel();
_scanSubscription = _scanLaunch.scanStream.listen(
(code) => _onScan?.call(code),
onError: (Object error) => _onError?.call(Exception(error.toString())),
);
_active = true;
return _scanLaunch.isSupported();
}
Future<void> pause() async {
_active = false;
}
Future<void> deactivate() async {
await _scanSubscription?.cancel();
_scanSubscription = null;
_onScan = null;
_onError = null;
_active = false;
}
Future<void> ensureStopped() async {
await deactivate();
await _scanLaunch.releaseScanner();
}
}
+62
View File
@@ -0,0 +1,62 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
class ScanLaunchService {
ScanLaunchService._();
static final ScanLaunchService instance = ScanLaunchService._();
static const _methodChannel = MethodChannel('com.example.qrscanner/scan_launch');
static const _eventChannel = EventChannel('com.example.qrscanner/scan_events');
Stream<String>? _scanStream;
bool get isAndroid => !kIsWeb && Platform.isAndroid;
Stream<String> get scanStream {
_scanStream ??= _eventChannel
.receiveBroadcastStream()
.where((event) => event is String && event.trim().isNotEmpty)
.map((event) => (event as String).trim());
return _scanStream!;
}
Future<bool> isSupported() async {
if (!isAndroid) return false;
try {
final supported = await _methodChannel.invokeMethod<bool>('isSupported');
return supported ?? false;
} catch (_) {
return false;
}
}
Future<void> claimScanner() async {
if (!isAndroid) return;
try {
await _methodChannel.invokeMethod<void>('claimScanner');
} catch (_) {}
}
Future<void> releaseScanner() async {
if (!isAndroid) return;
try {
await _methodChannel.invokeMethod<void>('releaseScanner');
} catch (_) {}
}
Future<String?> consumePendingScan() async {
if (!isAndroid) return null;
try {
final code = await _methodChannel.invokeMethod<String>('consumePendingScan');
final trimmed = code?.trim();
if (trimmed == null || trimmed.isEmpty) return null;
return trimmed;
} catch (_) {
return null;
}
}
}
+137
View File
@@ -0,0 +1,137 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:qrscanner/models/auth_session.dart';
import 'package:qrscanner/screens/home_screen.dart';
import 'package:qrscanner/screens/scanner_screen.dart';
import 'package:qrscanner/services/auth_service.dart';
import 'package:qrscanner/services/scan_launch_service.dart';
class ScanNavigationService {
ScanNavigationService._();
static final ScanNavigationService instance = ScanNavigationService._();
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final AuthService _authService = AuthService();
StreamSubscription<String>? _scanSubscription;
String? _pendingCode;
bool _handlingScan = false;
bool _scannerOpen = false;
String? get pendingCode => _pendingCode;
void setPendingCode(String code) {
final trimmed = code.trim();
if (trimmed.isEmpty) return;
_pendingCode = trimmed;
}
String? takePendingCode() {
final code = _pendingCode;
_pendingCode = null;
return code;
}
Future<void> initialize() async {
await ScanLaunchService.instance.claimScanner();
final pending = await ScanLaunchService.instance.consumePendingScan();
if (pending != null) {
setPendingCode(pending);
}
await _scanSubscription?.cancel();
_scanSubscription = ScanLaunchService.instance.scanStream.listen(
(code) => unawaited(handleScan(code)),
);
}
Future<void> dispose() async {
await _scanSubscription?.cancel();
_scanSubscription = null;
}
Future<void> handleScan(String code, {AuthSession? session}) async {
final trimmed = code.trim();
if (trimmed.isEmpty || _handlingScan) return;
_handlingScan = true;
try {
setPendingCode(trimmed);
final activeSession = session ??
_authService.currentSession ??
await _authService.loadStoredSession();
if (activeSession == null) return;
final navigator = navigatorKey.currentState;
if (navigator == null) return;
takePendingCode();
if (_scannerOpen) return;
_scannerOpen = true;
try {
await navigator.push(
PageRouteBuilder<void>(
settings: const RouteSettings(name: '/scanner'),
pageBuilder: (context, animation, secondaryAnimation) =>
ScannerScreen(
session: activeSession,
initialCode: trimmed,
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.06),
end: Offset.zero,
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
)),
child: child,
),
);
},
transitionDuration: const Duration(milliseconds: 380),
),
);
} finally {
_scannerOpen = false;
}
} finally {
_handlingScan = false;
}
}
Future<void> openPendingScanAfterAuth(AuthSession session) async {
final code = takePendingCode();
if (code == null) return;
await handleScan(code, session: session);
}
Future<void> navigateToHome(AuthSession session) async {
final navigator = navigatorKey.currentState;
if (navigator == null) return;
await navigator.pushAndRemoveUntil(
PageRouteBuilder<void>(
pageBuilder: (context, animation, secondaryAnimation) =>
HomeScreen(session: session),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(opacity: animation, child: child);
},
transitionDuration: const Duration(milliseconds: 400),
),
(route) => false,
);
await openPendingScanAfterAuth(session);
}
}