Screen sharing stops when the car leaves

Nothing stopped the audio capture once the drive was over: CarAudio.release() only ran when
the activity went away, and through a wireless dongle it stays open, so the phone kept sharing
its screen and stayed muted (issue #43). A wireless session that drops also never closed the
music channel, since nothing calls stop() until the next session. run() now closes it, and
when the music channel closes the capture stops unless a car comes back within 3 seconds.
This commit is contained in:
Davide-Resigotti committed 2026-11-06 14:09:20 +01:00
1 parent c389c1c586
commit 18a655e990
2 files changed
+30 -1

No files matched your search

@@ -173,6 +173,10 @@ class GearslipRunner(
reportDisconnect()
} finally {
running = false
// The car is gone, but nothing calls stop() when a wireless session drops: it waits
// for the next one. Close the music channel now, so the audio capture behind it,
// and the screen sharing Android shows for it, stop too (issue #43).
if (!stopped) audioLink?.close()
writer.shutdown()
if (drainWriterOnExit) writer.awaitTermination(2, TimeUnit.SECONDS)
writer.shutdownNow()
@@ -54,7 +54,21 @@ object CarAudio {
val sampleRate get() = link?.sampleRate ?: 48_000
val channels get() = link?.channels ?: 2
private val main by lazy { android.os.Handler(android.os.Looper.getMainLooper()) }
/**
* The car left and none came back: stop capturing, which ends the screen sharing Android
* shows for it and gives the phone its speaker back. Nothing else stopped it once the drive
* was over; with a wireless dongle Gearslip's screen stays open, so it ran on (issue #43).
*/
private val releaseWhenGone = Runnable {
if (link != null || _capture.value == Capture.OFF) return@Runnable
GearslipLog.i("audio: no car for ${CAR_GONE_MS / 1000}s - stopping the capture and screen sharing")
release()
}
fun attach(link: Link) {
main.removeCallbacks(releaseWhenGone)
this.link = link
GearslipLog.i("audio: the car offered a sink at ${link.sampleRate}Hz x${link.channels}")
when {
@@ -63,10 +77,20 @@ object CarAudio {
}
}
/**
* The car's music channel closed. A car that comes straight back (a cable that slips) keeps
* the capture it had, so the driver isn't asked again mid-drive.
*/
fun detach(link: Link) {
if (this.link === link) this.link = null
if (this.link !== link) return
this.link = null
main.removeCallbacks(releaseWhenGone)
main.postDelayed(releaseWhenGone, CAR_GONE_MS)
}
/** How long after the car leaves the capture waits for it to come back: a cable that slips. */
private const val CAR_GONE_MS = 3_000L
/**
* The car wants sound. Safe to call as often as the session likes: the consent dialog appears
* at most once, and only once there is a sink to play through - asking on the bench, where the
@@ -117,6 +141,7 @@ object CarAudio {
/** The car went away. Everything is torn down; the next connection asks again. */
fun release() {
main.removeCallbacks(releaseWhenGone)
wanted = false
toldBlocked = false
AudioCaptureService.stop()