Call audio device: handle bluetooth headset properly with icon and name

This commit is contained in:
ganfra 2021-07-26 21:03:51 +02:00
parent 4788630949
commit 1f3da38eb0
8 changed files with 63 additions and 42 deletions

View File

@ -21,7 +21,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.activityViewModel
import im.vector.app.R
import im.vector.app.core.epoxy.bottomsheet.bottomSheetActionItem
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetGenericListBinding
@ -52,23 +51,28 @@ class CallSoundDeviceChooserBottomSheet : VectorBaseBottomSheetDialogFragment<Bo
private fun render(available: Set<CallAudioManager.Device>, current: CallAudioManager.Device) {
views.bottomSheetRecyclerView.withModels {
available.forEach { device ->
bottomSheetActionItem {
id(device.ordinal)
textRes(device.titleRes)
iconRes(device.drawableRes)
selected(current == device)
listener {
callViewModel.handle(VectorCallViewActions.ChangeAudioDevice(device))
dismiss()
}
val title = when (device) {
is CallAudioManager.Device.WirelessHeadset -> device.name ?: getString(device.titleRes)
else -> getString(device.titleRes)
}
bottomSheetActionItem {
id(device.titleRes)
text(title)
iconRes(device.drawableRes)
selected(current == device)
listener {
callViewModel.handle(VectorCallViewActions.ChangeAudioDevice(device))
dismiss()
}
}
}
}
}
companion object {
fun newInstance(): RoomListQuickActionsBottomSheet {
return RoomListQuickActionsBottomSheet()
}
companion object {
fun newInstance(): RoomListQuickActionsBottomSheet {
return RoomListQuickActionsBottomSheet()
}
}
}

View File

@ -146,7 +146,7 @@ class VectorCallViewModel @AssistedInject constructor(
override fun onAudioDevicesChange() {
val currentSoundDevice = callManager.audioManager.selectedDevice ?: return
if (currentSoundDevice == CallAudioManager.Device.PHONE) {
if (currentSoundDevice == CallAudioManager.Device.Phone) {
proximityManager.start()
} else {
proximityManager.stop()
@ -184,7 +184,7 @@ class VectorCallViewModel @AssistedInject constructor(
callManager.addCurrentCallListener(currentCallListener)
webRtcCall.addListener(callListener)
val currentSoundDevice = callManager.audioManager.selectedDevice
if (currentSoundDevice == CallAudioManager.Device.PHONE) {
if (currentSoundDevice == CallAudioManager.Device.Phone) {
proximityManager.start()
}
setState {
@ -194,7 +194,7 @@ class VectorCallViewModel @AssistedInject constructor(
isVideoCall = webRtcCall.mxCall.isVideoCall,
callState = Success(webRtcCall.mxCall.state),
callInfo = webRtcCall.extractCallInfo(),
device = currentSoundDevice ?: CallAudioManager.Device.PHONE,
device = currentSoundDevice ?: CallAudioManager.Device.Phone,
isLocalOnHold = webRtcCall.isLocalOnHold,
isRemoteOnHold = webRtcCall.isRemoteOnHold,
availableDevices = callManager.audioManager.availableDevices,

View File

@ -35,7 +35,7 @@ data class VectorCallViewState(
val isHD: Boolean = false,
val isFrontCamera: Boolean = true,
val canSwitchCamera: Boolean = true,
val device: CallAudioManager.Device = CallAudioManager.Device.PHONE,
val device: CallAudioManager.Device = CallAudioManager.Device.Phone,
val availableDevices: Set<CallAudioManager.Device> = emptySet(),
val callState: Async<CallState> = Uninitialized,
val otherKnownCallInfo: CallInfo? = null,

View File

@ -50,13 +50,17 @@ internal class API21AudioDeviceDetector(private val context: Context,
private fun getAvailableSoundDevices(): Set<CallAudioManager.Device> {
return HashSet<CallAudioManager.Device>().apply {
if (isBluetoothHeadsetOn()) add(CallAudioManager.Device.WIRELESS_HEADSET)
if (isWiredHeadsetOn()) {
add(CallAudioManager.Device.HEADSET)
} else {
add(CallAudioManager.Device.PHONE)
if (isBluetoothHeadsetOn()) {
connectedBlueToothHeadset?.connectedDevices?.forEach {
add(CallAudioManager.Device.WirelessHeadset(it.name))
}
}
add(CallAudioManager.Device.SPEAKER)
if (isWiredHeadsetOn()) {
add(CallAudioManager.Device.Headset)
} else {
add(CallAudioManager.Device.Phone)
}
add(CallAudioManager.Device.Speaker)
}
}

View File

@ -33,10 +33,10 @@ internal class API23AudioDeviceDetector(private val audioManager: AudioManager,
val deviceInfos = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
for (info in deviceInfos) {
when (info.type) {
AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> devices.add(CallAudioManager.Device.WIRELESS_HEADSET)
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> devices.add(CallAudioManager.Device.PHONE)
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> devices.add(CallAudioManager.Device.SPEAKER)
AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_WIRED_HEADSET, TYPE_USB_HEADSET -> devices.add(CallAudioManager.Device.HEADSET)
AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> devices.add(CallAudioManager.Device.WirelessHeadset(info.productName.toString()))
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> devices.add(CallAudioManager.Device.Phone)
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> devices.add(CallAudioManager.Device.Speaker)
AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_WIRED_HEADSET, TYPE_USB_HEADSET -> devices.add(CallAudioManager.Device.Headset)
}
}
callAudioManager.replaceDevices(devices)

View File

@ -23,6 +23,7 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.content.getSystemService
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import org.matrix.android.sdk.api.extensions.orFalse
import timber.log.Timber
import java.util.HashSet
@ -34,11 +35,11 @@ class CallAudioManager(private val context: Context, val configChange: (() -> Un
private var audioDeviceDetector: AudioDeviceDetector? = null
private var audioDeviceRouter: AudioDeviceRouter? = null
enum class Device(@StringRes val titleRes: Int, @DrawableRes val drawableRes: Int) {
PHONE(R.string.sound_device_phone,R.drawable.ic_sound_device_phone),
SPEAKER(R.string.sound_device_speaker,R.drawable.ic_sound_device_speaker),
HEADSET(R.string.sound_device_headset,R.drawable.ic_sound_device_headphone),
WIRELESS_HEADSET(R.string.sound_device_wireless_headset,R.drawable.ic_sound_device_headphone)
sealed class Device(@StringRes val titleRes: Int, @DrawableRes val drawableRes: Int) {
object Phone : Device(R.string.sound_device_phone, R.drawable.ic_sound_device_phone)
object Speaker : Device(R.string.sound_device_speaker, R.drawable.ic_sound_device_speaker)
object Headset : Device(R.string.sound_device_headset, R.drawable.ic_sound_device_headphone)
data class WirelessHeadset(val name: String?) : Device(R.string.sound_device_wireless_headset, R.drawable.ic_sound_device_wireless)
}
enum class Mode {
@ -136,19 +137,19 @@ class CallAudioManager(private val context: Context, val configChange: (() -> Un
userSelectedDevice = null
return true
}
val bluetoothAvailable = _availableDevices.contains(Device.WIRELESS_HEADSET)
val headsetAvailable = _availableDevices.contains(Device.HEADSET)
val availableBluetoothDevice = _availableDevices.firstOrNull { it is Device.WirelessHeadset }
val headsetAvailable = _availableDevices.contains(Device.Headset)
// Pick the desired device based on what's available and the mode.
var audioDevice: Device
audioDevice = if (bluetoothAvailable) {
Device.WIRELESS_HEADSET
audioDevice = if (availableBluetoothDevice != null) {
availableBluetoothDevice
} else if (headsetAvailable) {
Device.HEADSET
Device.Headset
} else if (mode == Mode.VIDEO_CALL) {
Device.SPEAKER
Device.Speaker
} else {
Device.PHONE
Device.Phone
}
// Consider the user's selection
if (userSelectedDevice != null && _availableDevices.contains(userSelectedDevice)) {

View File

@ -31,8 +31,8 @@ class DefaultAudioDeviceRouter(private val audioManager: AudioManager,
private var focusRequestCompat: AudioFocusRequestCompat? = null
override fun setAudioRoute(device: CallAudioManager.Device) {
audioManager.isSpeakerphoneOn = device === CallAudioManager.Device.SPEAKER
setBluetoothAudioRoute(device === CallAudioManager.Device.WIRELESS_HEADSET)
audioManager.isSpeakerphoneOn = device is CallAudioManager.Device.Speaker
setBluetoothAudioRoute(device is CallAudioManager.Device.WirelessHeadset)
}
override fun setMode(mode: CallAudioManager.Mode): Boolean {

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M10.8817,3.1593L5.4545,7.682L1.3636,7.682C0.6105,7.682 0,8.2925 0,9.0456V15.0456C0,15.7987 0.6105,16.4092 1.3636,16.4092L5.4545,16.4092L10.8817,20.9318C11.3258,21.3019 12,20.9861 12,20.4081V3.6831C12,3.1051 11.3258,2.7893 10.8817,3.1593Z"
android:fillColor="#737D8C"/>
<path
android:pathData="M23.6656,7.8241L20.0887,4.2472C19.5634,3.7219 18.6629,4.0888 18.6629,4.8309V9.9836L15.4195,6.7402C15.0943,6.415 14.5691,6.415 14.2439,6.7402C13.9187,7.0654 13.9187,7.5907 14.2439,7.9159L18.3211,11.993L14.2439,16.0703C13.9187,16.3954 13.9187,16.9207 14.2439,17.2459C14.5691,17.5711 15.0943,17.5711 15.4195,17.2459L18.6629,14.0025V19.1553C18.6629,19.8973 19.5634,20.2725 20.0887,19.7472L23.6656,16.162C23.9908,15.8368 23.9908,15.3115 23.6656,14.9863L20.6724,11.993L23.6656,9.0081C23.9908,8.6829 23.9908,8.1493 23.6656,7.8241ZM20.3305,6.8486L21.898,8.4161L20.3305,9.9836V6.8486ZM21.898,15.57L20.3305,17.1375V14.0025L21.898,15.57Z"
android:fillColor="#737D8C"/>
</vector>