@@ -26,6 +26,7 @@ import androidx.compose.runtime.Composable
2626import androidx.compose.runtime.Composition
2727import androidx.compose.runtime.CompositionContext
2828import androidx.compose.runtime.CompositionLocalProvider
29+ import androidx.compose.runtime.LaunchedEffect
2930import androidx.compose.runtime.Stable
3031import androidx.compose.runtime.getValue
3132import androidx.compose.runtime.mutableStateOf
@@ -35,6 +36,7 @@ import androidx.compose.runtime.rememberCoroutineScope
3536import androidx.compose.runtime.rememberUpdatedState
3637import androidx.compose.runtime.setValue
3738import androidx.compose.ui.Modifier
39+ import androidx.compose.ui.platform.LocalContext
3840import androidx.compose.ui.platform.LocalInspectionMode
3941import androidx.compose.ui.viewinterop.AndroidView
4042import androidx.lifecycle.Lifecycle
@@ -53,9 +55,11 @@ import com.google.maps.android.compose.meta.AttributionId
5355import com.google.maps.android.ktx.awaitMap
5456import kotlinx.coroutines.CoroutineScope
5557import kotlinx.coroutines.CoroutineStart
58+ import kotlinx.coroutines.Dispatchers
5659import kotlinx.coroutines.Job
5760import kotlinx.coroutines.awaitCancellation
5861import kotlinx.coroutines.launch
62+ import kotlinx.coroutines.withContext
5963
6064/* *
6165 * A compose container for a [MapView].
@@ -109,101 +113,118 @@ public fun GoogleMap(
109113 return
110114 }
111115
112- // rememberUpdatedState and friends are used here to make these values observable to
113- // the subcomposition without providing a new content function each recomposition
114- val mapClickListeners = remember { MapClickListeners () }.also {
115- it.indoorStateChangeListener = indoorStateChangeListener
116- it.onMapClick = onMapClick
117- it.onMapLongClick = onMapLongClick
118- it.onMapLoaded = onMapLoaded
119- it.onMyLocationButtonClick = onMyLocationButtonClick
120- it.onMyLocationClick = onMyLocationClick
121- it.onPOIClick = onPOIClick
122- }
116+ var isInitialized by remember { mutableStateOf(false ) }
117+ val context = LocalContext .current
123118
124- val mapUpdaterState = remember {
125- MapUpdaterState (
126- mergeDescendants,
127- contentDescription,
128- cameraPositionState,
129- contentPadding,
130- locationSource,
131- properties,
132- uiSettings,
133- mapColorScheme?.value,
134- )
135- }.also {
136- it.mergeDescendants = mergeDescendants
137- it.contentDescription = contentDescription
138- it.cameraPositionState = cameraPositionState
139- it.contentPadding = contentPadding
140- it.locationSource = locationSource
141- it.mapProperties = properties
142- it.mapUiSettings = uiSettings
143- it.mapColorScheme = mapColorScheme?.value
119+ LaunchedEffect (Unit ) {
120+ withContext(Dispatchers .IO ) {
121+ MapsApiSettings .addInternalUsageAttributionId(context, AttributionId .VALUE )
122+ }
123+ isInitialized = true
144124 }
145125
146- val parentComposition = rememberCompositionContext()
147- val currentContent by rememberUpdatedState(content)
148- var subcompositionJob by remember { mutableStateOf<Job ?>(null ) }
149- val parentCompositionScope = rememberCoroutineScope()
150-
151- AndroidView (
152- modifier = modifier,
153- factory = { context ->
154- MapView (context, googleMapOptionsFactory()) .also { mapView ->
155- MapsApiSettings .addInternalUsageAttributionId(context, AttributionId .VALUE )
156- val componentCallbacks = object : ComponentCallbacks2 {
157- override fun onConfigurationChanged (newConfig : Configuration ) {}
158- @Deprecated(" Deprecated in Java" , ReplaceWith (" onTrimMemory(level)" ))
159- override fun onLowMemory () { mapView.onLowMemory() }
160- override fun onTrimMemory (level : Int ) { mapView.onLowMemory() }
161- }
162- context.registerComponentCallbacks(componentCallbacks)
163-
164- val lifecycleObserver = MapLifecycleEventObserver (mapView)
126+ if (isInitialized) {
127+ // rememberUpdatedState and friends are used here to make these values observable to
128+ // the subcomposition without providing a new content function each recomposition
129+ val mapClickListeners = remember { MapClickListeners () }.also {
130+ it.indoorStateChangeListener = indoorStateChangeListener
131+ it.onMapClick = onMapClick
132+ it.onMapLongClick = onMapLongClick
133+ it.onMapLoaded = onMapLoaded
134+ it.onMyLocationButtonClick = onMyLocationButtonClick
135+ it.onMyLocationClick = onMyLocationClick
136+ it.onPOIClick = onPOIClick
137+ }
165138
166- mapView.tag = MapTagData (componentCallbacks, lifecycleObserver)
139+ val mapUpdaterState = remember {
140+ MapUpdaterState (
141+ mergeDescendants,
142+ contentDescription,
143+ cameraPositionState,
144+ contentPadding,
145+ locationSource,
146+ properties,
147+ uiSettings,
148+ mapColorScheme?.value,
149+ )
150+ }.also {
151+ it.mergeDescendants = mergeDescendants
152+ it.contentDescription = contentDescription
153+ it.cameraPositionState = cameraPositionState
154+ it.contentPadding = contentPadding
155+ it.locationSource = locationSource
156+ it.mapProperties = properties
157+ it.mapUiSettings = uiSettings
158+ it.mapColorScheme = mapColorScheme?.value
159+ }
167160
168- // Only register for [lifecycleOwner]'s lifecycle events while MapView is attached
169- val onAttachStateListener = object : View .OnAttachStateChangeListener {
170- private var lifecycle: Lifecycle ? = null
161+ val parentComposition = rememberCompositionContext()
162+ val currentContent by rememberUpdatedState(content)
163+ var subcompositionJob by remember { mutableStateOf<Job ?>(null ) }
164+ val parentCompositionScope = rememberCoroutineScope()
165+
166+ AndroidView (
167+ modifier = modifier,
168+ factory = { context ->
169+ MapView (context, googleMapOptionsFactory()).also { mapView ->
170+ MapsApiSettings .addInternalUsageAttributionId(context, AttributionId .VALUE )
171+ val componentCallbacks = object : ComponentCallbacks2 {
172+ override fun onConfigurationChanged (newConfig : Configuration ) {}
173+
174+ @Deprecated(" Deprecated in Java" , ReplaceWith (" onTrimMemory(level)" ))
175+ override fun onLowMemory () {
176+ mapView.onLowMemory()
177+ }
171178
172- override fun onViewAttachedToWindow (mapView : View ) {
173- lifecycle = mapView.findViewTreeLifecycleOwner()!! .lifecycle.also {
174- it.addObserver(lifecycleObserver)
179+ override fun onTrimMemory (level : Int ) {
180+ mapView.onLowMemory()
175181 }
176182 }
183+ context.registerComponentCallbacks(componentCallbacks)
184+
185+ val lifecycleObserver = MapLifecycleEventObserver (mapView)
186+
187+ mapView.tag = MapTagData (componentCallbacks, lifecycleObserver)
188+
189+ // Only register for [lifecycleOwner]'s lifecycle events while MapView is attached
190+ val onAttachStateListener = object : View .OnAttachStateChangeListener {
191+ private var lifecycle: Lifecycle ? = null
177192
178- override fun onViewDetachedFromWindow (v : View ) {
179- lifecycle?.removeObserver(lifecycleObserver)
180- lifecycle = null
181- lifecycleObserver.moveToBaseState()
193+ override fun onViewAttachedToWindow (mapView : View ) {
194+ lifecycle = mapView.findViewTreeLifecycleOwner()!! .lifecycle.also {
195+ it.addObserver(lifecycleObserver)
196+ }
197+ }
198+
199+ override fun onViewDetachedFromWindow (v : View ) {
200+ lifecycle?.removeObserver(lifecycleObserver)
201+ lifecycle = null
202+ lifecycleObserver.moveToBaseState()
203+ }
182204 }
183- }
184205
185- mapView.addOnAttachStateChangeListener(onAttachStateListener)
186- }
187- },
188- onReset = { /* View is detached. */ },
189- onRelease = { mapView ->
190- val (componentCallbacks, lifecycleObserver) = mapView.tagData
191- mapView.context.unregisterComponentCallbacks(componentCallbacks)
192- lifecycleObserver.moveToDestroyedState()
193- mapView.tag = null
194- },
195- update = { mapView ->
196- if (subcompositionJob == null ) {
197- subcompositionJob = parentCompositionScope.launchSubcomposition(
198- mapUpdaterState,
199- parentComposition,
200- mapView,
201- mapClickListeners,
202- currentContent,
203- )
204- }
205- }
206- )
206+ mapView.addOnAttachStateChangeListener(onAttachStateListener)
207+ }
208+ },
209+ onReset = { /* View is detached. */ },
210+ onRelease = { mapView ->
211+ val (componentCallbacks, lifecycleObserver) = mapView.tagData
212+ mapView.context.unregisterComponentCallbacks(componentCallbacks)
213+ lifecycleObserver.moveToDestroyedState()
214+ mapView.tag = null
215+ },
216+ update = { mapView ->
217+ if (subcompositionJob == null ) {
218+ subcompositionJob = parentCompositionScope.launchSubcomposition(
219+ mapUpdaterState,
220+ parentComposition,
221+ mapView,
222+ mapClickListeners,
223+ currentContent,
224+ )
225+ }
226+ })
227+ }
207228}
208229
209230/* *
0 commit comments