Reworked audio route handling code and added auto switch to bluetooth if available and to speaker when video is enabled

This commit is contained in:
Sylvain Berfini 2021-03-25 11:58:08 +01:00
parent 704e7d84fa
commit 7172d7cf60
15 changed files with 186 additions and 66 deletions

View file

@ -36,6 +36,7 @@ import org.linphone.compatibility.Compatibility
import org.linphone.core.* import org.linphone.core.*
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils import org.linphone.utils.AppUtils
import org.linphone.utils.AudioRouteUtils
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.PermissionHelper import org.linphone.utils.PermissionHelper
@ -68,7 +69,7 @@ class ControlsViewModel : ViewModel() {
val optionsVisibility = MutableLiveData<Boolean>() val optionsVisibility = MutableLiveData<Boolean>()
val audioRoutesVisibility = MutableLiveData<Boolean>() val audioRoutesSelected = MutableLiveData<Boolean>()
val audioRoutesEnabled = MutableLiveData<Boolean>() val audioRoutesEnabled = MutableLiveData<Boolean>()
@ -170,7 +171,9 @@ class ControlsViewModel : ViewModel() {
state: Call.State, state: Call.State,
message: String message: String
) { ) {
if (state == Call.State.StreamsRunning) isVideoUpdateInProgress.value = false if (state == Call.State.StreamsRunning) {
isVideoUpdateInProgress.value = false
}
if (coreContext.isVideoCallOrConferenceActive() && !PermissionHelper.get().hasCameraPermission()) { if (coreContext.isVideoCallOrConferenceActive() && !PermissionHelper.get().hasCameraPermission()) {
askPermissionEvent.value = Event(Manifest.permission.CAMERA) askPermissionEvent.value = Event(Manifest.permission.CAMERA)
@ -186,9 +189,14 @@ class ControlsViewModel : ViewModel() {
} }
override fun onAudioDevicesListUpdated(core: Core) { override fun onAudioDevicesListUpdated(core: Core) {
if (core.callsNb == 0) return Log.i("[Call] Audio devices list updated")
val wasBluetoothPreviouslyAvailable = audioRoutesEnabled.value == true
updateAudioRoutesState() 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 numpadVisibility.value = false
optionsVisibility.value = false optionsVisibility.value = false
audioRoutesVisibility.value = false audioRoutesSelected.value = false
isRecording.value = currentCall?.isRecording isRecording.value = currentCall?.isRecording
isVideoUpdateInProgress.value = false isVideoUpdateInProgress.value = false
@ -309,8 +317,8 @@ class ControlsViewModel : ViewModel() {
fun toggleRoutesMenu() { fun toggleRoutesMenu() {
somethingClickedEvent.value = Event(true) somethingClickedEvent.value = Event(true)
audioRoutesVisibility.value = audioRoutesVisibility.value != true audioRoutesSelected.value = audioRoutesSelected.value != true
if (audioRoutesVisibility.value == true) { if (audioRoutesSelected.value == true) {
audioRoutesMenuAnimator.start() audioRoutesMenuAnimator.start()
} else { } else {
audioRoutesMenuAnimator.reverse() audioRoutesMenuAnimator.reverse()
@ -365,38 +373,17 @@ class ControlsViewModel : ViewModel() {
fun forceEarpieceAudioRoute() { fun forceEarpieceAudioRoute() {
somethingClickedEvent.value = Event(true) somethingClickedEvent.value = Event(true)
for (audioDevice in coreContext.core.audioDevices) { AudioRouteUtils.routeAudioToEarpiece()
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")
} }
fun forceSpeakerAudioRoute() { fun forceSpeakerAudioRoute() {
somethingClickedEvent.value = Event(true) somethingClickedEvent.value = Event(true)
for (audioDevice in coreContext.core.audioDevices) { AudioRouteUtils.routeAudioToSpeaker()
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")
} }
fun forceBluetoothAudioRoute() { fun forceBluetoothAudioRoute() {
somethingClickedEvent.value = Event(true) somethingClickedEvent.value = Event(true)
for (audioDevice in coreContext.core.audioDevices) { AudioRouteUtils.routeAudioToBluetooth()
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")
} }
private fun updateAudioRelated() { private fun updateAudioRelated() {
@ -425,21 +412,16 @@ class ControlsViewModel : ViewModel() {
} }
private fun updateAudioRoutesState() { private fun updateAudioRoutesState() {
var bluetoothDeviceAvailable = false val bluetoothDeviceAvailable = AudioRouteUtils.isBluetoothAudioRouteAvailable()
for (audioDevice in coreContext.core.audioDevices) {
if (audioDevice.type == AudioDevice.Type.Bluetooth) {
bluetoothDeviceAvailable = true
break
}
}
audioRoutesEnabled.value = bluetoothDeviceAvailable audioRoutesEnabled.value = bluetoothDeviceAvailable
if (!bluetoothDeviceAvailable) { if (!bluetoothDeviceAvailable) {
audioRoutesVisibility.value = false audioRoutesSelected.value = false
} }
} }
private fun updateBluetoothHeadsetState() { 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 isBluetoothHeadsetSelected.value = audioDevice?.type == AudioDevice.Type.Bluetooth
} }

View file

@ -21,7 +21,7 @@ package org.linphone.activities.main.about
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.utils.LogsUploadViewModel import org.linphone.activities.main.viewmodels.LogsUploadViewModel
class AboutViewModel : LogsUploadViewModel() { class AboutViewModel : LogsUploadViewModel() {
val appVersion: String = coreContext.appVersion val appVersion: String = coreContext.appVersion

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.utils package org.linphone.activities.main.adapters
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter

View file

@ -33,6 +33,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R 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.ChatMessageViewModel
import org.linphone.activities.main.chat.viewmodels.EventViewModel import org.linphone.activities.main.chat.viewmodels.EventViewModel
import org.linphone.activities.main.chat.viewmodels.OnContentClickedListener 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.ChatEventListCellBinding
import org.linphone.databinding.ChatMessageListCellBinding import org.linphone.databinding.ChatMessageListCellBinding
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.SelectionListAdapter
class ChatMessagesListAdapter( class ChatMessagesListAdapter(
selectionVM: ListTopBarViewModel, selectionVM: ListTopBarViewModel,

View file

@ -27,12 +27,12 @@ import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.adapters.SelectionListAdapter
import org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel import org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel
import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.core.ChatRoom import org.linphone.core.ChatRoom
import org.linphone.databinding.ChatRoomListCellBinding import org.linphone.databinding.ChatRoomListCellBinding
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.SelectionListAdapter
class ChatRoomsListAdapter( class ChatRoomsListAdapter(
selectionVM: ListTopBarViewModel, selectionVM: ListTopBarViewModel,

View file

@ -29,6 +29,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.adapters.SelectionListAdapter
import org.linphone.activities.main.contact.viewmodels.ContactViewModel import org.linphone.activities.main.contact.viewmodels.ContactViewModel
import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.contact.Contact import org.linphone.contact.Contact
@ -37,7 +38,6 @@ import org.linphone.databinding.GenericListHeaderBinding
import org.linphone.utils.AppUtils import org.linphone.utils.AppUtils
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.HeaderAdapter import org.linphone.utils.HeaderAdapter
import org.linphone.utils.SelectionListAdapter
class ContactsListAdapter( class ContactsListAdapter(
selectionVM: ListTopBarViewModel, selectionVM: ListTopBarViewModel,

View file

@ -27,12 +27,12 @@ import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.activities.main.dialer.NumpadDigitListener import org.linphone.activities.main.dialer.NumpadDigitListener
import org.linphone.activities.main.viewmodels.LogsUploadViewModel
import org.linphone.compatibility.Compatibility import org.linphone.compatibility.Compatibility
import org.linphone.core.* import org.linphone.core.*
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils import org.linphone.utils.LinphoneUtils
import org.linphone.utils.LogsUploadViewModel
class DialerViewModel : LogsUploadViewModel() { class DialerViewModel : LogsUploadViewModel() {
val enteredUri = MutableLiveData<String>() val enteredUri = MutableLiveData<String>()

View file

@ -28,12 +28,12 @@ import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import org.linphone.LinphoneApplication import org.linphone.LinphoneApplication
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.adapters.SelectionListAdapter
import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils import org.linphone.utils.AppUtils
import org.linphone.utils.DialogUtils import org.linphone.utils.DialogUtils
import org.linphone.utils.SelectionListAdapter
/** /**
* This fragment can be inherited by all fragments that will display a list * This fragment can be inherited by all fragments that will display a list

View file

@ -29,6 +29,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.linphone.R 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.CallLogViewModel
import org.linphone.activities.main.history.viewmodels.GroupedCallLogViewModel import org.linphone.activities.main.history.viewmodels.GroupedCallLogViewModel
import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel

View file

@ -30,6 +30,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.adapters.SelectionListAdapter
import org.linphone.activities.main.recordings.viewmodels.RecordingViewModel import org.linphone.activities.main.recordings.viewmodels.RecordingViewModel
import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.databinding.GenericListHeaderBinding import org.linphone.databinding.GenericListHeaderBinding

View file

@ -18,13 +18,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.utils package org.linphone.activities.main.viewmodels
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Core import org.linphone.core.Core
import org.linphone.core.CoreListenerStub import org.linphone.core.CoreListenerStub
import org.linphone.utils.Event
open class LogsUploadViewModel : ViewModel() { open class LogsUploadViewModel : ViewModel() {
val uploadInProgress = MutableLiveData<Boolean>() val uploadInProgress = MutableLiveData<Boolean>()

View file

@ -47,6 +47,7 @@ import org.linphone.core.tools.Log
import org.linphone.mediastream.Version import org.linphone.mediastream.Version
import org.linphone.notifications.NotificationsManager import org.linphone.notifications.NotificationsManager
import org.linphone.utils.AppUtils import org.linphone.utils.AppUtils
import org.linphone.utils.AudioRouteUtils
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils import org.linphone.utils.LinphoneUtils
@ -110,6 +111,7 @@ class CoreContext(val context: Context, coreConfig: Config) {
private var overlayY = 0f private var overlayY = 0f
private var callOverlay: View? = null private var callOverlay: View? = null
private var isVibrating = false private var isVibrating = false
private var previousCallState = Call.State.Idle
private val listener: CoreListenerStub = object : CoreListenerStub() { private val listener: CoreListenerStub = object : CoreListenerStub() {
override fun onGlobalStateChanged(core: Core, state: GlobalState, message: String) { 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) { } else if (state == Call.State.OutgoingInit) {
onOutgoingStarted() onOutgoingStarted()
} else if (state == Call.State.OutgoingProgress) { } else if (state == Call.State.OutgoingProgress) {
if (core.callsNb == 1) { if (core.callsNb == 1 && corePreferences.routeAudioToBluetoothIfAvailable) {
routeAudioToBluetoothIfAvailable(call) AudioRouteUtils.routeAudioToBluetooth(call)
} }
} else if (state == Call.State.Connected) { } else if (state == Call.State.Connected) {
if (isVibrating) { if (isVibrating) {
@ -180,11 +182,23 @@ class CoreContext(val context: Context, coreConfig: Config) {
isVibrating = false isVibrating = false
} }
if (call.dir == Call.Dir.Incoming && core.callsNb == 1) { onCallStarted()
routeAudioToBluetoothIfAvailable(call) } 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) { } else if (state == Call.State.End || state == Call.State.Error || state == Call.State.Released) {
if (core.callsNb == 0) { if (core.callsNb == 0) {
if (isVibrating) { if (isVibrating) {
@ -215,6 +229,8 @@ class CoreContext(val context: Context, coreConfig: Config) {
callErrorMessageResourceId.value = Event(id) 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 */ /* Start call related activities */
private fun onIncomingReceived() { private fun onIncomingReceived() {

View file

@ -225,6 +225,19 @@ class CorePreferences constructor(private val context: Context) {
config.setBool("app", "full_screen_call", value) 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 */ /* Assistant */
var firstStart: Boolean var firstStart: Boolean

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
}

View file

@ -144,7 +144,7 @@
android:id="@+id/audio_routes" android:id="@+id/audio_routes"
android:onClick="@{() -> viewModel.toggleRoutesMenu()}" android:onClick="@{() -> viewModel.toggleRoutesMenu()}"
android:visibility="@{viewModel.audioRoutesEnabled ? View.VISIBLE : View.INVISIBLE, default=invisible}" 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:contentDescription="@string/content_description_toggle_audio_menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/call_button_size" android:layout_height="@dimen/call_button_size"