mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-06-06 04:55:07 -04:00
fix(map): scope cluster-renderer ViewTreeLifecycleOwner to map host view (#5708)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,18 @@
|
||||
package org.meshtastic.app.map.component
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.currentStateAsState
|
||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||
import androidx.savedstate.compose.LocalSavedStateRegistryOwner
|
||||
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
|
||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||
import com.google.maps.android.clustering.Cluster
|
||||
import com.google.maps.android.clustering.view.DefaultClusterRenderer
|
||||
import com.google.maps.android.compose.Circle
|
||||
@@ -36,6 +47,40 @@ fun NodeClusterMarkers(
|
||||
navigateToNodeDetails: (Int) -> Unit,
|
||||
onClusterClick: (Cluster<NodeClusterItem>) -> Boolean,
|
||||
) {
|
||||
val view = LocalView.current
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val savedStateRegistryOwner = LocalSavedStateRegistryOwner.current
|
||||
val lifecycleState by lifecycleOwner.lifecycle.currentStateAsState()
|
||||
|
||||
// maps-compose renders each non-clustered item to a bitmap through an off-screen ComposeView that
|
||||
// it attaches under the MapView (see ComposeUiClusterRenderer + NoDrawContainerView in
|
||||
// MapComposeViewRender). That ComposeView walks up the view tree for a ViewTreeLifecycleOwner and,
|
||||
// when it finds none, crashes with "Composed into the View which doesn't propagate
|
||||
// ViewTreeLifecycleOwner!" (googlemaps/android-maps-compose#875 / #325) — a FATAL on the map screen.
|
||||
//
|
||||
// Propagate the owners onto this map screen's host view (LocalView.current), which is an ancestor
|
||||
// of the internally-created MapView, so the renderer's ComposeView can resolve them. We deliberately
|
||||
// do NOT touch view.rootView (the activity root): attaching a transient NavEntry lifecycle there is
|
||||
// what caused the node-list popup regression (#5684), which is why #5704 removed the prior, broader
|
||||
// workaround entirely. Scoping to the map host view and restoring the previous owners on dispose
|
||||
// keeps the fix local to the map and leaves Popups/DropdownMenus untouched.
|
||||
DisposableEffect(view, lifecycleOwner, savedStateRegistryOwner) {
|
||||
val prevLifecycleOwner = view.findViewTreeLifecycleOwner()
|
||||
val prevSavedStateRegistryOwner = view.findViewTreeSavedStateRegistryOwner()
|
||||
view.setViewTreeLifecycleOwner(lifecycleOwner)
|
||||
view.setViewTreeSavedStateRegistryOwner(savedStateRegistryOwner)
|
||||
onDispose {
|
||||
view.setViewTreeLifecycleOwner(prevLifecycleOwner)
|
||||
view.setViewTreeSavedStateRegistryOwner(prevSavedStateRegistryOwner)
|
||||
}
|
||||
}
|
||||
|
||||
// The cluster renderer drives marker rendering from an async Handler (DefaultClusterRenderer's
|
||||
// MarkerModifier), which can fire after this screen has stopped and the internal ComposeView is
|
||||
// detached — at which point no owner is reachable regardless of the above. Skip rendering once the
|
||||
// lifecycle is no longer at least STARTED to close most of that race.
|
||||
if (!lifecycleState.isAtLeast(Lifecycle.State.STARTED)) return
|
||||
|
||||
Clustering(
|
||||
items = nodeClusterItems,
|
||||
onClusterClick = onClusterClick,
|
||||
|
||||
Reference in New Issue
Block a user