From cee43bda2e8f919b72de95c734e8dd6938c8d22d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Apr 2026 18:54:54 +0000 Subject: [PATCH] fix: use setCommunicationDevice API for audio routing on Android 12+ AudioManager.isSpeakerphoneOn is deprecated since API 31 and silently ignored on modern devices. Switch to setCommunicationDevice() which properly routes call audio to speaker, earpiece, or Bluetooth on Android 12+, with fallback to the legacy API on older versions. https://claude.ai/code/session_014espsysob7MrE8X8e75jFX --- .../amethyst/service/call/CallAudioManager.kt | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/CallAudioManager.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/CallAudioManager.kt index 171f532431..40c81c0487 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/CallAudioManager.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/CallAudioManager.kt @@ -127,7 +127,11 @@ class CallAudioManager( fun restoreAudioMode() { stopBluetoothSco() unregisterBluetoothScoReceiver() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + audioManager.clearCommunicationDevice() + } audioManager.mode = previousAudioMode + @Suppress("DEPRECATION") audioManager.isSpeakerphoneOn = false } @@ -189,17 +193,47 @@ class CallAudioManager( private fun routeToEarpiece() { stopBluetoothSco() - audioManager.isSpeakerphoneOn = false + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val earpiece = + audioManager + .availableCommunicationDevices + .firstOrNull { it.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE } + earpiece?.let { audioManager.setCommunicationDevice(it) } + } else { + @Suppress("DEPRECATION") + audioManager.isSpeakerphoneOn = false + } } private fun routeToSpeaker() { stopBluetoothSco() - audioManager.isSpeakerphoneOn = true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val speaker = + audioManager + .availableCommunicationDevices + .firstOrNull { it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER } + speaker?.let { audioManager.setCommunicationDevice(it) } + } else { + @Suppress("DEPRECATION") + audioManager.isSpeakerphoneOn = true + } } private fun routeToBluetooth() { - audioManager.isSpeakerphoneOn = false - startBluetoothSco() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val btDevice = + audioManager + .availableCommunicationDevices + .firstOrNull { + it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO || + it.type == AudioDeviceInfo.TYPE_BLE_HEADSET + } + btDevice?.let { audioManager.setCommunicationDevice(it) } + } else { + @Suppress("DEPRECATION") + audioManager.isSpeakerphoneOn = false + startBluetoothSco() + } } private fun hasBluetoothDevice(): Boolean =