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:
parent
704e7d84fa
commit
7172d7cf60
15 changed files with 186 additions and 66 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>()
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
118
app/src/main/java/org/linphone/utils/AudioRouteUtils.kt
Normal file
118
app/src/main/java/org/linphone/utils/AudioRouteUtils.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue