Fix issue with initial audio output setting.

Co-authored-by: Greyson Parrelli <greyson@signal.org>
This commit is contained in:
Alex Hart
2026-02-12 09:18:22 -04:00
parent 2771b31aab
commit a4469a4285
5 changed files with 256 additions and 14 deletions

View File

@@ -7,6 +7,7 @@ import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
@@ -28,6 +29,15 @@ class WebRtcAudioPicker31(private val audioOutputChangedListener: OnAudioOutputC
companion object {
const val TAG = "WebRtcAudioPicker31"
private fun WebRtcAudioOutput.toSignalAudioDevice(): SignalAudioManager.AudioDevice {
return when (this) {
WebRtcAudioOutput.HANDSET -> SignalAudioManager.AudioDevice.EARPIECE
WebRtcAudioOutput.SPEAKER -> SignalAudioManager.AudioDevice.SPEAKER_PHONE
WebRtcAudioOutput.BLUETOOTH_HEADSET -> SignalAudioManager.AudioDevice.BLUETOOTH
WebRtcAudioOutput.WIRED_HEADSET -> SignalAudioManager.AudioDevice.WIRED_HEADSET
}
}
}
fun showPicker(fragmentActivity: FragmentActivity, threshold: Int, onDismiss: (DialogInterface) -> Unit): DialogInterface? {
@@ -60,20 +70,20 @@ class WebRtcAudioPicker31(private val audioOutputChangedListener: OnAudioOutputC
val am = AppDependencies.androidCallAudioManager
if (am.availableCommunicationDevices.isEmpty()) {
Toast.makeText(context, R.string.WebRtcAudioOutputToggleButton_no_eligible_audio_i_o_detected, Toast.LENGTH_LONG).show()
LaunchedEffect(Unit) {
Toast.makeText(context, R.string.WebRtcAudioOutputToggleButton_no_eligible_audio_i_o_detected, Toast.LENGTH_LONG).show()
stateUpdater.hidePicker()
}
return
}
val devices: List<AudioOutputOption> = am.availableCommunicationDevices.map { AudioOutputOption(it.toFriendlyName(context).toString(), AudioDeviceMapping.fromPlatformType(it.type), it.id) }.distinctBy { it.deviceType.name }.filterNot { it.deviceType == SignalAudioManager.AudioDevice.NONE }
val currentDeviceId = am.communicationDevice?.id ?: -1
if (devices.size < threshold) {
Log.d(TAG, "Only found $devices devices, not showing picker.")
if (devices.isEmpty()) return
val index = devices.indexOfFirst { it.deviceId == currentDeviceId }
if (index == -1) return
onAudioDeviceSelected(devices[(index + 1) % devices.size])
LaunchedEffect(Unit) {
Log.d(TAG, "Only found $devices devices, not showing picker.")
cycleToNextDevice()
}
return
} else {
Log.d(TAG, "Found $devices devices, showing picker.")
@@ -124,6 +134,37 @@ class WebRtcAudioPicker31(private val audioOutputChangedListener: OnAudioOutputC
}
}
/**
* Cycles to the next audio device without showing a picker.
* Uses the system device list to resolve the actual device ID, falling back to
* type-based lookup from app-tracked state when the current communication device is unknown.
*/
fun cycleToNextDevice() {
val am = AppDependencies.androidCallAudioManager
val devices: List<AudioOutputOption> = am.availableCommunicationDevices
.map { AudioOutputOption("", AudioDeviceMapping.fromPlatformType(it.type), it.id) }
.distinctBy { it.deviceType.name }
.filterNot { it.deviceType == SignalAudioManager.AudioDevice.NONE }
if (devices.isEmpty()) {
Log.w(TAG, "cycleToNextDevice: no available communication devices")
return
}
val currentDeviceId = am.communicationDevice?.id ?: -1
val index = devices.indexOfFirst { it.deviceId == currentDeviceId }
if (index != -1) {
onAudioDeviceSelected(devices[(index + 1) % devices.size])
} else {
val nextOutput = outputState.peekNext()
val targetDeviceType = nextOutput.toSignalAudioDevice()
val targetDevice = devices.firstOrNull { it.deviceType == targetDeviceType } ?: devices.first()
Log.d(TAG, "cycleToNextDevice: communicationDevice unknown, selecting ${targetDevice.deviceType} by type")
onAudioDeviceSelected(targetDevice)
}
}
private fun AudioOutputOption.toWebRtcAudioOutput(): WebRtcAudioOutput {
return when (this.deviceType) {
SignalAudioManager.AudioDevice.WIRED_HEADSET -> WebRtcAudioOutput.WIRED_HEADSET

View File

@@ -185,11 +185,13 @@ class AudioOutputPickerController(
val isLegacy = Build.VERSION.SDK_INT < 31
if (!willDisplayPicker) {
if (isLegacy) {
LaunchedEffect(Unit) {
displaySheet = false
onSelectedDeviceChanged(WebRtcAudioDevice(outputState.peekNext(), null))
} else {
newApiController!!.Picker(threshold = SHOW_PICKER_THRESHOLD)
if (isLegacy) {
onSelectedDeviceChanged(WebRtcAudioDevice(outputState.peekNext(), null))
} else {
newApiController!!.cycleToNextDevice()
}
}
return
}