Workaround for TelecomManager to prevent incoming audio call to be answered on speaker

This commit is contained in:
Sylvain Berfini 2025-02-25 09:59:42 +01:00
parent f1b23337e0
commit 26db756cfc

View file

@ -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
}
}
}
}