For some reason, sometimes Telecom Manager Call Control Callback doesn't lists Earpiece as an available endpoint, disabling it when it happens

This commit is contained in:
Sylvain Berfini 2024-10-02 16:51:45 +02:00
parent e8100e58da
commit 248a06c8be
6 changed files with 44 additions and 13 deletions

View file

@ -49,6 +49,7 @@ class TelecomCallControlCallback(
}
private var availableEndpoints: List<CallEndpointCompat> = arrayListOf()
private var currentEndpoint = CallEndpointCompat.TYPE_UNKNOWN
private val callListener = object : CallListenerStub() {
@WorkerThread
@ -138,6 +139,7 @@ class TelecomCallControlCallback(
else -> null
}
if (route.isNotEmpty()) {
currentEndpoint = endpoint.type
coreContext.postOnCoreThread {
if (!AudioUtils.applyAudioRouteChangeInLinphone(call, route)) {
Log.w("$TAG Failed to apply audio route change, trying again in 200ms")
@ -166,14 +168,21 @@ class TelecomCallControlCallback(
}.launchIn(scope)
}
fun applyAudioRouteToCallWithId(routes: List<AudioDevice.Type>) {
fun isEarpieceAvailable(): Boolean {
return availableEndpoints.find {
it.type == CallEndpointCompat.Companion.TYPE_EARPIECE
} != null
}
fun applyAudioRouteToCallWithId(routes: List<AudioDevice.Type>): Boolean {
Log.i("$TAG Looking for audio endpoint with type [${routes.first()}]")
var wiredHeadsetFound = false
for (endpoint in availableEndpoints) {
Log.i(
"$TAG Found audio endpoint [${endpoint.name}] with type [${endpoint.type}]"
)
val found = when (endpoint.type) {
val matches = when (endpoint.type) {
CallEndpointCompat.Companion.TYPE_EARPIECE -> {
routes.find { it == AudioDevice.Type.Earpiece }
}
@ -184,15 +193,20 @@ class TelecomCallControlCallback(
routes.find { it == AudioDevice.Type.Bluetooth }
}
CallEndpointCompat.Companion.TYPE_WIRED_HEADSET -> {
wiredHeadsetFound = true
routes.find { it == AudioDevice.Type.Headset || it == AudioDevice.Type.Headphones }
}
else -> null
}
if (found != null) {
if (matches != null) {
Log.i(
"$TAG Found matching audio endpoint [${endpoint.name}], trying to use it"
)
if (currentEndpoint == endpoint.type) {
Log.w("$TAG Endpoint already in use, skipping")
continue
}
scope.launch {
Log.i("$TAG Requesting audio endpoint change with [${endpoint.name}]")
@ -213,13 +227,19 @@ class TelecomCallControlCallback(
Log.i(
"$TAG It took [$attempts] attempt(s) to change endpoint audio device..."
)
currentEndpoint = endpoint.type
}
}
break
} else {
Log.w("$TAG No matching audio endpoint found...")
return true
}
}
if (routes.size == 1 && routes[0] == AudioDevice.Type.Earpiece && wiredHeadsetFound) {
Log.e("$TAG User asked for earpiece but endpoint doesn't exists!")
} else {
Log.e("$TAG No matching endpoint found")
}
return false
}
}

View file

@ -192,7 +192,12 @@ class TelecomManager @WorkerThread constructor(context: Context) {
return false
}
callControlCallback.applyAudioRouteToCallWithId(routes)
return true
return callControlCallback.applyAudioRouteToCallWithId(routes)
}
@WorkerThread
fun isEarpieceAvailable(callId: String): Boolean {
val callControlCallback = map[callId]
return callControlCallback?.isEarpieceAvailable() ?: false
}
}

View file

@ -27,6 +27,7 @@ data class AudioDeviceModel @WorkerThread constructor(
val name: String,
val type: AudioDevice.Type,
val isCurrentlySelected: Boolean,
val isEnabled: Boolean,
private val onAudioDeviceSelected: (() -> Unit)? = null
) {
var dismissDialog: (() -> Unit)? = null

View file

@ -681,6 +681,9 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() {
coreContext.postOnCoreThread { core ->
val audioDevices = core.audioDevices
val list = arrayListOf<AudioDeviceModel>()
val isEarpieceAvailable = coreContext.telecomManager.isEarpieceAvailable(
currentCall.callLog.callId ?: ""
)
for (device in audioDevices) {
// Only list output audio devices
if (!device.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) continue
@ -714,7 +717,8 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() {
}
val currentDevice = currentCall.outputAudioDevice
val isCurrentlyInUse = device.type == currentDevice?.type && device.deviceName == currentDevice?.deviceName
val model = AudioDeviceModel(device, name, device.type, isCurrentlyInUse) {
val enabled = if (device.type == AudioDevice.Type.Earpiece) isEarpieceAvailable else true
val model = AudioDeviceModel(device, name, device.type, isCurrentlyInUse, enabled) {
// onSelected
coreContext.postOnCoreThread {
Log.i("$TAG Selected audio device with ID [${device.id}]")

View file

@ -351,7 +351,7 @@ class MeetingWaitingRoomViewModel @UiThread constructor() : GenericViewModel() {
core.outputAudioDevice ?: core.defaultOutputAudioDevice
}
val isCurrentlyInUse = device.type == currentDevice?.type && device.deviceName == currentDevice?.deviceName
val model = AudioDeviceModel(device, name, device.type, isCurrentlyInUse) {
val model = AudioDeviceModel(device, name, device.type, isCurrentlyInUse, true) {
// onSelected
coreContext.postOnCoreThread {
Log.i("$TAG Selected audio device with ID [${device.id}]")

View file

@ -17,14 +17,15 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:enabled="@{model.isEnabled}"
android:text="@{model.name, default=`Speaker`}"
android:textColor="@color/white"
android:textColor="@color/in_call_label_color"
android:gravity="center_vertical"
android:layout_marginBottom="1dp"
android:drawableEnd="@{model.type == Type.Speaker ? @drawable/speaker_high : model.type == Type.Bluetooth || model.type == Type.HearingAid ? @drawable/bluetooth : model.type == Type.Headphones || model.type == Type.Headset ? @drawable/headset : @drawable/ear, default=@drawable/speaker_high}"
android:drawableTint="@color/white"
android:drawableTint="@color/in_call_label_color"
android:checked="@{model.isCurrentlySelected}"
app:useMaterialThemeColors="false"
app:buttonTint="@color/white"/>
app:buttonTint="@color/in_call_label_color"/>
</layout>