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 ducking = false
|
||||||
@Volatile private var capturing = 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 ──────────────────────────────────────────────────────────
|
// ─── Capture ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
fun startCapture(scope: CoroutineScope): Boolean {
|
fun startCapture(scope: CoroutineScope): Boolean {
|
||||||
|
|
@ -92,11 +121,6 @@ class AudioPipeline(private val context: Context) {
|
||||||
|
|
||||||
recorder = rec
|
recorder = rec
|
||||||
|
|
||||||
previousAudioMode = audioManager.mode
|
|
||||||
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
audioManager.isSpeakerphoneOn = true
|
|
||||||
|
|
||||||
if (AcousticEchoCanceler.isAvailable()) {
|
if (AcousticEchoCanceler.isAvailable()) {
|
||||||
try {
|
try {
|
||||||
aec = AcousticEchoCanceler.create(rec.audioSessionId)
|
aec = AcousticEchoCanceler.create(rec.audioSessionId)
|
||||||
|
|
@ -147,7 +171,6 @@ class AudioPipeline(private val context: Context) {
|
||||||
aec = null
|
aec = null
|
||||||
recorder?.release()
|
recorder?.release()
|
||||||
recorder = null
|
recorder = null
|
||||||
audioManager.mode = previousAudioMode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Playback ─────────────────────────────────────────────────────────
|
// ─── Playback ─────────────────────────────────────────────────────────
|
||||||
|
|
@ -220,6 +243,7 @@ class AudioPipeline(private val context: Context) {
|
||||||
fun release() {
|
fun release() {
|
||||||
stopCapture()
|
stopCapture()
|
||||||
stopPlayback()
|
stopPlayback()
|
||||||
|
exitCallMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Helpers ──────────────────────────────────────────────────────────
|
// ─── Helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,7 @@ class LiveCallService : Service() {
|
||||||
connectTimeoutJob?.cancel()
|
connectTimeoutJob?.cancel()
|
||||||
_status.value = CallStatus.LISTENING
|
_status.value = CallStatus.LISTENING
|
||||||
|
|
||||||
|
pipeline.enterCallMode()
|
||||||
if (!pipeline.initPlayback()) {
|
if (!pipeline.initPlayback()) {
|
||||||
Log.e(TAG, "Playback init failed")
|
Log.e(TAG, "Playback init failed")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue