From f78be7e30613a91212657194d3da846a9b5cc383 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 23 Apr 2021 14:31:41 +0200 Subject: [PATCH] Route audio to headset/headphones if available (replaces earpiece) --- CHANGELOG.md | 1 + .../call/viewmodels/ControlsViewModel.kt | 15 +++++-- .../java/org/linphone/core/CoreContext.kt | 11 +++-- .../org/linphone/utils/AudioRouteUtils.kt | 41 +++++++++++++------ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66fffd0f1..061826c96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ This version is a full rewrite of the app in kotlin, using modern Android compon - Call history view groups call from the same SIP URI (like linphone-iphone) - Reworked conference (using new linphone-sdk APIs) +- Route audio to headset / headphones / bluetooth device automatically when available - Improved how Android native contacts are used - Switched to material design for text input fields & switches - Launcher shortcuts can be to either contacts or chat rooms 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 b2ee1a071..4df101e78 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 @@ -195,9 +195,13 @@ class ControlsViewModel : ViewModel() { val wasBluetoothPreviouslyAvailable = audioRoutesEnabled.value == true updateAudioRoutesState() - if (!wasBluetoothPreviouslyAvailable && corePreferences.routeAudioToBluetoothIfAvailable) { + if (AudioRouteUtils.isHeadsetAudioRouteAvailable()) { + AudioRouteUtils.routeAudioToHeadset() + } else if (!wasBluetoothPreviouslyAvailable && corePreferences.routeAudioToBluetoothIfAvailable) { // Only attempt to route audio to bluetooth automatically when bluetooth device is connected - AudioRouteUtils.routeAudioToBluetooth() + if (AudioRouteUtils.isBluetoothAudioRouteAvailable()) { + AudioRouteUtils.routeAudioToBluetooth() + } } } } @@ -375,7 +379,12 @@ class ControlsViewModel : ViewModel() { fun forceEarpieceAudioRoute() { somethingClickedEvent.value = Event(true) - AudioRouteUtils.routeAudioToEarpiece() + if (AudioRouteUtils.isHeadsetAudioRouteAvailable()) { + Log.i("[Call] Headset found, route audio to it instead of earpiece") + AudioRouteUtils.routeAudioToHeadset() + } else { + AudioRouteUtils.routeAudioToEarpiece() + } } fun forceSpeakerAudioRoute() { diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index aa7a129b6..7b47fad80 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -194,9 +194,14 @@ class CoreContext(val context: Context, coreConfig: Config) { } 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) + // Only try to route bluetooth / headphone / headset when the call is in StreamsRunning for the first time + if (previousCallState == Call.State.Connected) { + Log.i("[Context] First call going into StreamsRunning state for the first time, trying to route audio to headset or bluetooth if available") + if (AudioRouteUtils.isHeadsetAudioRouteAvailable()) { + AudioRouteUtils.routeAudioToHeadset(call) + } else if (corePreferences.routeAudioToBluetoothIfAvailable && AudioRouteUtils.isBluetoothAudioRouteAvailable()) { + AudioRouteUtils.routeAudioToBluetooth(call) + } } } diff --git a/app/src/main/java/org/linphone/utils/AudioRouteUtils.kt b/app/src/main/java/org/linphone/utils/AudioRouteUtils.kt index ddb9ba385..d000a0f5c 100644 --- a/app/src/main/java/org/linphone/utils/AudioRouteUtils.kt +++ b/app/src/main/java/org/linphone/utils/AudioRouteUtils.kt @@ -68,18 +68,36 @@ class AudioRouteUtils { 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 + if (audioDevice.type == AudioDevice.Type.Bluetooth) { + if (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 routeAudioToHeadset(call: Call? = null) { + if (coreContext.core.callsNb == 0) { + Log.e("[Audio Route Helper] No call found, aborting headset 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.Headphones || audioDevice.type == AudioDevice.Type.Headset) { + if (audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) { + Log.i("[Audio Route Helper] Found headset audio device [${audioDevice.deviceName}], routing audio to it") + currentCall.outputAudioDevice = audioDevice + return + } + } + } + Log.e("[Audio Route Helper] Couldn't find headset 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") @@ -94,10 +112,8 @@ class AudioRouteUtils { fun isBluetoothAudioRouteAvailable(): Boolean { for (audioDevice in coreContext.core.audioDevices) { - if (audioDevice.type == AudioDevice.Type.Bluetooth && audioDevice.hasCapability( - AudioDevice.Capabilities.CapabilityPlay - ) - ) { + if (audioDevice.type == AudioDevice.Type.Bluetooth && + audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) { Log.i("[Audio Route Helper] Found bluetooth audio device [${audioDevice.deviceName}]") return true } @@ -107,7 +123,8 @@ class AudioRouteUtils { fun isHeadsetAudioRouteAvailable(): Boolean { for (audioDevice in coreContext.core.audioDevices) { - if (audioDevice.type == AudioDevice.Type.Headset || audioDevice.type == AudioDevice.Type.Headphones) { + if ((audioDevice.type == AudioDevice.Type.Headset || audioDevice.type == AudioDevice.Type.Headphones) && + audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) { Log.i("[Audio Route Helper] Found headset/headphones audio device [${audioDevice.deviceName}]") return true }