From 18a655e99037fbb88437b495d4da11baa76a79d9 Mon Sep 17 00:00:00 2001 From: Davide-Resigotti Date: Fri, 6 Nov 2026 14:09:20 +0100 Subject: [PATCH] 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. --- .../seb3thehacker/gearslip/GearslipRunner.kt | 4 +++ .../seb3thehacker/gearslip/audio/CarAudio.kt | 27 ++++++++++++++++++- 2 files changed, 30 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 f18e867..71a2a71 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt @@ -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() 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 859ceab..5b1da9d 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarAudio.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarAudio.kt @@ -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()