fix: mute and speaker options working in callPage
All checks were successful
/ mirror (push) Successful in 5s
/ build (push) Successful in 10m16s
/ build-stealth (push) Successful in 10m19s

This commit is contained in:
Florian Griffon 2025-04-28 18:51:35 +03:00
parent 936250829b
commit 81187f4384
3 changed files with 63 additions and 33 deletions

View File

@ -29,13 +29,8 @@ class MyInCallService : InCallService() {
fun toggleMute(mute: Boolean): Boolean { fun toggleMute(mute: Boolean): Boolean {
return instance?.let { service -> return instance?.let { service ->
try { try {
val audioManager = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager service.setMuted(mute)
audioManager.isMicrophoneMute = mute Log.d(TAG, "Requested to set call mute state to $mute")
Log.d(TAG, "Set microphone mute state to $mute")
channel?.invokeMethod("audioStateChanged", mapOf(
"muted" to mute,
"speaker" to audioManager.isSpeakerphoneOn
))
true true
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to set mute state: $e") Log.e(TAG, "Failed to set mute state: $e")
@ -47,16 +42,12 @@ class MyInCallService : InCallService() {
fun toggleSpeaker(speaker: Boolean): Boolean { fun toggleSpeaker(speaker: Boolean): Boolean {
return instance?.let { service -> return instance?.let { service ->
try { try {
val audioManager = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager val route = if (speaker) CallAudioState.ROUTE_SPEAKER else CallAudioState.ROUTE_EARPIECE
audioManager.isSpeakerphoneOn = speaker service.setAudioRoute(route)
Log.d(TAG, "Set speakerphone state to $speaker") Log.d(TAG, "Requested to set audio route to $route")
channel?.invokeMethod("audioStateChanged", mapOf(
"muted" to audioManager.isMicrophoneMute,
"speaker" to speaker
))
true true
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to set speaker state: $e") Log.e(TAG, "Failed to set audio route: $e")
false false
} }
} ?: false } ?: false
@ -119,6 +110,16 @@ class MyInCallService : InCallService() {
showIncomingCallScreen(call.details.handle.toString().replace("tel:", "")) showIncomingCallScreen(call.details.handle.toString().replace("tel:", ""))
} }
call.registerCallback(callCallback) 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) { override fun onCallRemoved(call: Call) {
@ -134,7 +135,7 @@ class MyInCallService : InCallService() {
cancelNotification() cancelNotification()
} }
override fun onCallAudioStateChanged(state: android.telecom.CallAudioState) { override fun onCallAudioStateChanged(state: CallAudioState) {
super.onCallAudioStateChanged(state) super.onCallAudioStateChanged(state)
Log.d(TAG, "Audio state changed: route=${state.route}, muted=${state.isMuted}") Log.d(TAG, "Audio state changed: route=${state.route}, muted=${state.isMuted}")
channel?.invokeMethod("audioStateChanged", mapOf( channel?.invokeMethod("audioStateChanged", mapOf(

View File

@ -33,21 +33,35 @@ class _CallPageState extends State<CallPage> {
int _callSeconds = 0; int _callSeconds = 0;
String _callStatus = "Calling..."; String _callStatus = "Calling...";
StreamSubscription<String>? _callStateSubscription; StreamSubscription<String>? _callStateSubscription;
StreamSubscription<Map<String, dynamic>>? _audioStateSubscription;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_checkInitialCallState(); _checkInitialCallState();
_listenToCallState(); _listenToCallState();
_listenToAudioState();
_setInitialAudioState();
} }
@override @override
void dispose() { void dispose() {
_callTimer?.cancel(); _callTimer?.cancel();
_callStateSubscription?.cancel(); _callStateSubscription?.cancel();
_audioStateSubscription?.cancel();
super.dispose(); super.dispose();
} }
void _setInitialAudioState() {
final initialAudioState = _callService.currentAudioState;
if (initialAudioState != null) {
setState(() {
isMuted = initialAudioState['muted'] ?? false;
isSpeaker = initialAudioState['speaker'] ?? false;
});
}
}
void _checkInitialCallState() async { void _checkInitialCallState() async {
try { try {
final state = await _callService.getCallState(); final state = await _callService.getCallState();
@ -82,6 +96,17 @@ class _CallPageState extends State<CallPage> {
}); });
} }
void _listenToAudioState() {
_audioStateSubscription = _callService.audioStateStream.listen((state) {
if (mounted) {
setState(() {
isMuted = state['muted'] ?? isMuted;
isSpeaker = state['speaker'] ?? isSpeaker;
});
}
});
}
void _startCallTimer() { void _startCallTimer() {
_callTimer?.cancel(); _callTimer?.cancel();
_callTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _callTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
@ -107,19 +132,13 @@ class _CallPageState extends State<CallPage> {
print('CallPage: Toggling mute, current state: $isMuted'); print('CallPage: Toggling mute, current state: $isMuted');
final result = await _callService.muteCall(context, mute: !isMuted); final result = await _callService.muteCall(context, mute: !isMuted);
print('CallPage: Mute call result: $result'); print('CallPage: Mute call result: $result');
if (mounted && result['status'] == 'success') { if (mounted && result['status'] != 'success') {
setState(() {
isMuted = !isMuted;
});
} else {
print('CallPage: Failed to toggle mute: ${result['message']}'); print('CallPage: Failed to toggle mute: ${result['message']}');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Failed to toggle mute: ${result['message']}')), content: Text('Failed to toggle mute: ${result['message']}')),
); );
} }
}
} catch (e) { } catch (e) {
print('CallPage: Error toggling mute: $e'); print('CallPage: Error toggling mute: $e');
if (mounted) { if (mounted) {
@ -136,11 +155,7 @@ class _CallPageState extends State<CallPage> {
final result = final result =
await _callService.speakerCall(context, speaker: !isSpeaker); await _callService.speakerCall(context, speaker: !isSpeaker);
print('CallPage: Speaker call result: $result'); print('CallPage: Speaker call result: $result');
if (result['status'] == 'success') { if (result['status'] != 'success') {
setState(() {
isSpeaker = !isSpeaker;
});
} else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Failed to toggle speaker: ${result['message']}')), content: Text('Failed to toggle speaker: ${result['message']}')),

View File

@ -17,10 +17,14 @@ class CallService {
static bool _isNavigating = false; static bool _isNavigating = false;
final ContactService _contactService = ContactService(); final ContactService _contactService = ContactService();
final _callStateController = StreamController<String>.broadcast(); final _callStateController = StreamController<String>.broadcast();
final _audioStateController = StreamController<Map<String, dynamic>>.broadcast();
Map<String, dynamic>? _currentAudioState;
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
Stream<String> get callStateStream => _callStateController.stream; Stream<String> get callStateStream => _callStateController.stream;
Stream<Map<String, dynamic>> get audioStateStream => _audioStateController.stream;
Map<String, dynamic>? get currentAudioState => _currentAudioState;
CallService() { CallService() {
_channel.setMethodCallHandler((call) async { _channel.setMethodCallHandler((call) async {
@ -127,7 +131,16 @@ class CallService {
break; break;
case "audioStateChanged": case "audioStateChanged":
final route = call.arguments["route"] as int?; 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; break;
} }
}); });
@ -182,6 +195,7 @@ class CallService {
void dispose() { void dispose() {
_callStateController.close(); _callStateController.close();
_audioStateController.close();
} }
Future<void> _fetchContactInfo(String phoneNumber) async { Future<void> _fetchContactInfo(String phoneNumber) async {