From 7172d7cf60d1a4df669569e04d6d8e121e6ad3ab Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 25 Mar 2021 11:58:08 +0100 Subject: [PATCH] Reworked audio route handling code and added auto switch to bluetooth if available and to speaker when video is enabled --- .../call/viewmodels/ControlsViewModel.kt | 62 ++++----- .../activities/main/about/AboutViewModel.kt | 2 +- .../main/adapters}/SelectionListAdapter.kt | 2 +- .../chat/adapters/ChatMessagesListAdapter.kt | 2 +- .../chat/adapters/ChatRoomsListAdapter.kt | 2 +- .../contact/adapters/ContactsListAdapter.kt | 2 +- .../main/dialer/viewmodels/DialerViewModel.kt | 2 +- .../main/fragments/MasterFragment.kt | 2 +- .../history/adapters/CallLogsListAdapter.kt | 1 + .../adapters/RecordingsListAdapter.kt | 1 + .../main/viewmodels}/LogsUploadViewModel.kt | 3 +- .../java/org/linphone/core/CoreContext.kt | 38 +++--- .../java/org/linphone/core/CorePreferences.kt | 13 ++ .../org/linphone/utils/AudioRouteUtils.kt | 118 ++++++++++++++++++ .../res/layout/call_secondary_buttons.xml | 2 +- 15 files changed, 186 insertions(+), 66 deletions(-) rename app/src/main/java/org/linphone/{utils => activities/main/adapters}/SelectionListAdapter.kt (97%) rename app/src/main/java/org/linphone/{utils => activities/main/viewmodels}/LogsUploadViewModel.kt (96%) create mode 100644 app/src/main/java/org/linphone/utils/AudioRouteUtils.kt diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsViewModel.kt index bf77b7617..51d11d06d 100644 --- a/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsViewModel.kt +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsViewModel.kt @@ -36,6 +36,7 @@ import org.linphone.compatibility.Compatibility import org.linphone.core.* import org.linphone.core.tools.Log import org.linphone.utils.AppUtils +import org.linphone.utils.AudioRouteUtils import org.linphone.utils.Event import org.linphone.utils.PermissionHelper @@ -68,7 +69,7 @@ class ControlsViewModel : ViewModel() { val optionsVisibility = MutableLiveData() - val audioRoutesVisibility = MutableLiveData() + val audioRoutesSelected = MutableLiveData() val audioRoutesEnabled = MutableLiveData() @@ -170,7 +171,9 @@ class ControlsViewModel : ViewModel() { state: Call.State, message: String ) { - if (state == Call.State.StreamsRunning) isVideoUpdateInProgress.value = false + if (state == Call.State.StreamsRunning) { + isVideoUpdateInProgress.value = false + } if (coreContext.isVideoCallOrConferenceActive() && !PermissionHelper.get().hasCameraPermission()) { askPermissionEvent.value = Event(Manifest.permission.CAMERA) @@ -186,9 +189,14 @@ class ControlsViewModel : ViewModel() { } override fun onAudioDevicesListUpdated(core: Core) { - if (core.callsNb == 0) return + Log.i("[Call] Audio devices list updated") + val wasBluetoothPreviouslyAvailable = audioRoutesEnabled.value == true updateAudioRoutesState() - coreContext.routeAudioToBluetoothIfAvailable(core.currentCall ?: core.calls[0]) + + if (!wasBluetoothPreviouslyAvailable && corePreferences.routeAudioToBluetoothIfAvailable) { + // Only attempt to route audio to bluetooth automatically when bluetooth device is connected + AudioRouteUtils.routeAudioToBluetooth() + } } } @@ -202,7 +210,7 @@ class ControlsViewModel : ViewModel() { numpadVisibility.value = false optionsVisibility.value = false - audioRoutesVisibility.value = false + audioRoutesSelected.value = false isRecording.value = currentCall?.isRecording isVideoUpdateInProgress.value = false @@ -309,8 +317,8 @@ class ControlsViewModel : ViewModel() { fun toggleRoutesMenu() { somethingClickedEvent.value = Event(true) - audioRoutesVisibility.value = audioRoutesVisibility.value != true - if (audioRoutesVisibility.value == true) { + audioRoutesSelected.value = audioRoutesSelected.value != true + if (audioRoutesSelected.value == true) { audioRoutesMenuAnimator.start() } else { audioRoutesMenuAnimator.reverse() @@ -365,38 +373,17 @@ class ControlsViewModel : ViewModel() { fun forceEarpieceAudioRoute() { somethingClickedEvent.value = Event(true) - for (audioDevice in coreContext.core.audioDevices) { - if (audioDevice.type == AudioDevice.Type.Earpiece) { - Log.i("[Call] Found earpiece audio device [${audioDevice.deviceName}], routing audio to it") - coreContext.core.outputAudioDevice = audioDevice - return - } - } - Log.e("[Call] Couldn't find earpiece audio device") + AudioRouteUtils.routeAudioToEarpiece() } fun forceSpeakerAudioRoute() { somethingClickedEvent.value = Event(true) - for (audioDevice in coreContext.core.audioDevices) { - if (audioDevice.type == AudioDevice.Type.Speaker) { - Log.i("[Call] Found speaker audio device [${audioDevice.deviceName}], routing audio to it") - coreContext.core.outputAudioDevice = audioDevice - return - } - } - Log.e("[Call] Couldn't find speaker audio device") + AudioRouteUtils.routeAudioToSpeaker() } fun forceBluetoothAudioRoute() { somethingClickedEvent.value = Event(true) - for (audioDevice in coreContext.core.audioDevices) { - if ((audioDevice.type == AudioDevice.Type.Bluetooth) && audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) { - Log.i("[Call] Found bluetooth audio device [${audioDevice.deviceName}], routing audio to it") - coreContext.core.outputAudioDevice = audioDevice - return - } - } - Log.e("[Call] Couldn't find bluetooth audio device") + AudioRouteUtils.routeAudioToBluetooth() } private fun updateAudioRelated() { @@ -425,21 +412,16 @@ class ControlsViewModel : ViewModel() { } private fun updateAudioRoutesState() { - var bluetoothDeviceAvailable = false - for (audioDevice in coreContext.core.audioDevices) { - if (audioDevice.type == AudioDevice.Type.Bluetooth) { - bluetoothDeviceAvailable = true - break - } - } + val bluetoothDeviceAvailable = AudioRouteUtils.isBluetoothAudioRouteAvailable() audioRoutesEnabled.value = bluetoothDeviceAvailable if (!bluetoothDeviceAvailable) { - audioRoutesVisibility.value = false + audioRoutesSelected.value = false } } private fun updateBluetoothHeadsetState() { - val audioDevice = coreContext.core.outputAudioDevice + if (coreContext.core.callsNb == 0) return + val audioDevice = (coreContext.core.currentCall ?: coreContext.core.calls[0]).outputAudioDevice isBluetoothHeadsetSelected.value = audioDevice?.type == AudioDevice.Type.Bluetooth } diff --git a/app/src/main/java/org/linphone/activities/main/about/AboutViewModel.kt b/app/src/main/java/org/linphone/activities/main/about/AboutViewModel.kt index e047a59af..b773cc975 100644 --- a/app/src/main/java/org/linphone/activities/main/about/AboutViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/about/AboutViewModel.kt @@ -21,7 +21,7 @@ package org.linphone.activities.main.about import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences -import org.linphone.utils.LogsUploadViewModel +import org.linphone.activities.main.viewmodels.LogsUploadViewModel class AboutViewModel : LogsUploadViewModel() { val appVersion: String = coreContext.appVersion diff --git a/app/src/main/java/org/linphone/utils/SelectionListAdapter.kt b/app/src/main/java/org/linphone/activities/main/adapters/SelectionListAdapter.kt similarity index 97% rename from app/src/main/java/org/linphone/utils/SelectionListAdapter.kt rename to app/src/main/java/org/linphone/activities/main/adapters/SelectionListAdapter.kt index b7a460532..e02070b29 100644 --- a/app/src/main/java/org/linphone/utils/SelectionListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/adapters/SelectionListAdapter.kt @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.linphone.utils +package org.linphone.activities.main.adapters import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter diff --git a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt index be36a07f2..d7c0638f2 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt @@ -33,6 +33,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R +import org.linphone.activities.main.adapters.SelectionListAdapter import org.linphone.activities.main.chat.viewmodels.ChatMessageViewModel import org.linphone.activities.main.chat.viewmodels.EventViewModel import org.linphone.activities.main.chat.viewmodels.OnContentClickedListener @@ -43,7 +44,6 @@ import org.linphone.core.EventLog import org.linphone.databinding.ChatEventListCellBinding import org.linphone.databinding.ChatMessageListCellBinding import org.linphone.utils.Event -import org.linphone.utils.SelectionListAdapter class ChatMessagesListAdapter( selectionVM: ListTopBarViewModel, diff --git a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomsListAdapter.kt b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomsListAdapter.kt index 1aa34c817..d270314ca 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomsListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomsListAdapter.kt @@ -27,12 +27,12 @@ import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import org.linphone.R +import org.linphone.activities.main.adapters.SelectionListAdapter import org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.core.ChatRoom import org.linphone.databinding.ChatRoomListCellBinding import org.linphone.utils.Event -import org.linphone.utils.SelectionListAdapter class ChatRoomsListAdapter( selectionVM: ListTopBarViewModel, diff --git a/app/src/main/java/org/linphone/activities/main/contact/adapters/ContactsListAdapter.kt b/app/src/main/java/org/linphone/activities/main/contact/adapters/ContactsListAdapter.kt index 8654da23c..c5a734d25 100644 --- a/app/src/main/java/org/linphone/activities/main/contact/adapters/ContactsListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/contact/adapters/ContactsListAdapter.kt @@ -29,6 +29,7 @@ import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import org.linphone.R +import org.linphone.activities.main.adapters.SelectionListAdapter import org.linphone.activities.main.contact.viewmodels.ContactViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.contact.Contact @@ -37,7 +38,6 @@ import org.linphone.databinding.GenericListHeaderBinding import org.linphone.utils.AppUtils import org.linphone.utils.Event import org.linphone.utils.HeaderAdapter -import org.linphone.utils.SelectionListAdapter class ContactsListAdapter( selectionVM: ListTopBarViewModel, diff --git a/app/src/main/java/org/linphone/activities/main/dialer/viewmodels/DialerViewModel.kt b/app/src/main/java/org/linphone/activities/main/dialer/viewmodels/DialerViewModel.kt index 2347bc969..072c4cc37 100644 --- a/app/src/main/java/org/linphone/activities/main/dialer/viewmodels/DialerViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/dialer/viewmodels/DialerViewModel.kt @@ -27,12 +27,12 @@ import androidx.lifecycle.MutableLiveData import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.activities.main.dialer.NumpadDigitListener +import org.linphone.activities.main.viewmodels.LogsUploadViewModel import org.linphone.compatibility.Compatibility import org.linphone.core.* import org.linphone.core.tools.Log import org.linphone.utils.Event import org.linphone.utils.LinphoneUtils -import org.linphone.utils.LogsUploadViewModel class DialerViewModel : LogsUploadViewModel() { val enteredUri = MutableLiveData() diff --git a/app/src/main/java/org/linphone/activities/main/fragments/MasterFragment.kt b/app/src/main/java/org/linphone/activities/main/fragments/MasterFragment.kt index 23f7c91db..4fddaa3f1 100644 --- a/app/src/main/java/org/linphone/activities/main/fragments/MasterFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/fragments/MasterFragment.kt @@ -28,12 +28,12 @@ import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import org.linphone.LinphoneApplication import org.linphone.R +import org.linphone.activities.main.adapters.SelectionListAdapter import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.core.tools.Log import org.linphone.utils.AppUtils import org.linphone.utils.DialogUtils -import org.linphone.utils.SelectionListAdapter /** * This fragment can be inherited by all fragments that will display a list diff --git a/app/src/main/java/org/linphone/activities/main/history/adapters/CallLogsListAdapter.kt b/app/src/main/java/org/linphone/activities/main/history/adapters/CallLogsListAdapter.kt index d18047c33..f1ace7721 100644 --- a/app/src/main/java/org/linphone/activities/main/history/adapters/CallLogsListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/history/adapters/CallLogsListAdapter.kt @@ -29,6 +29,7 @@ import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import org.linphone.R +import org.linphone.activities.main.adapters.SelectionListAdapter import org.linphone.activities.main.history.viewmodels.CallLogViewModel import org.linphone.activities.main.history.viewmodels.GroupedCallLogViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel diff --git a/app/src/main/java/org/linphone/activities/main/recordings/adapters/RecordingsListAdapter.kt b/app/src/main/java/org/linphone/activities/main/recordings/adapters/RecordingsListAdapter.kt index cd457cccf..f632b4770 100644 --- a/app/src/main/java/org/linphone/activities/main/recordings/adapters/RecordingsListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/recordings/adapters/RecordingsListAdapter.kt @@ -30,6 +30,7 @@ import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import org.linphone.R +import org.linphone.activities.main.adapters.SelectionListAdapter import org.linphone.activities.main.recordings.viewmodels.RecordingViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.databinding.GenericListHeaderBinding diff --git a/app/src/main/java/org/linphone/utils/LogsUploadViewModel.kt b/app/src/main/java/org/linphone/activities/main/viewmodels/LogsUploadViewModel.kt similarity index 96% rename from app/src/main/java/org/linphone/utils/LogsUploadViewModel.kt rename to app/src/main/java/org/linphone/activities/main/viewmodels/LogsUploadViewModel.kt index 519539c2f..9216d923b 100644 --- a/app/src/main/java/org/linphone/utils/LogsUploadViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/viewmodels/LogsUploadViewModel.kt @@ -18,13 +18,14 @@ * along with this program. If not, see . */ -package org.linphone.utils +package org.linphone.activities.main.viewmodels import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.core.Core import org.linphone.core.CoreListenerStub +import org.linphone.utils.Event open class LogsUploadViewModel : ViewModel() { val uploadInProgress = MutableLiveData() diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index 1bf0a2ff7..4a417bca4 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -47,6 +47,7 @@ import org.linphone.core.tools.Log import org.linphone.mediastream.Version import org.linphone.notifications.NotificationsManager import org.linphone.utils.AppUtils +import org.linphone.utils.AudioRouteUtils import org.linphone.utils.Event import org.linphone.utils.LinphoneUtils @@ -110,6 +111,7 @@ class CoreContext(val context: Context, coreConfig: Config) { private var overlayY = 0f private var callOverlay: View? = null private var isVibrating = false + private var previousCallState = Call.State.Idle private val listener: CoreListenerStub = object : CoreListenerStub() { override fun onGlobalStateChanged(core: Core, state: GlobalState, message: String) { @@ -169,8 +171,8 @@ class CoreContext(val context: Context, coreConfig: Config) { } else if (state == Call.State.OutgoingInit) { onOutgoingStarted() } else if (state == Call.State.OutgoingProgress) { - if (core.callsNb == 1) { - routeAudioToBluetoothIfAvailable(call) + if (core.callsNb == 1 && corePreferences.routeAudioToBluetoothIfAvailable) { + AudioRouteUtils.routeAudioToBluetooth(call) } } else if (state == Call.State.Connected) { if (isVibrating) { @@ -180,11 +182,23 @@ class CoreContext(val context: Context, coreConfig: Config) { isVibrating = false } - if (call.dir == Call.Dir.Incoming && core.callsNb == 1) { - routeAudioToBluetoothIfAvailable(call) + onCallStarted() + } else if (state == Call.State.StreamsRunning) { + // Do not automatically route audio to bluetooth after first call + if (core.callsNb == 1) { + // Only try to route bluetooth when the call is in StreamsRunning for the first time + if (previousCallState == Call.State.Connected && corePreferences.routeAudioToBluetoothIfAvailable) { + AudioRouteUtils.routeAudioToBluetooth(call) + } } - onCallStarted() + if (corePreferences.routeAudioToSpeakerWhenVideoIsEnabled && call.currentParams.videoEnabled()) { + // Do not turn speaker on when video is enabled if headset or bluetooth is used + if (!AudioRouteUtils.isHeadsetAudioRouteAvailable() && !AudioRouteUtils.isBluetoothAudioRouteCurrentlyUsed(call)) { + Log.i("[Context] Video enabled and no wired headset not bluetooth in use, routing audio to speaker") + AudioRouteUtils.routeAudioToSpeaker(call) + } + } } else if (state == Call.State.End || state == Call.State.Error || state == Call.State.Released) { if (core.callsNb == 0) { if (isVibrating) { @@ -215,6 +229,8 @@ class CoreContext(val context: Context, coreConfig: Config) { callErrorMessageResourceId.value = Event(id) } } + + previousCallState = state } } @@ -549,18 +565,6 @@ class CoreContext(val context: Context, coreConfig: Config) { } } - fun routeAudioToBluetoothIfAvailable(call: Call) { - for (audioDevice in core.audioDevices) { - if (audioDevice.type == AudioDevice.Type.Bluetooth && - audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) { - Log.i("[Context] Found bluetooth audio device [${audioDevice.deviceName}], routing audio to it") - call.outputAudioDevice = audioDevice - return - } - } - Log.w("[Context] Didn't find any bluetooth audio device, keeping default audio route") - } - /* Start call related activities */ private fun onIncomingReceived() { diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt index fdce95602..2c8d58dbc 100644 --- a/app/src/main/java/org/linphone/core/CorePreferences.kt +++ b/app/src/main/java/org/linphone/core/CorePreferences.kt @@ -225,6 +225,19 @@ class CorePreferences constructor(private val context: Context) { config.setBool("app", "full_screen_call", value) } + var routeAudioToBluetoothIfAvailable: Boolean + get() = config.getBool("app", "route_audio_to_bluetooth_if_available", true) + set(value) { + config.setBool("app", "route_audio_to_bluetooth_if_available", value) + } + + // This won't be done if bluetooth or wired headset is used + var routeAudioToSpeakerWhenVideoIsEnabled: Boolean + get() = config.getBool("app", "route_audio_to_speaker_when_video_enabled", true) + set(value) { + config.setBool("app", "route_audio_to_speaker_when_video_enabled", value) + } + /* Assistant */ var firstStart: Boolean diff --git a/app/src/main/java/org/linphone/utils/AudioRouteUtils.kt b/app/src/main/java/org/linphone/utils/AudioRouteUtils.kt new file mode 100644 index 000000000..ddb9ba385 --- /dev/null +++ b/app/src/main/java/org/linphone/utils/AudioRouteUtils.kt @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010-2021 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.linphone.utils + +import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.core.AudioDevice +import org.linphone.core.Call +import org.linphone.core.tools.Log + +class AudioRouteUtils { + companion object { + fun routeAudioToEarpiece(call: Call? = null) { + if (coreContext.core.callsNb == 0) { + Log.e("[Audio Route Helper] No call found, aborting earpiece audio route change") + return + } + val currentCall = call ?: coreContext.core.currentCall ?: coreContext.core.calls[0] + + for (audioDevice in coreContext.core.audioDevices) { + if (audioDevice.type == AudioDevice.Type.Earpiece) { + Log.i("[Audio Route Helper] Found earpiece audio device [${audioDevice.deviceName}], routing audio to it") + currentCall.outputAudioDevice = audioDevice + return + } + } + Log.e("[Audio Route Helper] Couldn't find earpiece audio device") + } + + fun routeAudioToSpeaker(call: Call? = null) { + if (coreContext.core.callsNb == 0) { + Log.e("[Audio Route Helper] No call found, aborting speaker audio route change") + return + } + val currentCall = call ?: coreContext.core.currentCall ?: coreContext.core.calls[0] + + for (audioDevice in coreContext.core.audioDevices) { + if (audioDevice.type == AudioDevice.Type.Speaker) { + Log.i("[Audio Route Helper] Found speaker audio device [${audioDevice.deviceName}], routing audio to it") + currentCall.outputAudioDevice = audioDevice + return + } + } + Log.e("[Audio Route Helper] Couldn't find speaker audio device") + } + + fun routeAudioToBluetooth(call: Call? = null) { + if (coreContext.core.callsNb == 0) { + Log.e("[Audio Route Helper] No call found, aborting bluetooth audio route change") + return + } + val currentCall = call ?: coreContext.core.currentCall ?: coreContext.core.calls[0] + + for (audioDevice in coreContext.core.audioDevices) { + if (audioDevice.type == AudioDevice.Type.Bluetooth && audioDevice.hasCapability( + AudioDevice.Capabilities.CapabilityPlay + ) + ) { + Log.i("[Audio Route Helper] Found bluetooth audio device [${audioDevice.deviceName}], routing audio to it") + currentCall.outputAudioDevice = audioDevice + return + } + } + Log.e("[Audio Route Helper] Couldn't find bluetooth audio device") + } + + fun isBluetoothAudioRouteCurrentlyUsed(call: Call? = null): Boolean { + if (coreContext.core.callsNb == 0) { + Log.w("[Audio Route Helper] No call found, so bluetooth audio route isn't used") + return false + } + val currentCall = call ?: coreContext.core.currentCall ?: coreContext.core.calls[0] + + val audioDevice = currentCall.outputAudioDevice + Log.i("[Audio Route Helper] Audio device currently in use is [${audioDevice?.deviceName}]") + return audioDevice?.type == AudioDevice.Type.Bluetooth + } + + fun isBluetoothAudioRouteAvailable(): Boolean { + for (audioDevice in coreContext.core.audioDevices) { + if (audioDevice.type == AudioDevice.Type.Bluetooth && audioDevice.hasCapability( + AudioDevice.Capabilities.CapabilityPlay + ) + ) { + Log.i("[Audio Route Helper] Found bluetooth audio device [${audioDevice.deviceName}]") + return true + } + } + return false + } + + fun isHeadsetAudioRouteAvailable(): Boolean { + for (audioDevice in coreContext.core.audioDevices) { + if (audioDevice.type == AudioDevice.Type.Headset || audioDevice.type == AudioDevice.Type.Headphones) { + Log.i("[Audio Route Helper] Found headset/headphones audio device [${audioDevice.deviceName}]") + return true + } + } + return false + } + } +} diff --git a/app/src/main/res/layout/call_secondary_buttons.xml b/app/src/main/res/layout/call_secondary_buttons.xml index 049bbd4ba..9ff309106 100644 --- a/app/src/main/res/layout/call_secondary_buttons.xml +++ b/app/src/main/res/layout/call_secondary_buttons.xml @@ -144,7 +144,7 @@ android:id="@+id/audio_routes" android:onClick="@{() -> viewModel.toggleRoutesMenu()}" android:visibility="@{viewModel.audioRoutesEnabled ? View.VISIBLE : View.INVISIBLE, default=invisible}" - android:selected="@{viewModel.audioRoutesVisibility}" + android:selected="@{viewModel.audioRoutesSelected}" android:contentDescription="@string/content_description_toggle_audio_menu" android:layout_width="match_parent" android:layout_height="@dimen/call_button_size"