Fix local dev config and ALOTTTT OFF FLUTTER, im tired boss..

This commit is contained in:
Wowieee4
2026-05-26 21:09:13 +07:00
parent b8ad8df993
commit a629357e8c
72 changed files with 2566 additions and 530 deletions
@@ -4,6 +4,9 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../app/injection_container.dart';
import '../../core/errors/friendly_error.dart';
import '../../core/network/api_client.dart';
import '../../core/services/hardware_shortcut_listener.dart';
import '../../core/services/stt_service.dart';
import '../../core/services/tts_service.dart';
import '../../core/services/voice_command_handler.dart';
@@ -20,6 +23,8 @@ class _UserShellState extends State<UserShell> {
@override
void initState() {
super.initState();
_loadVoiceCommands();
_startHardwareShortcuts();
sl<SttService>().startListening();
sl<VoiceCommandHandler>().onCommand = (key) {
if (!mounted) return;
@@ -58,6 +63,70 @@ class _UserShellState extends State<UserShell> {
};
}
Future<void> _loadVoiceCommands() async {
await runFriendlyAction(
() async {
final res = await sl<ApiClient>()
.dio
.get('/user/voice-commands')
.timeout(const Duration(seconds: 8));
final body = res.data;
final data = body is Map ? body['data'] : body;
if (data is! List) return;
final commands = data
.whereType<Map>()
.map((item) => _voiceCommandFromJson(Map<String, dynamic>.from(item)))
.whereType<VoiceCommand>()
.toList();
if (commands.isNotEmpty) {
sl<VoiceCommandHandler>().loadCommands(commands);
}
},
onError: (_) {},
fallback: 'Voice command belum bisa dimuat.',
);
}
Future<void> _startHardwareShortcuts() async {
await runFriendlyAction(
() => sl<HardwareShortcutListener>().startListening(
onAction: (action) {
if (!mounted) return;
switch (action) {
case HardwareShortcutAction.callGuardian:
context.go('/user/call');
sl<TtsService>().speak('Memanggil guardian');
break;
case HardwareShortcutAction.startWalkguide:
context.go('/user/walkguide');
sl<TtsService>().speak('WalkGuide dibuka');
break;
case HardwareShortcutAction.stopWalkguide:
context.go('/user/walkguide');
sl<TtsService>().speak('WalkGuide dibuka untuk dihentikan');
break;
case HardwareShortcutAction.sendSos:
context.go('/user/sos');
sl<TtsService>().speak('SOS dibuka');
break;
case HardwareShortcutAction.openNotification:
context.go('/user/notifications');
sl<TtsService>().speak('Notifikasi dibuka');
break;
}
},
),
onError: (_) {},
fallback: 'Hardware shortcut belum bisa dimuat.',
);
}
@override
void dispose() {
sl<HardwareShortcutListener>().stopListening();
super.dispose();
}
@override
Widget build(BuildContext context) {
final location = GoRouterState.of(context).matchedLocation;
@@ -169,3 +238,49 @@ String _spokenRouteName(VoiceCommandKey key) {
return '';
}
}
VoiceCommand? _voiceCommandFromJson(Map<String, dynamic> item) {
final key = _commandKeyFromBackend(item['commandKey']?.toString());
final phrase = item['triggerPhrase']?.toString().trim();
if (key == null || phrase == null || phrase.isEmpty) return null;
return VoiceCommand(
key: key,
phrase: phrase,
enabled: item['enabled'] != false,
);
}
VoiceCommandKey? _commandKeyFromBackend(String? key) {
switch (key) {
case 'OPEN_WALKGUIDE':
return VoiceCommandKey.openWalkguide;
case 'START_WALKGUIDE':
return VoiceCommandKey.startWalkguide;
case 'STOP_WALKGUIDE':
return VoiceCommandKey.stopWalkguide;
case 'CALL_GUARDIAN':
return VoiceCommandKey.callGuardian;
case 'OPEN_NOTIFICATION':
return VoiceCommandKey.openNotification;
case 'READ_ALL_NOTIF':
return VoiceCommandKey.readAllNotif;
case 'OPEN_SOS':
return VoiceCommandKey.openSos;
case 'SEND_SOS':
return VoiceCommandKey.sendSos;
case 'WHERE_AM_I':
return VoiceCommandKey.whereAmI;
case 'OPEN_ACTIVITY':
return VoiceCommandKey.openActivity;
case 'OPEN_NAVIGATION':
return VoiceCommandKey.openNavigation;
case 'OPEN_SETTINGS':
return VoiceCommandKey.openSettings;
case 'REPEAT_LAST':
return VoiceCommandKey.repeatLast;
case 'STOP_TTS':
return VoiceCommandKey.stopTts;
default:
return null;
}
}