From cec98fcde61252a311c718ef897029f9ebc1c886 Mon Sep 17 00:00:00 2001 From: Seb3thehacker Date: Thu, 5 Nov 2026 17:14:28 -0800 Subject: [PATCH] Opening a media app that can't connect no longer drops the music you were listening to --- .../app/seb3thehacker/gearslip/car/CarServices.kt | 15 +++++++++++---- .../app/seb3thehacker/gearslip/media/CarMedia.kt | 5 +++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarServices.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarServices.kt index 4be7150..f92f993 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarServices.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarServices.kt @@ -37,7 +37,13 @@ object CarServices { private const val REPAUSE_MS = 2_000L val nav: CarAppConnection by lazy { CarAppConnection(requireNotNull(context)) } - val media: CarMedia by lazy { CarMedia(requireNotNull(context)) } + val media: CarMedia by lazy { + CarMedia(requireNotNull(context)).also { m -> + // Remembered only once it connects: an app that refuses (NewPipe without Notification + // access) must not replace the one the driver was listening to on the next drive. + m.onReady = { app -> CarSettings.setLastMedia(app.component.flattenToString()) } + } + } /** [nav], or null before the car screen has set things up. */ fun navOrNull(): CarAppConnection? = if (context != null) nav else null @@ -67,7 +73,7 @@ object CarServices { private var autoplayPending = false /** - * The app that was playing when the driver opened another one. Opening an app is only a look + * The app that was playing, or paused with a track loaded, when the driver opened another one. Opening an app is only a look * at its library: until something plays there, the music carries on in this one, so leaving * the new app's screen goes back to it, as Android Auto does. */ @@ -138,13 +144,14 @@ object CarServices { // --- media --------------------------------------------------------------------------- fun openMedia(app: MediaApp, autoplay: Boolean = false) { - CarSettings.setLastMedia(app.component.flattenToString()) val phase = media.phase.value val live = phase == CarMedia.Phase.READY || phase == CarMedia.Phase.CONNECTING if (_mediaApp.value?.component == app.component && live) return val previous = _mediaApp.value - stillPlaying = previous?.takeIf { it.component != app.component && media.now.value.playing && !autoplay } + // Paused counts too: a track loaded there is still the music the driver is listening to. + val loaded = media.now.value.let { it.playing || it.title.isNotEmpty() } + stillPlaying = previous?.takeIf { it.component != app.component && loaded && !autoplay } _mediaApp.value = app autoplayPending = autoplay media.connect(app) diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/media/CarMedia.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/media/CarMedia.kt index c6f44b7..68fdf48 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/media/CarMedia.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/media/CarMedia.kt @@ -188,6 +188,9 @@ class CarMedia(private val context: Context) { /** The music app Gearslip is attached to, for the assistant to name; null when there's none. */ val appLabel: String? get() = connecting?.label?.takeIf { controller != null } + /** Called once an app is connected and can be played, library or not. */ + var onReady: ((MediaApp) -> Unit)? = null + private val _phase = MutableStateFlow(Phase.IDLE) val phase: StateFlow = _phase.asStateFlow() @@ -354,6 +357,7 @@ class CarMedia(private val context: Context) { attach(session) _browse.value = BrowseState(loading = false, unavailable = true) _phase.value = Phase.READY + onReady?.invoke(app) } private fun onConnected(app: MediaApp) { @@ -366,6 +370,7 @@ class CarMedia(private val context: Context) { mb.extras?.let { childStyles[mb.root] = it.getInt(STYLE_BROWSABLE, 0) to it.getInt(STYLE_PLAYABLE, 0) } path.clear() _phase.value = Phase.READY + onReady?.invoke(app) open(mb.root) }