From 26db756cfc5cf92acce2b5ebbe02dec5fda9ce77 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 25 Feb 2025 09:59:42 +0100 Subject: [PATCH] Workaround for TelecomManager to prevent incoming audio call to be answered on speaker --- .../org/linphone/telecom/TelecomManager.kt | 124 +++++++++++------- 1 file changed, 73 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/org/linphone/telecom/TelecomManager.kt b/app/src/main/java/org/linphone/telecom/TelecomManager.kt index 4b7923459..0c1bac8ed 100644 --- a/app/src/main/java/org/linphone/telecom/TelecomManager.kt +++ b/app/src/main/java/org/linphone/telecom/TelecomManager.kt @@ -23,6 +23,7 @@ import android.content.Context import android.net.Uri import androidx.annotation.WorkerThread import androidx.core.telecom.CallAttributesCompat +import androidx.core.telecom.CallEndpointCompat import androidx.core.telecom.CallException import androidx.core.telecom.CallsManager import kotlinx.coroutines.CoroutineScope @@ -126,62 +127,83 @@ class TelecomManager scope.launch { try { - val callAttributes = CallAttributesCompat( - displayName, - uri, - direction, - type, - capabilities - ) - Log.i("$TAG Adding call to Telecom's CallsManager with attributes [$callAttributes]") + callsManager.getAvailableStartingCallEndpoints().collect { list -> + var endpoint: CallEndpointCompat? = null + for (device in list) { + if (device.type == CallEndpointCompat.TYPE_BLUETOOTH || device.type == CallEndpointCompat.TYPE_WIRED_HEADSET) { + Log.w("$TAG Bluetooth or wired headset available, do not force endpoint") + // /!\ DO NOT set endpoint to device, for some reason it will trigger an exception, + // leave endpoint to null so it will device which device is best automatically... + break + } - callsManager.addCall( - callAttributes, - { callType -> // onAnswer - Log.i("$TAG We're asked to answer the call with type [$callType]") - coreContext.postOnCoreThread { - if (LinphoneUtils.isCallIncoming(call.state)) { - Log.i("$TAG Answering call") - coreContext.answerCall(call) - } - } - }, - { disconnectCause -> // onDisconnect - Log.i( - "$TAG We're asked to terminate the call with reason [$disconnectCause]" - ) - coreContext.postOnCoreThread { - coreContext.terminateCall(call) - } - currentlyFollowedCalls -= 1 - }, - { // onSetActive - Log.i("$TAG We're asked to resume the call") - coreContext.postOnCoreThread { - Log.i("$TAG Resuming call") - call.resume() - } - }, - { // onSetInactive - Log.i("$TAG We're asked to pause the call") - coreContext.postOnCoreThread { - Log.i("$TAG Pausing call") - call.pause() + if (isVideo && device.type == CallEndpointCompat.TYPE_SPEAKER) { + Log.i("$TAG Call has video enabled and speaker device found, requesting it") + endpoint = device + } else if (!isVideo && device.type == CallEndpointCompat.TYPE_EARPIECE) { + Log.i("$TAG Call is audio only and earpiece device found, requesting it") + endpoint = device } } - ) { - val callbacks = TelecomCallControlCallback(call, this, scope) - // We must first call setCallback on callControlScope before using it - callbacks.onCallControlCallbackSet() - currentlyFollowedCalls += 1 - Log.i("$TAG Call added to Telecom's CallsManager") + val callAttributes = CallAttributesCompat( + displayName, + uri, + direction, + type, + capabilities, + endpoint + ) + Log.i("$TAG Adding call to Telecom's CallsManager with attributes [$callAttributes] and endpoint [$endpoint]") - coreContext.postOnCoreThread { - val callId = call.callLog.callId.orEmpty() - if (callId.isNotEmpty()) { - Log.i("$TAG Storing our callbacks for call ID [$callId]") - map[callId] = callbacks + callsManager.addCall( + callAttributes, + { callType -> // onAnswer + Log.i("$TAG We're asked to answer the call with type [$callType]") + coreContext.postOnCoreThread { + if (LinphoneUtils.isCallIncoming(call.state)) { + Log.i("$TAG Answering call") + coreContext.answerCall(call) + } + } + }, + { disconnectCause -> // onDisconnect + Log.i( + "$TAG We're asked to terminate the call with reason [$disconnectCause]" + ) + coreContext.postOnCoreThread { + coreContext.terminateCall(call) + } + currentlyFollowedCalls -= 1 + }, + { // onSetActive + Log.i("$TAG We're asked to resume the call") + coreContext.postOnCoreThread { + Log.i("$TAG Resuming call") + call.resume() + } + }, + { // onSetInactive + Log.i("$TAG We're asked to pause the call") + coreContext.postOnCoreThread { + Log.i("$TAG Pausing call") + call.pause() + } + } + ) { + val callbacks = TelecomCallControlCallback(call, this, scope) + + // We must first call setCallback on callControlScope before using it + callbacks.onCallControlCallbackSet() + currentlyFollowedCalls += 1 + Log.i("$TAG Call added to Telecom's CallsManager") + + coreContext.postOnCoreThread { + val callId = call.callLog.callId.orEmpty() + if (callId.isNotEmpty()) { + Log.i("$TAG Storing our callbacks for call ID [$callId]") + map[callId] = callbacks + } } } }