diff --git a/dialer/android/app/src/main/kotlin/com/icing/dialer/services/MyInCallService.kt b/dialer/android/app/src/main/kotlin/com/icing/dialer/services/MyInCallService.kt index f9f71ad..5469c6d 100644 --- a/dialer/android/app/src/main/kotlin/com/icing/dialer/services/MyInCallService.kt +++ b/dialer/android/app/src/main/kotlin/com/icing/dialer/services/MyInCallService.kt @@ -29,13 +29,8 @@ class MyInCallService : InCallService() { fun toggleMute(mute: Boolean): Boolean { return instance?.let { service -> try { - val audioManager = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager - audioManager.isMicrophoneMute = mute - Log.d(TAG, "Set microphone mute state to $mute") - channel?.invokeMethod("audioStateChanged", mapOf( - "muted" to mute, - "speaker" to audioManager.isSpeakerphoneOn - )) + service.setMuted(mute) + Log.d(TAG, "Requested to set call mute state to $mute") true } catch (e: Exception) { Log.e(TAG, "Failed to set mute state: $e") @@ -47,16 +42,12 @@ class MyInCallService : InCallService() { fun toggleSpeaker(speaker: Boolean): Boolean { return instance?.let { service -> try { - val audioManager = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager - audioManager.isSpeakerphoneOn = speaker - Log.d(TAG, "Set speakerphone state to $speaker") - channel?.invokeMethod("audioStateChanged", mapOf( - "muted" to audioManager.isMicrophoneMute, - "speaker" to speaker - )) + val route = if (speaker) CallAudioState.ROUTE_SPEAKER else CallAudioState.ROUTE_EARPIECE + service.setAudioRoute(route) + Log.d(TAG, "Requested to set audio route to $route") true } catch (e: Exception) { - Log.e(TAG, "Failed to set speaker state: $e") + Log.e(TAG, "Failed to set audio route: $e") false } } ?: false @@ -119,6 +110,16 @@ class MyInCallService : InCallService() { showIncomingCallScreen(call.details.handle.toString().replace("tel:", "")) } call.registerCallback(callCallback) + if (callAudioState != null) { + val audioState = callAudioState + channel?.invokeMethod("audioStateChanged", mapOf( + "route" to audioState.route, + "muted" to audioState.isMuted, + "speaker" to (audioState.route == CallAudioState.ROUTE_SPEAKER) + )) + } else { + Log.w("MyInCallService", "callAudioState is null in onCallAdded") + } } override fun onCallRemoved(call: Call) { @@ -134,7 +135,7 @@ class MyInCallService : InCallService() { cancelNotification() } - override fun onCallAudioStateChanged(state: android.telecom.CallAudioState) { + override fun onCallAudioStateChanged(state: CallAudioState) { super.onCallAudioStateChanged(state) Log.d(TAG, "Audio state changed: route=${state.route}, muted=${state.isMuted}") channel?.invokeMethod("audioStateChanged", mapOf( diff --git a/dialer/lib/features/call/call_page.dart b/dialer/lib/features/call/call_page.dart index 54333c7..0b3bdc3 100644 --- a/dialer/lib/features/call/call_page.dart +++ b/dialer/lib/features/call/call_page.dart @@ -33,21 +33,35 @@ class _CallPageState extends State { int _callSeconds = 0; String _callStatus = "Calling..."; StreamSubscription? _callStateSubscription; + StreamSubscription>? _audioStateSubscription; @override void initState() { super.initState(); _checkInitialCallState(); _listenToCallState(); + _listenToAudioState(); + _setInitialAudioState(); } @override void dispose() { _callTimer?.cancel(); _callStateSubscription?.cancel(); + _audioStateSubscription?.cancel(); super.dispose(); } + void _setInitialAudioState() { + final initialAudioState = _callService.currentAudioState; + if (initialAudioState != null) { + setState(() { + isMuted = initialAudioState['muted'] ?? false; + isSpeaker = initialAudioState['speaker'] ?? false; + }); + } + } + void _checkInitialCallState() async { try { final state = await _callService.getCallState(); @@ -82,6 +96,17 @@ class _CallPageState extends State { }); } + void _listenToAudioState() { + _audioStateSubscription = _callService.audioStateStream.listen((state) { + if (mounted) { + setState(() { + isMuted = state['muted'] ?? isMuted; + isSpeaker = state['speaker'] ?? isSpeaker; + }); + } + }); + } + void _startCallTimer() { _callTimer?.cancel(); _callTimer = Timer.periodic(const Duration(seconds: 1), (timer) { @@ -107,18 +132,12 @@ class _CallPageState extends State { print('CallPage: Toggling mute, current state: $isMuted'); final result = await _callService.muteCall(context, mute: !isMuted); print('CallPage: Mute call result: $result'); - if (mounted && result['status'] == 'success') { - setState(() { - isMuted = !isMuted; - }); - } else { + if (mounted && result['status'] != 'success') { print('CallPage: Failed to toggle mute: ${result['message']}'); - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Failed to toggle mute: ${result['message']}')), - ); - } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to toggle mute: ${result['message']}')), + ); } } catch (e) { print('CallPage: Error toggling mute: $e'); @@ -136,11 +155,7 @@ class _CallPageState extends State { final result = await _callService.speakerCall(context, speaker: !isSpeaker); print('CallPage: Speaker call result: $result'); - if (result['status'] == 'success') { - setState(() { - isSpeaker = !isSpeaker; - }); - } else { + if (result['status'] != 'success') { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Failed to toggle speaker: ${result['message']}')), diff --git a/dialer/lib/services/call_service.dart b/dialer/lib/services/call_service.dart index ec52df9..d14073e 100644 --- a/dialer/lib/services/call_service.dart +++ b/dialer/lib/services/call_service.dart @@ -17,10 +17,14 @@ class CallService { static bool _isNavigating = false; final ContactService _contactService = ContactService(); final _callStateController = StreamController.broadcast(); + final _audioStateController = StreamController>.broadcast(); + Map? _currentAudioState; static final GlobalKey navigatorKey = GlobalKey(); Stream get callStateStream => _callStateController.stream; + Stream> get audioStateStream => _audioStateController.stream; + Map? get currentAudioState => _currentAudioState; CallService() { _channel.setMethodCallHandler((call) async { @@ -127,7 +131,16 @@ class CallService { break; case "audioStateChanged": final route = call.arguments["route"] as int?; - print('CallService: Audio state changed, route: $route'); + final muted = call.arguments["muted"] as bool?; + final speaker = call.arguments["speaker"] as bool?; + print('CallService: Audio state changed, route: $route, muted: $muted, speaker: $speaker'); + final audioState = { + "route": route, + "muted": muted, + "speaker": speaker, + }; + _currentAudioState = audioState; + _audioStateController.add(audioState); break; } }); @@ -182,6 +195,7 @@ class CallService { void dispose() { _callStateController.close(); + _audioStateController.close(); } Future _fetchContactInfo(String phoneNumber) async {