diff --git a/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt b/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt index d465f71e5..33094cc28 100644 --- a/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt +++ b/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt @@ -26,13 +26,11 @@ import androidx.core.telecom.CallControlResult import androidx.core.telecom.CallControlScope import androidx.core.telecom.CallEndpointCompat import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences -import org.linphone.core.AudioDevice import org.linphone.core.Call import org.linphone.core.CallListenerStub import org.linphone.core.Reason @@ -143,14 +141,14 @@ class TelecomCallControlCallback( }.launchIn(scope) callControl.currentCallEndpoint.onEach { endpoint -> - var newEndpointToUse = endpoint + // var newEndpointToUse = endpoint if (endpointUpdateRequestFromLinphone) { Log.i("$TAG Linphone requests to use [${endpoint.name}] audio endpoint with type [${endpointTypeToString(endpoint.type)}]") } else { Log.i("$TAG Android requests us to use [${endpoint.name}] audio endpoint with type [${endpointTypeToString(endpoint.type)}]") } - val requestedEndpoint = latestLinphoneRequestedEndpoint + /*val requestedEndpoint = latestLinphoneRequestedEndpoint if (endpointUpdateRequestFromLinphone && requestedEndpoint != null && requestedEndpoint != endpoint) { Log.w("$TAG WARNING: Linphone requested endpoint [${requestedEndpoint.name}] but Telecom Manager notified endpoint [${endpoint.name}], trying to use the one we requested anyway") newEndpointToUse = requestedEndpoint @@ -163,7 +161,13 @@ class TelecomCallControlCallback( Log.w("$TAG Device isn't connected to Android Auto, do not follow system request to change audio endpoint to [${newEndpointToUse.name}] with type [${endpointTypeToString(type)}]") return@onEach } - endpointUpdateRequestFromLinphone = false + endpointUpdateRequestFromLinphone = false*/ + /*val type = endpoint.type + if (!endpointUpdateRequestFromLinphone && !coreContext.isConnectedToAndroidAuto && (type == CallEndpointCompat.Companion.TYPE_EARPIECE || type == CallEndpointCompat.Companion.TYPE_SPEAKER)) { + endpointUpdateRequestFromLinphone = false + Log.w("$TAG Device isn't connected to Android Auto, do not follow system request to change audio endpoint to [${endpoint.name}] with type [${endpointTypeToString(type)}]") + return@onEach + } // Change audio route in SDK, this way the usual listener will trigger // and we'll be able to update the UI accordingly @@ -186,6 +190,7 @@ class TelecomCallControlCallback( } if (route.isNotEmpty()) { coreContext.postOnCoreThread { + Log.i("$TAG Requesting Linphone to change audio route to [$route]([${endpointTypeToString(endpoint.type)}])") if (!AudioUtils.applyAudioRouteChangeInLinphone(call, route)) { Log.w("$TAG Failed to apply audio route change, trying again in 200ms") coreContext.postOnCoreThreadDelayed({ @@ -193,7 +198,7 @@ class TelecomCallControlCallback( }, 200) } } - } + }*/ }.launchIn(scope) callControl.isMuted.onEach { muted -> @@ -224,7 +229,7 @@ class TelecomCallControlCallback( }.launchIn(scope) } - fun applyAudioRouteToCallWithId(routes: List): Boolean { + /*fun applyAudioRouteToCallWithId(routes: List): Boolean { Log.i("$TAG Looking for audio endpoint with type [${routes.first()}]") var wiredHeadsetFound = false @@ -260,13 +265,14 @@ class TelecomCallControlCallback( continue } + var endpointChangeSuccessful = false scope.launch { Log.i("$TAG Requesting audio endpoint change to [${endpoint.name}] with type [${endpointTypeToString(endpoint.type)}]") endpointUpdateRequestFromLinphone = true latestLinphoneRequestedEndpoint = endpoint var result: CallControlResult = callControl.requestEndpointChange(endpoint) var attempts = 1 - while (result is CallControlResult.Error && attempts <= 10) { + while (result is CallControlResult.Error && attempts <= 2) { delay(100) Log.i( "$TAG Previous attempt failed [$result], requesting again audio endpoint change to [${endpoint.name}] with type [${endpointTypeToString(endpoint.type)}]" @@ -282,10 +288,11 @@ class TelecomCallControlCallback( "$TAG It took [$attempts] attempt(s) to change endpoint audio device..." ) currentEndpoint = endpoint.type + endpointChangeSuccessful = true } } - return true + return endpointChangeSuccessful } } @@ -297,7 +304,7 @@ class TelecomCallControlCallback( Log.e("$TAG No matching endpoint found") } return false - } + }*/ private fun answerCall() { val isVideo = LinphoneUtils.isVideoEnabled(call) diff --git a/app/src/main/java/org/linphone/telecom/TelecomManager.kt b/app/src/main/java/org/linphone/telecom/TelecomManager.kt index f71b43fe8..f2b9c129b 100644 --- a/app/src/main/java/org/linphone/telecom/TelecomManager.kt +++ b/app/src/main/java/org/linphone/telecom/TelecomManager.kt @@ -20,6 +20,8 @@ package org.linphone.telecom import android.content.Context +import android.media.AudioDeviceInfo +import android.media.AudioManager import androidx.annotation.WorkerThread import androidx.core.telecom.CallAttributesCompat import androidx.core.telecom.CallException @@ -29,13 +31,14 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import org.linphone.LinphoneApplication.Companion.coreContext -import org.linphone.core.AudioDevice import org.linphone.core.Call import org.linphone.core.Core import org.linphone.core.CoreListenerStub import org.linphone.core.tools.Log import org.linphone.utils.LinphoneUtils import androidx.core.net.toUri +import org.linphone.core.AudioDevice +import org.linphone.utils.AudioUtils class TelecomManager @WorkerThread @@ -66,6 +69,9 @@ class TelecomManager @WorkerThread override fun onLastCallEnded(core: Core) { currentlyFollowedCalls = 0 + val audioManager = coreContext.context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + Log.i("$TAG Clearing communication device") + audioManager.clearCommunicationDevice() } } @@ -87,6 +93,18 @@ class TelecomManager } catch (e: Exception) { Log.e("$TAG Can't init TelecomManager: $e") } + + val listener = + AudioManager.OnCommunicationDeviceChangedListener { device -> // Handle changes + Log.i("$TAG Communication device changed: $device") + if (device?.type != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) { + AudioUtils.applyAudioRouteChangeInLinphone(null, listOf(AudioDevice.Type.Speaker), true) + } else { + AudioUtils.applyAudioRouteChangeInLinphone(null, listOf(AudioDevice.Type.Bluetooth), true) + } + } + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + audioManager.addOnCommunicationDeviceChangedListener(context.mainExecutor, listener) } @WorkerThread @@ -214,12 +232,33 @@ class TelecomManager Log.i( "$TAG Looking for audio endpoint with type [${routes.first()}] for call with ID [$callId]" ) - val callControlCallback = map[callId] + /*val callControlCallback = map[callId] if (callControlCallback == null) { Log.w("$TAG Failed to find callbacks for call with ID [$callId]") return false } - return callControlCallback.applyAudioRouteToCallWithId(routes) + return callControlCallback.applyAudioRouteToCallWithId(routes)*/ + val audioManager = coreContext.context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + if (AudioDevice.Type.Bluetooth in routes) { + val devices = audioManager.availableCommunicationDevices + for (device in devices) { + if (device.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO && device.isSink) { + Log.i("$TAG [Telecom] Setting communication device: $device") + audioManager.setCommunicationDevice(device) + return true + } + } + } else { + val devices = audioManager.availableCommunicationDevices + for (device in devices) { + if (device.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && device.isSink) { + Log.i("$TAG [Telecom] Setting communication device: $device") + audioManager.setCommunicationDevice(device) + return true + } + } + } + return false } } diff --git a/app/src/main/java/org/linphone/utils/AudioUtils.kt b/app/src/main/java/org/linphone/utils/AudioUtils.kt index 3ac0268cf..e4d9d0d1a 100644 --- a/app/src/main/java/org/linphone/utils/AudioUtils.kt +++ b/app/src/main/java/org/linphone/utils/AudioUtils.kt @@ -95,6 +95,26 @@ class AudioUtils { null } + /*val audioManager = coreContext.context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + if (AudioDevice.Type.Bluetooth in types) { + val devices = audioManager.availableCommunicationDevices + for (device in devices) { + if (device.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO && device.isSink) { + Log.i("$TAG [Telecom] Setting communication device: $device") + audioManager.setCommunicationDevice(device) + } + } + } else { + val devices = audioManager.availableCommunicationDevices + for (device in devices) { + if (device.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && device.isSink) { + Log.i("$TAG [Telecom] Setting communication device: $device") + audioManager.setCommunicationDevice(device) + } + } + }*/ + + // applyAudioRouteChangeInLinphone(currentCall, types, output) if (!skipTelecom) { val callId = currentCall?.callLog?.callId.orEmpty() Log.i("$TAG Trying to change audio endpoint using Telecom Manager APIs")