api update and flutter update

This commit is contained in:
Wowieee4
2026-05-07 10:22:06 +07:00
parent c5d716248e
commit 53e612e221
25 changed files with 4423 additions and 142 deletions
@@ -0,0 +1,37 @@
import 'package:vibration/vibration.dart';
class HapticService {
Future<bool> get _hasVibrator async => (await Vibration.hasVibrator()) ?? false;
Future<void> obstacleVeryClose() async {
if (!await _hasVibrator) return;
Vibration.vibrate(pattern: [0, 500, 100, 500, 100, 500]);
}
Future<void> obstacleClose() async {
if (!await _hasVibrator) return;
Vibration.vibrate(pattern: [0, 300, 100, 300]);
}
Future<void> obstacleMedium() async {
if (!await _hasVibrator) return;
Vibration.vibrate(duration: 150);
}
Future<void> sosTriggered() async {
if (!await _hasVibrator) return;
Vibration.vibrate(pattern: [0, 1000, 200, 1000, 200, 1000]);
}
Future<void> callIncoming() async {
if (!await _hasVibrator) return;
Vibration.vibrate(pattern: [0, 500, 500, 500, 500, 500, 500, 500]);
}
Future<void> success() async {
if (!await _hasVibrator) return;
Vibration.vibrate(duration: 80);
}
Future<void> stop() async => Vibration.cancel();
}
@@ -0,0 +1,54 @@
import 'package:speech_to_text/speech_to_text.dart';
class SttService {
final SpeechToText _stt = SpeechToText();
bool _available = false;
bool _listening = false;
Function(String)? onResult;
Future<bool> init() async {
_available = await _stt.initialize(
onError: (e) => _onError(e),
onStatus: (s) => _onStatus(s),
);
return _available;
}
Future<void> startListening() async {
if (!_available || _listening) return;
_listening = true;
await _stt.listen(
onResult: (result) {
if (result.finalResult && result.recognizedWords.isNotEmpty) {
onResult?.call(result.recognizedWords.toLowerCase().trim());
}
},
listenFor: const Duration(seconds: 10),
pauseFor: const Duration(seconds: 3),
localeId: 'id_ID',
cancelOnError: false,
);
}
Future<void> stopListening() async {
_listening = false;
await _stt.stop();
}
bool get isListening => _listening;
bool get isAvailable => _available;
void _onError(dynamic error) {
_listening = false;
// Auto-restart setelah error
Future.delayed(const Duration(seconds: 1), startListening);
}
void _onStatus(String status) {
if (status == 'done' || status == 'notListening') {
_listening = false;
// Auto-restart agar selalu mendengarkan
Future.delayed(const Duration(milliseconds: 500), startListening);
}
}
}
@@ -0,0 +1,61 @@
import 'package:flutter_tts/flutter_tts.dart';
class TtsService {
final FlutterTts _tts = FlutterTts();
final List<String> _queue = [];
bool _speaking = false;
String _lastSpoken = '';
Future<void> init({String language = 'id-ID', double pitch = 1.0, double rate = 0.5}) async {
await _tts.setLanguage(language);
await _tts.setPitch(pitch);
await _tts.setSpeechRate(rate);
await _tts.setVolume(1.0);
_tts.setCompletionHandler(() {
_speaking = false;
_processQueue();
});
}
/// Tambah ke antrian - tidak memotong yg sedang bicara
void speak(String text) {
if (text.isEmpty) return;
_queue.add(text);
if (!_speaking) _processQueue();
}
/// Potong TTS sekarang dan langsung ucapkan ini - untuk obstacle alert
Future<void> speakImmediate(String text) async {
if (text.isEmpty) return;
_queue.clear();
await _tts.stop();
_speaking = true;
_lastSpoken = text;
await _tts.speak(text);
}
Future<void> stop() async {
_queue.clear();
_speaking = false;
await _tts.stop();
}
String get lastSpoken => _lastSpoken;
bool get isSpeaking => _speaking;
Future<void> setLanguage(String lang) async => _tts.setLanguage(lang);
Future<void> setPitch(double pitch) async => _tts.setPitch(pitch);
Future<void> setRate(double rate) async => _tts.setSpeechRate(rate);
void repeatLast() {
if (_lastSpoken.isNotEmpty) speak(_lastSpoken);
}
void _processQueue() {
if (_queue.isEmpty || _speaking) return;
final text = _queue.removeAt(0);
_speaking = true;
_lastSpoken = text;
_tts.speak(text);
}
}
@@ -0,0 +1,75 @@
import 'package:get_it/get_it.dart';
import 'tts_service.dart';
import 'stt_service.dart';
enum VoiceCommandKey {
openWalkguide, startWalkguide, stopWalkguide,
callGuardian, openNotification, readAllNotif,
openSos, sendSos, whereAmI,
openActivity, openNavigation, openSettings,
repeatLast, stopTts,
}
class VoiceCommand {
final VoiceCommandKey key;
final String phrase;
final bool enabled;
const VoiceCommand({required this.key, required this.phrase, required this.enabled});
}
/// Callback yang dipanggil saat command terdeteksi
/// Registered oleh router/screen yang relevan
typedef CommandCallback = void Function(VoiceCommandKey key);
class VoiceCommandHandler {
final SttService _stt;
final TtsService _tts;
List<VoiceCommand> _commands = [];
CommandCallback? onCommand;
VoiceCommandHandler(this._stt, this._tts);
void loadCommands(List<VoiceCommand> commands) {
_commands = commands;
_stt.onResult = _processText;
}
void loadDefaultCommands() {
_commands = const [
VoiceCommand(key: VoiceCommandKey.openWalkguide, phrase: 'open walkguide', enabled: true),
VoiceCommand(key: VoiceCommandKey.startWalkguide, phrase: 'start walkguide', enabled: true),
VoiceCommand(key: VoiceCommandKey.stopWalkguide, phrase: 'stop walkguide', enabled: true),
VoiceCommand(key: VoiceCommandKey.callGuardian, phrase: 'call guardian', enabled: true),
VoiceCommand(key: VoiceCommandKey.openNotification, phrase: 'open notifications', enabled: true),
VoiceCommand(key: VoiceCommandKey.readAllNotif, phrase: 'read all my notifications', enabled: true),
VoiceCommand(key: VoiceCommandKey.openSos, phrase: 'open sos', enabled: true),
VoiceCommand(key: VoiceCommandKey.sendSos, phrase: 'send sos', enabled: true),
VoiceCommand(key: VoiceCommandKey.whereAmI, phrase: 'where am i', enabled: true),
VoiceCommand(key: VoiceCommandKey.openActivity, phrase: 'open activity log', enabled: true),
VoiceCommand(key: VoiceCommandKey.openNavigation, phrase: 'open navigation', enabled: true),
VoiceCommand(key: VoiceCommandKey.openSettings, phrase: 'open settings', enabled: true),
VoiceCommand(key: VoiceCommandKey.repeatLast, phrase: 'repeat', enabled: true),
VoiceCommand(key: VoiceCommandKey.stopTts, phrase: 'stop', enabled: true),
];
_stt.onResult = _processText;
}
void _processText(String text) {
final lower = text.toLowerCase().trim();
for (final cmd in _commands) {
if (!cmd.enabled) continue;
if (lower.contains(cmd.phrase.toLowerCase())) {
_handleCommand(cmd.key);
return;
}
}
}
void _handleCommand(VoiceCommandKey key) {
onCommand?.call(key);
// Built-in actions for TTS-only commands
if (key == VoiceCommandKey.repeatLast) _tts.repeatLast();
if (key == VoiceCommandKey.stopTts) _tts.stop();
}
}