From 4e71689f8e6356d491a7c8e814776c32b3dd27e9 Mon Sep 17 00:00:00 2001 From: Seb3thehacker Date: Thu, 15 Oct 2026 11:21:30 -0700 Subject: [PATCH] audio: with music on Bluetooth, leave the car's media channel closed so the car keeps its Bluetooth source --- .../app/seb3thehacker/gearslip/GearslipRunner.kt | 14 +++++++++++++- .../app/seb3thehacker/gearslip/audio/CarAudio.kt | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt index 63c3c34..633fb38 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt @@ -2,6 +2,7 @@ package app.seb3thehacker.gearslip import android.os.Build import app.seb3thehacker.gearslip.audio.AudioLink +import app.seb3thehacker.gearslip.audio.CarAudio import app.seb3thehacker.gearslip.audio.CarMic import app.seb3thehacker.gearslip.media.CarMediaStatus import app.seb3thehacker.gearslip.car.BluetoothAudioCheck @@ -160,6 +161,7 @@ class GearslipRunner( synchronized(ackLock) { ackLock.notifyAll() } // free an encoder waiting on an ack videoSource?.stop() videoSource = null + CarAudio.openSink = null audioLink?.close() audioLink = null guidanceLink?.close() @@ -527,7 +529,17 @@ class GearslipRunner( // as Android Auto does, instead of letting it play on to no one. onTakenBack = { CarServices.pausePhoneMedia("the car switched to another source") }, ) - openChannel(media.serviceId) + if (CarSettings.pipeAudio.value) { + openChannel(media.serviceId) + } else { + // Many cars switch their source to Android Auto the moment this channel opens, which + // cut off music the driver chose to play over Bluetooth. Open it only if they switch. + log.i("music goes over Bluetooth: the media channel stays closed, so the car keeps its own source") + CarAudio.openSink = { + CarAudio.openSink = null + openChannel(media.serviceId) + } + } } /** The car's directions channel, where [DirectionsVoice] reads out each turn. */ diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarAudio.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarAudio.kt index 21c6cf2..ca5ec70 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarAudio.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarAudio.kt @@ -44,6 +44,9 @@ object CarAudio { @Volatile private var link: Link? = null + /** Opens the car's media channel, left closed while music went over Bluetooth; null once open. */ + @Volatile var openSink: (() -> Unit)? = null + /** Rate and channel count the capture should use: the car's, or a sensible default. */ val sampleRate get() = link?.sampleRate ?: 48_000 val channels get() = link?.channels ?: 2 @@ -71,6 +74,7 @@ object CarAudio { when (_capture.value) { Capture.ON, Capture.ASKING, Capture.DENIED -> return Capture.OFF -> if (link == null) { + openSink?.invoke() GearslipLog.i("audio: waiting for the car to offer an audio sink") } else { ask()