import 'dart:async'; import 'package:twilio_voice/twilio_voice.dart'; import 'api_client.dart'; class VoiceService { final ApiClient _api; Timer? _tokenRefreshTimer; String? _identity; final StreamController _callEventController = StreamController.broadcast(); Stream get callEvents => _callEventController.stream; VoiceService(this._api); Future initialize() async { await _fetchAndRegisterToken(); TwilioVoice.instance.callEventsListener.listen((event) { _callEventController.add(event); }); // Refresh token every 50 minutes _tokenRefreshTimer?.cancel(); _tokenRefreshTimer = Timer.periodic( const Duration(minutes: 50), (_) => _fetchAndRegisterToken(), ); } Future _fetchAndRegisterToken() async { try { final response = await _api.dio.get('/voice/token'); final data = response.data; final token = data['token'] as String; _identity = data['identity'] as String; await TwilioVoice.instance.setTokens(accessToken: token); } catch (e) { // Token fetch failed - will retry on next interval } } String? get identity => _identity; Future answer() async { await TwilioVoice.instance.call.answer(); } Future reject() async { await TwilioVoice.instance.call.hangUp(); } Future hangUp() async { await TwilioVoice.instance.call.hangUp(); } Future toggleMute(bool mute) async { await TwilioVoice.instance.call.toggleMute(mute); } Future toggleSpeaker(bool speaker) async { await TwilioVoice.instance.call.toggleSpeaker(speaker); } Future sendDigits(String digits) async { await TwilioVoice.instance.call.sendDigits(digits); } Future holdCall(String callSid) async { await _api.dio.post('/calls/$callSid/hold'); } Future unholdCall(String callSid) async { await _api.dio.post('/calls/$callSid/unhold'); } Future transferCall(String callSid, String target) async { await _api.dio.post('/calls/$callSid/transfer', data: {'target': target}); } void dispose() { _tokenRefreshTimer?.cancel(); _callEventController.close(); } }