fix Live Call: route audio to speaker with proper communication device API
MODE_IN_COMMUNICATION routes to earpiece by default — no sound on speaker. The deprecated isSpeakerphoneOn doesn't work on Android 12+. Fix: enterCallMode()/exitCallMode() lifecycle — sets MODE_IN_COMMUNICATION + setCommunicationDevice(SPEAKER) on API 31+ (S25 Ultra = Android 15). Called BEFORE AudioTrack creation so the routing is active when playback starts. exitCallMode() restores previous audio mode on call end.
This commit is contained in:
parent
0baab7db9e
commit
f5d7d9a9ab
2 changed files with 31 additions and 6 deletions
|
|
@ -62,6 +62,35 @@ class AudioPipeline(private val context: Context) {
|
|||
@Volatile private var ducking = false
|
||||
@Volatile private var capturing = false
|
||||
|
||||
// ─── Audio Mode ───────────────────────────────────────────────────────
|
||||
|
||||
fun enterCallMode() {
|
||||
previousAudioMode = audioManager.mode
|
||||
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 31) {
|
||||
val speaker = audioManager.availableCommunicationDevices
|
||||
.firstOrNull { it.type == android.media.AudioDeviceInfo.TYPE_BUILTIN_SPEAKER }
|
||||
if (speaker != null) {
|
||||
audioManager.setCommunicationDevice(speaker)
|
||||
Log.i(TAG, "Communication device → speaker")
|
||||
}
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
audioManager.isSpeakerphoneOn = true
|
||||
}
|
||||
}
|
||||
|
||||
fun exitCallMode() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 31) {
|
||||
audioManager.clearCommunicationDevice()
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
audioManager.isSpeakerphoneOn = false
|
||||
}
|
||||
audioManager.mode = previousAudioMode
|
||||
}
|
||||
|
||||
// ─── Capture ──────────────────────────────────────────────────────────
|
||||
|
||||
fun startCapture(scope: CoroutineScope): Boolean {
|
||||
|
|
@ -92,11 +121,6 @@ class AudioPipeline(private val context: Context) {
|
|||
|
||||
recorder = rec
|
||||
|
||||
previousAudioMode = audioManager.mode
|
||||
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
@Suppress("DEPRECATION")
|
||||
audioManager.isSpeakerphoneOn = true
|
||||
|
||||
if (AcousticEchoCanceler.isAvailable()) {
|
||||
try {
|
||||
aec = AcousticEchoCanceler.create(rec.audioSessionId)
|
||||
|
|
@ -147,7 +171,6 @@ class AudioPipeline(private val context: Context) {
|
|||
aec = null
|
||||
recorder?.release()
|
||||
recorder = null
|
||||
audioManager.mode = previousAudioMode
|
||||
}
|
||||
|
||||
// ─── Playback ─────────────────────────────────────────────────────────
|
||||
|
|
@ -220,6 +243,7 @@ class AudioPipeline(private val context: Context) {
|
|||
fun release() {
|
||||
stopCapture()
|
||||
stopPlayback()
|
||||
exitCallMode()
|
||||
}
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ class LiveCallService : Service() {
|
|||
connectTimeoutJob?.cancel()
|
||||
_status.value = CallStatus.LISTENING
|
||||
|
||||
pipeline.enterCallMode()
|
||||
if (!pipeline.initPlayback()) {
|
||||
Log.e(TAG, "Playback init failed")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue