diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c3b5aa637..0063f72db 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,6 +48,9 @@ + diff --git a/app/src/main/java/com/google/maps/android/compose/MainActivity.kt b/app/src/main/java/com/google/maps/android/compose/MainActivity.kt index 42ef0c0e7..ea0c1a17b 100644 --- a/app/src/main/java/com/google/maps/android/compose/MainActivity.kt +++ b/app/src/main/java/com/google/maps/android/compose/MainActivity.kt @@ -102,6 +102,18 @@ class MainActivity : ComponentActivity() { Text(getString(R.string.map_in_column_activity)) } Spacer(modifier = Modifier.padding(5.dp)) + Button( + onClick = { + context.startActivity( + Intent( + context, + MapsInLazyColumnActivity::class.java + ) + ) + }) { + Text(getString(R.string.maps_in_lazy_column_activity)) + } + Spacer(modifier = Modifier.padding(5.dp)) Button( onClick = { context.startActivity( diff --git a/app/src/main/java/com/google/maps/android/compose/MapsInLazyColumnActivity.kt b/app/src/main/java/com/google/maps/android/compose/MapsInLazyColumnActivity.kt new file mode 100644 index 000000000..c53d3b6b1 --- /dev/null +++ b/app/src/main/java/com/google/maps/android/compose/MapsInLazyColumnActivity.kt @@ -0,0 +1,260 @@ +package com.google.maps.android.compose + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material.Card +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.LocalTextStyle +import androidx.compose.material.ProvideTextStyle +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.model.CameraPosition +import com.google.android.gms.maps.model.IndoorBuilding +import com.google.android.gms.maps.model.LatLng + +private data class CountryLocation(val name: String, val latLng: LatLng, val zoom: Float) + +private typealias MapItemId = String + +// From https://developers.google.com/public-data/docs/canonical/countries_csv +private val countries = listOf( + CountryLocation("Hong Kong", LatLng(22.396428, 114.109497), 5f), + CountryLocation("Madison Square Garden (has indoor mode)", LatLng(40.7504656, -73.9937246), 19.33f), + CountryLocation("Bolivia", LatLng(-16.290154, -63.588653), 5f), + CountryLocation("Ecuador", LatLng(-1.831239, -78.183406), 5f), + CountryLocation("Sweden", LatLng(60.128161, 18.643501), 5f), + CountryLocation("Eritrea", LatLng(15.179384, 39.782334), 5f), + CountryLocation("Portugal", LatLng(39.399872, -8.224454), 5f), + CountryLocation("Belgium", LatLng(50.503887, 4.469936), 5f), + CountryLocation("Slovakia", LatLng(48.669026, 19.699024), 5f), + CountryLocation("El Salvador", LatLng(13.794185, -88.89653), 5f), + CountryLocation("Bhutan", LatLng(27.514162, 90.433601), 5f), + CountryLocation("Saint Lucia", LatLng(13.909444, -60.978893), 5f), + CountryLocation("Uganda", LatLng(1.373333, 32.290275), 5f), + CountryLocation("South Africa", LatLng(-30.559482, 22.937506), 5f), + CountryLocation("Spain", LatLng(40.463667, -3.74922), 5f), + CountryLocation("Georgia", LatLng(42.315407, 43.356892), 5f), + CountryLocation("Burundi", LatLng(-3.373056, 29.918886), 5f) +) + +private data class MapListItem( + val title: String, + val location: LatLng, + val zoom: Float, + val id: MapItemId +) + +private val allItems = countries.mapIndexed { index, country -> + MapListItem(country.name, country.latLng, country.zoom, "MapInLazyColumn#$index") +} + +class MapsInLazyColumnActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + var showLazyColumn by rememberSaveable { mutableStateOf(true) } + var visibleItems by rememberSaveable { mutableStateOf(allItems) } + + fun setItemCount(count: Int) { + visibleItems = allItems.take(count.coerceIn(0, allItems.size)) + } + + Column { + Row( + Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + TextButton(onClick = { setItemCount(0) }) { + Text(text = "Clear") + } + TextButton(onClick = { setItemCount(visibleItems.size - 1) }) { + Text(text = "Remove") + } + TextButton(onClick = { showLazyColumn = !showLazyColumn }) { + Text(text = if (showLazyColumn) "Hide" else "Show") + } + TextButton(onClick = { setItemCount(visibleItems.size + 1) }) { + Text(text = "Add") + } + TextButton(onClick = { setItemCount(allItems.size) }) { + Text(text = "Fill") + } + } + if (showLazyColumn) { + Box(Modifier.border(1.dp, Color.LightGray.copy(0.5f))) { + MapsInLazyColumn(visibleItems) + } + } + } + } + } +} + +@Composable +private fun MapsInLazyColumn(mapItems: List) { + val lazyListState = rememberLazyListState() + + val cameraPositionStates = mapItems.associate { item -> + item.id to rememberCameraPositionState( + key = item.id, + init = { position = CameraPosition.fromLatLngZoom(item.location, item.zoom) } + ) + } + val visibleItemIds by remember(lazyListState) { + derivedStateOf { + lazyListState.layoutInfo.visibleItemsInfo.map { it.key as MapItemId } + } + } + val anyMapMoving by remember(cameraPositionStates) { + derivedStateOf { + visibleItemIds.any { cameraPositionStates[it]?.isMoving == true } + } + } + + Box { + LazyColumn( + state = lazyListState, + userScrollEnabled = !anyMapMoving + ) { + items(mapItems, key = { it.id }) { item -> + val cameraPositionState = cameraPositionStates[item.id]!! + + Box( + Modifier + .fillMaxWidth() + .height(300.dp), + contentAlignment = Alignment.Center + ) { + MapCard(item, cameraPositionState) + } + } + } + } +} + +@OptIn(MapsComposeExperimentalApi::class) +@Composable +private fun MapCard(item: MapListItem, cameraPositionState: CameraPositionState) { + Card( + Modifier.padding(16.dp), + elevation = 4.dp + ) { + var mapLoaded by remember { mutableStateOf(false) } + var buildingFocused: Boolean? by remember { mutableStateOf(null) } + var focusedBuildingInvocationCount by remember { mutableIntStateOf(0) } + var activatedIndoorLevel: String? by remember { mutableStateOf(null) } + var activatedIndoorLevelInvocationCount by remember { mutableIntStateOf(0) } + var onMapClickCount by remember { mutableIntStateOf(0) } + + var map: GoogleMap? by remember { mutableStateOf(null) } + + fun updateIndoorLevel() { + activatedIndoorLevel = map!!.focusedBuilding?.run { levels.getOrNull(activeLevelIndex)?.name } + } + + Box { + GoogleMap( + onMapClick = { + onMapClickCount++ + }, + properties = remember { + MapProperties( + isBuildingEnabled = true, + isIndoorEnabled = true + ) + }, + cameraPositionState = cameraPositionState, + onMapLoaded = { mapLoaded = true }, + indoorStateChangeListener = object : IndoorStateChangeListener { + override fun onIndoorBuildingFocused() { + super.onIndoorBuildingFocused() + focusedBuildingInvocationCount++ + buildingFocused = (map!!.focusedBuilding != null) + updateIndoorLevel() + } + + override fun onIndoorLevelActivated(building: IndoorBuilding) { + super.onIndoorLevelActivated(building) + activatedIndoorLevelInvocationCount++ + updateIndoorLevel() + } + } + ) { + MapEffect(Unit) { googleMap -> + map = googleMap + updateIndoorLevel() + buildingFocused = (googleMap.focusedBuilding != null) + } + } + + AnimatedVisibility(!mapLoaded, enter = fadeIn(), exit = fadeOut()) { + Box( + Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + + @Composable + fun TextWithBackground(text: String, fontWeight: FontWeight = FontWeight.Medium) { + Text( + modifier = Modifier.background(Color.White.copy(0.7f)), + text = text, + fontWeight = fontWeight, + fontSize = 10.sp + ) + } + + Column( + modifier = Modifier.align(Alignment.BottomStart) + ) { + TextWithBackground(item.title, fontWeight = FontWeight.Bold) + TextWithBackground("Map loaded: $mapLoaded") + TextWithBackground("Map click count: $onMapClickCount") + TextWithBackground("Building focused: $buildingFocused") + TextWithBackground("Building focused invocation count: $focusedBuildingInvocationCount") + TextWithBackground("Indoor level: $activatedIndoorLevel") + TextWithBackground("Indoor level invocation count: $activatedIndoorLevelInvocationCount") + } + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6638f6ee6..3993bcbab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,4 +32,5 @@ Street View Custom Location Button Accessibility + Maps in LazyColumn \ No newline at end of file diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt index df54fcd88..1c948c4c1 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt @@ -18,27 +18,29 @@ import android.content.ComponentCallbacks import android.content.res.Configuration import android.location.Location import android.os.Bundle +import android.view.View import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable import androidx.compose.runtime.Composition import androidx.compose.runtime.CompositionContext import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState +import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCompositionContext +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.findViewTreeLifecycleOwner import com.google.android.gms.maps.GoogleMapOptions import com.google.android.gms.maps.LocationSource import com.google.android.gms.maps.MapView @@ -46,7 +48,11 @@ import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.MapColorScheme import com.google.android.gms.maps.model.PointOfInterest import com.google.maps.android.ktx.awaitMap +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Job import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.launch /** * A compose container for a [MapView]. @@ -100,12 +106,6 @@ public fun GoogleMap( return } - val context = LocalContext.current - val mapView = remember { MapView(context, googleMapOptionsFactory()) } - - AndroidView(modifier = modifier, factory = { mapView }) - MapLifecycle(mapView) - // rememberUpdatedState and friends are used here to make these values observable to // the subcomposition without providing a new content function each recomposition val mapClickListeners = remember { MapClickListeners() }.also { @@ -117,125 +117,155 @@ public fun GoogleMap( it.onMyLocationClick = onMyLocationClick it.onPOIClick = onPOIClick } - val currentContentDescription by rememberUpdatedState(contentDescription) - val currentLocationSource by rememberUpdatedState(locationSource) - val currentCameraPositionState by rememberUpdatedState(cameraPositionState) - val currentContentPadding by rememberUpdatedState(contentPadding) - val currentUiSettings by rememberUpdatedState(uiSettings) - val currentMapProperties by rememberUpdatedState(properties) - val currentColorScheme by rememberUpdatedState(mapColorScheme) + + val mapUpdaterState = remember { + MapUpdaterState( + mergeDescendants, + contentDescription, + cameraPositionState, + contentPadding, + locationSource, + properties, + uiSettings, + mapColorScheme?.value, + ) + }.also { + it.mergeDescendants = mergeDescendants + it.contentDescription = contentDescription + it.cameraPositionState = cameraPositionState + it.contentPadding = contentPadding + it.locationSource = locationSource + it.mapProperties = properties + it.mapUiSettings = uiSettings + it.mapColorScheme = mapColorScheme?.value + } val parentComposition = rememberCompositionContext() val currentContent by rememberUpdatedState(content) - LaunchedEffect(Unit) { - disposingComposition { - mapView.newComposition(parentComposition, mapClickListeners) { - MapUpdater( - mergeDescendants = mergeDescendants, - contentDescription = currentContentDescription, - cameraPositionState = currentCameraPositionState, - contentPadding = currentContentPadding, - locationSource = currentLocationSource, - mapProperties = currentMapProperties, - mapUiSettings = currentUiSettings, - colorMapScheme = currentColorScheme?.value - ) + var subcompositionJob by remember { mutableStateOf(null) } + val parentCompositionScope = rememberCoroutineScope() - MapClickListenerUpdater() + AndroidView( + modifier = modifier, + factory = { context -> + MapView(context, googleMapOptionsFactory()).also { mapView -> + val componentCallbacks = object : ComponentCallbacks { + override fun onConfigurationChanged(newConfig: Configuration) {} + override fun onLowMemory() { mapView.onLowMemory() } + } + context.registerComponentCallbacks(componentCallbacks) - CompositionLocalProvider( - LocalCameraPositionState provides currentCameraPositionState, - currentContent + val lifecycleObserver = MapLifecycleEventObserver(mapView) + + mapView.tag = MapTagData(componentCallbacks, lifecycleObserver) + + // Only register for [lifecycleOwner]'s lifecycle events while MapView is attached + val onAttachStateListener = object : View.OnAttachStateChangeListener { + private var lifecycle: Lifecycle? = null + + override fun onViewAttachedToWindow(mapView: View) { + lifecycle = mapView.findViewTreeLifecycleOwner()!!.lifecycle.also { + it.addObserver(lifecycleObserver) + } + } + + override fun onViewDetachedFromWindow(v: View) { + lifecycle?.removeObserver(lifecycleObserver) + lifecycle = null + lifecycleObserver.moveToBaseState() + } + } + + mapView.addOnAttachStateChangeListener(onAttachStateListener) + } + }, + onReset = { /* View is detached. */ }, + onRelease = { mapView -> + val (componentCallbacks, lifecycleObserver) = mapView.tagData + mapView.context.unregisterComponentCallbacks(componentCallbacks) + lifecycleObserver.moveToDestroyedState() + mapView.tag = null + }, + update = { mapView -> + if (subcompositionJob == null) { + subcompositionJob = parentCompositionScope.launchSubcomposition( + mapUpdaterState, + parentComposition, + mapView, + mapClickListeners, + currentContent, ) } } - } -} - -internal suspend inline fun disposingComposition(factory: () -> Composition) { - val composition = factory() - try { - awaitCancellation() - } finally { - composition.dispose() - } + ) } -private suspend inline fun MapView.newComposition( - parent: CompositionContext, +/** + * Create and apply the [content] compositions to the map + + * dispose the [Composition] when the parent composable is disposed. + * */ +private fun CoroutineScope.launchSubcomposition( + mapUpdaterState: MapUpdaterState, + parentComposition: CompositionContext, + mapView: MapView, mapClickListeners: MapClickListeners, - noinline content: @Composable () -> Unit -): Composition { - val map = awaitMap() - return Composition( - MapApplier(map, this, mapClickListeners), parent - ).apply { - setContent(content) - } -} + content: @Composable @GoogleMapComposable () -> Unit, +): Job { + // Use [CoroutineStart.UNDISPATCHED] to kick off GoogleMap loading immediately + return launch(start = CoroutineStart.UNDISPATCHED) { + val map = mapView.awaitMap() + val composition = Composition( + applier = MapApplier(map, mapView, mapClickListeners), + parent = parentComposition + ) -/** - * Registers lifecycle observers to the local [MapView]. - */ -@Composable -private fun MapLifecycle(mapView: MapView) { - val context = LocalContext.current - val lifecycle = LocalLifecycleOwner.current.lifecycle - val previousState = remember { mutableStateOf(Lifecycle.Event.ON_CREATE) } - DisposableEffect(context, lifecycle, mapView) { - val mapLifecycleObserver = mapView.lifecycleObserver(previousState) - val callbacks = mapView.componentCallbacks() - - lifecycle.addObserver(mapLifecycleObserver) - context.registerComponentCallbacks(callbacks) - - onDispose { - lifecycle.removeObserver(mapLifecycleObserver) - context.unregisterComponentCallbacks(callbacks) - } - } - DisposableEffect(mapView) { - onDispose { - mapView.onDestroy() - mapView.removeAllViews() - } - } -} + try { + composition.setContent { + MapUpdater(mapUpdaterState) -private fun MapView.lifecycleObserver(previousState: MutableState): LifecycleEventObserver = - LifecycleEventObserver { _, event -> - event.targetState - when (event) { - Lifecycle.Event.ON_CREATE -> { - // Skip calling mapView.onCreate if the lifecycle did not go through onDestroy - in - // this case the GoogleMap composable also doesn't leave the composition. So, - // recreating the map does not restore state properly which must be avoided. - if (previousState.value != Lifecycle.Event.ON_STOP) { - this.onCreate(Bundle()) - } - } + MapClickListenerUpdater() - Lifecycle.Event.ON_START -> this.onStart() - Lifecycle.Event.ON_RESUME -> this.onResume() - Lifecycle.Event.ON_PAUSE -> this.onPause() - Lifecycle.Event.ON_STOP -> this.onStop() - Lifecycle.Event.ON_DESTROY -> { - //handled in onDispose + CompositionLocalProvider( + LocalCameraPositionState provides mapUpdaterState.cameraPositionState, + content + ) } - - else -> throw IllegalStateException() + awaitCancellation() + } finally { + composition.dispose() } - previousState.value = event } +} -private fun MapView.componentCallbacks(): ComponentCallbacks = - object : ComponentCallbacks { - override fun onConfigurationChanged(config: Configuration) {} +@Stable +internal class MapUpdaterState( + mergeDescendants: Boolean, + contentDescription: String?, + cameraPositionState: CameraPositionState, + contentPadding: PaddingValues, + locationSource: LocationSource?, + mapProperties: MapProperties, + mapUiSettings: MapUiSettings, + mapColorScheme: Int?, +) { + var mergeDescendants by mutableStateOf(mergeDescendants) + var contentDescription by mutableStateOf(contentDescription) + var cameraPositionState by mutableStateOf(cameraPositionState) + var contentPadding by mutableStateOf(contentPadding) + var locationSource by mutableStateOf(locationSource) + var mapProperties by mutableStateOf(mapProperties) + var mapUiSettings by mutableStateOf(mapUiSettings) + var mapColorScheme by mutableStateOf(mapColorScheme) +} - override fun onLowMemory() { - this@componentCallbacks.onLowMemory() - } - } +/** Used to store things in the tag which must be retrievable across recompositions */ +private data class MapTagData( + val componentCallbacks: ComponentCallbacks, + val lifecycleObserver: MapLifecycleEventObserver +) + +private val MapView.tagData: MapTagData + get() = tag as MapTagData public typealias GoogleMapFactory = @Composable () -> Unit @@ -276,6 +306,68 @@ public fun googleMapFactory( } } +private class MapLifecycleEventObserver(private val mapView: MapView) : LifecycleEventObserver { + private var currentLifecycleState: Lifecycle.State = Lifecycle.State.INITIALIZED + + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + when (event) { + // [mapView.onDestroy] is only invoked from AndroidView->onRelease. + Lifecycle.Event.ON_DESTROY -> moveToBaseState() + else -> moveToLifecycleState(event.targetState) + } + } + + /** + * Move down to [Lifecycle.State.CREATED] but only if [currentLifecycleState] is actually above that. + * It's theoretically possible that [currentLifecycleState] is still in [Lifecycle.State.INITIALIZED] state. + * */ + fun moveToBaseState() { + if (currentLifecycleState > Lifecycle.State.CREATED) { + moveToLifecycleState(Lifecycle.State.CREATED) + } + } + + fun moveToDestroyedState() { + if (currentLifecycleState > Lifecycle.State.INITIALIZED) { + moveToLifecycleState(Lifecycle.State.DESTROYED) + } + } + + private fun moveToLifecycleState(targetState: Lifecycle.State) { + while (currentLifecycleState != targetState) { + when { + currentLifecycleState < targetState -> moveUp() + currentLifecycleState > targetState -> moveDown() + } + } + } + + private fun moveDown() { + val event = Lifecycle.Event.downFrom(currentLifecycleState) + ?: error("no event down from $currentLifecycleState") + invokeEvent(event) + } + + private fun moveUp() { + val event = Lifecycle.Event.upFrom(currentLifecycleState) + ?: error("no event up from $currentLifecycleState") + invokeEvent(event) + } + + private fun invokeEvent(event: Lifecycle.Event) { + when (event) { + Lifecycle.Event.ON_CREATE -> mapView.onCreate(Bundle()) + Lifecycle.Event.ON_START -> mapView.onStart() + Lifecycle.Event.ON_RESUME -> mapView.onResume() + Lifecycle.Event.ON_PAUSE -> mapView.onPause() + Lifecycle.Event.ON_STOP -> mapView.onStop() + Lifecycle.Event.ON_DESTROY -> mapView.onDestroy() + else -> error("Unsupported lifecycle event: $event") + } + currentLifecycleState = event.targetState + } +} + /** * Enum representing a 1-1 mapping to [com.google.android.gms.maps.model.MapColorScheme]. * @@ -287,4 +379,4 @@ public enum class ComposeMapColorScheme(public val value: Int) { LIGHT(MapColorScheme.LIGHT), DARK(MapColorScheme.DARK), FOLLOW_SYSTEM(MapColorScheme.FOLLOW_SYSTEM); -} \ No newline at end of file +} diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt index 44a0b5df1..d30298434 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt @@ -25,8 +25,6 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.LayoutDirection import com.google.android.gms.maps.GoogleMap -import com.google.android.gms.maps.LocationSource -import com.google.android.gms.maps.model.MapColorScheme internal class MapPropertiesNode( val map: GoogleMap, @@ -97,16 +95,7 @@ public val DefaultMapContentPadding: PaddingValues = PaddingValues() @SuppressLint("MissingPermission") @Suppress("NOTHING_TO_INLINE") @Composable -internal inline fun MapUpdater( - mergeDescendants: Boolean = false, - contentDescription: String?, - cameraPositionState: CameraPositionState, - contentPadding: PaddingValues = DefaultMapContentPadding, - locationSource: LocationSource?, - mapProperties: MapProperties, - mapUiSettings: MapUiSettings, - colorMapScheme: Int?, -) { +internal inline fun MapUpdater(mapUpdaterState: MapUpdaterState) = with(mapUpdaterState) { val map = (currentComposer.applier as MapApplier).map val mapView = (currentComposer.applier as MapApplier).mapView if (mergeDescendants) { @@ -141,7 +130,7 @@ internal inline fun MapUpdater( set(mapProperties.mapType) { map.mapType = it.value } set(mapProperties.maxZoomPreference) { map.setMaxZoomPreference(it) } set(mapProperties.minZoomPreference) { map.setMinZoomPreference(it) } - set(colorMapScheme) { + set(mapColorScheme) { if (it != null) { map.mapColorScheme = it } diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetView.kt b/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetView.kt index e73c7d53f..98227a241 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetView.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetView.kt @@ -38,9 +38,9 @@ import androidx.lifecycle.LifecycleEventObserver import com.google.android.gms.maps.StreetViewPanoramaOptions import com.google.android.gms.maps.StreetViewPanoramaView import com.google.android.gms.maps.model.StreetViewPanoramaOrientation -import com.google.maps.android.compose.disposingComposition import com.google.maps.android.ktx.MapsExperimentalFeature import com.google.maps.android.ktx.awaitStreetViewPanorama +import kotlinx.coroutines.awaitCancellation /** * A composable for displaying a Street View for a given location. A location might not be available for a given @@ -131,6 +131,15 @@ private fun StreetViewLifecycle(streetView: StreetViewPanoramaView) { } } +private suspend inline fun disposingComposition(factory: () -> Composition) { + val composition = factory() + try { + awaitCancellation() + } finally { + composition.dispose() + } +} + private suspend inline fun StreetViewPanoramaView.newComposition( parent: CompositionContext, noinline content: @Composable () -> Unit