diff --git a/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt b/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt index 6f8248669..2f8e161b7 100644 --- a/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt +++ b/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt @@ -49,6 +49,7 @@ class TelecomCallControlCallback( } private var availableEndpoints: List = 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) { + fun isEarpieceAvailable(): Boolean { + return availableEndpoints.find { + it.type == CallEndpointCompat.Companion.TYPE_EARPIECE + } != null + } + + fun applyAudioRouteToCallWithId(routes: List): 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 } } diff --git a/app/src/main/java/org/linphone/telecom/TelecomManager.kt b/app/src/main/java/org/linphone/telecom/TelecomManager.kt index 92320253b..56c85db89 100644 --- a/app/src/main/java/org/linphone/telecom/TelecomManager.kt +++ b/app/src/main/java/org/linphone/telecom/TelecomManager.kt @@ -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 } } diff --git a/app/src/main/java/org/linphone/ui/call/model/AudioDeviceModel.kt b/app/src/main/java/org/linphone/ui/call/model/AudioDeviceModel.kt index 1a436380d..d716b471c 100644 --- a/app/src/main/java/org/linphone/ui/call/model/AudioDeviceModel.kt +++ b/app/src/main/java/org/linphone/ui/call/model/AudioDeviceModel.kt @@ -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 diff --git a/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt b/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt index 610364855..ac7864a6e 100644 --- a/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt +++ b/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt @@ -681,6 +681,9 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() { coreContext.postOnCoreThread { core -> val audioDevices = core.audioDevices val list = arrayListOf() + 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}]") diff --git a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingWaitingRoomViewModel.kt b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingWaitingRoomViewModel.kt index 8629e0fd1..dcca85eed 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingWaitingRoomViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingWaitingRoomViewModel.kt @@ -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}]") diff --git a/app/src/main/res/layout/call_audio_device_list_cell.xml b/app/src/main/res/layout/call_audio_device_list_cell.xml index e734f30dd..4474c0c7a 100644 --- a/app/src/main/res/layout/call_audio_device_list_cell.xml +++ b/app/src/main/res/layout/call_audio_device_list_cell.xml @@ -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"/> \ No newline at end of file