This is to fix the drawing after coming back from the media menu to the navigtion app. For organic maps the change from yesterday was fine. For flitsmeister it freezes the screen.

This commit is contained in:
Brian5647 committed 2026-10-01 19:03:45 +02:00
1 parent 5f557a5973
commit fbaeb0cc57
2 files changed
+40 -19

No files matched your search

@@ -83,12 +83,16 @@ fun CarHome() {
val navigator = LocalCarNavigator.current val navigator = LocalCarNavigator.current
val panel = navigator.sidePanel val panel = navigator.sidePanel
// Coming back to the map from another screen: make sure the nav app is drawing into a live // Leaving the map for another screen: hand the nav app a fresh surface while it's covered, so
// surface. The first composition is skipped, since the surface is only just being lent. // it has redrawn by the time the map comes back - a new surface shows black until the app's
// next frame. Coming back only replaces one that died meanwhile. Nothing happens before Home
// has first been shown, since the surface is only just being lent.
val onHome = navigator.current == CarScreen.Home val onHome = navigator.current == CarScreen.Home
val seenHome = remember { booleanArrayOf(false) } val seenHome = remember { booleanArrayOf(false) }
LaunchedEffect(onHome) { LaunchedEffect(onHome) {
if (onHome && seenHome[0]) CarServices.nav.refreshSurface() if (seenHome[0]) {
if (onHome) CarServices.nav.replaceDeadSurface() else CarServices.nav.renewSurface()
}
if (onHome) seenHome[0] = true if (onHome) seenHome[0] = true
} }
val splitScreen = phase == CarMedia.Phase.READY && now.isActive && panel != SidePanel.NONE val splitScreen = phase == CarMedia.Phase.READY && now.isActive && panel != SidePanel.NONE
@@ -455,31 +455,48 @@ class CarAppConnection(private val context: Context) {
private val _surfaceEpoch = MutableStateFlow(0) private val _surfaceEpoch = MutableStateFlow(0)
/** Bumped when the lent surface turns out to be dead, so the stage builds a fresh one. */ /** Bumped to throw the lent surface away, so the stage builds a fresh one. */
val surfaceEpoch: StateFlow<Int> = _surfaceEpoch.asStateFlow() val surfaceEpoch: StateFlow<Int> = _surfaceEpoch.asStateFlow()
/** /**
* Lends the app its surface again: called when the map comes back into view. An app that * Lends the app a fresh surface: called as the map goes out of view, so the app has redrawn
* draws through its own VirtualDisplay (Organic Maps does) can be left pointing at a surface * on the new one by the time it comes back. An app that draws through its own VirtualDisplay
* that has since died, and draws black until it's told about a live one. Taking the surface * (Organic Maps does) can be left pointing at a surface that has since died, and draws black
* back and lending it again repoints it; a surface that has itself died is replaced. * until it's told about a live one.
*
* Always a new surface, never the same one lent again: such an app (Flitsmeister does) builds
* its new display on the re-lent surface before releasing the old, and the old one's
* disconnect then takes the shared BufferQueue's connection with it - the map freezes on its
* last frame for good. The stage's key on [surfaceEpoch] swaps in a new SurfaceView, which
* lends itself as it comes up.
*/ */
fun refreshSurface() { fun renewSurface() {
main.post { main.post {
val current = lent ?: return@post val current = lent ?: return@post
if (!current.surface.isValid) { GearslipLog.i("host: building a fresh surface as the map goes out of view")
GearslipLog.w("host: the lent surface died; building a new one") replaceSurface(current)
lent = null
sendSurfaceDestroyed(current)
_surfaceEpoch.value = _surfaceEpoch.value + 1
return@post
}
GearslipLog.i("host: lending the surface again as the map comes back")
sendSurfaceDestroyed(current)
sendSurfaceAvailable()
} }
} }
/**
* Called as the map comes back into view: replaces the surface only if it died while the map
* was covered, since a new one shows black until the app's next frame.
*/
fun replaceDeadSurface() {
main.post {
val current = lent ?: return@post
if (current.surface.isValid) return@post
GearslipLog.w("host: the lent surface died; building a new one")
replaceSurface(current)
}
}
private fun replaceSurface(current: Lent) {
lent = null
sendSurfaceDestroyed(current)
_surfaceEpoch.value = _surfaceEpoch.value + 1
}
private fun sendSurfaceAvailable() { private fun sendSurfaceAvailable() {
val callback = surfaceCallback ?: return val callback = surfaceCallback ?: return
val surface = lent ?: return val surface = lent ?: return