[go: nahoru, domu]

Skip to content

Commit

Permalink
fix: Ensure CameraPositionState map reference clears when GoogleMap l… (
Browse files Browse the repository at this point in the history
#109)

* fix: Ensure CameraPositionState map reference clears when GoogleMap leaves composition.

Change-Id: Iccf615e1a468ea7fbddb652052cec51b5d98a763

* Adding test for GoogleMap coming in and out of the Composition.

Change-Id: I4e4bcdd39561ca17515e4c4891742c50bd14e409

* PR feedback.

Change-Id: I4e7ce6dc006cee72f49e29b7eb15303ae71e4654
  • Loading branch information
arriolac committed May 6, 2022
1 parent 3ce97d8 commit 2f09c6d
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
package com.google.maps.android.compose

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.annotation.UiThreadTest
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import org.junit.Assert.assertEquals
Expand All @@ -44,15 +44,7 @@ class GoogleMapViewTests {

private lateinit var cameraPositionState: CameraPositionState

@Before
fun setUp() {
cameraPositionState = CameraPositionState(
position = CameraPosition.fromLatLngZoom(
startingPosition,
startingZoom
)
)

private fun initMap(content: @Composable () -> Unit = {}) {
val countDownLatch = CountDownLatch(1)
composeTestRule.setContent {
GoogleMapView(
Expand All @@ -61,19 +53,33 @@ class GoogleMapViewTests {
onMapLoaded = {
countDownLatch.countDown()
}
)
) {
content.invoke()
}
}
val mapLoaded = countDownLatch.await(30, TimeUnit.SECONDS)
assertTrue("Map loaded", mapLoaded)
}

@Before
fun setUp() {
cameraPositionState = CameraPositionState(
position = CameraPosition.fromLatLngZoom(
startingPosition,
startingZoom
)
)
}

@Test
fun testStartingCameraPosition() {
initMap()
startingPosition.assertEquals(cameraPositionState.position.target)
}

@Test
fun testCameraReportsMoving() {
initMap()
zoom(shouldAnimate = true, zoomIn = true) {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
Expand All @@ -84,6 +90,7 @@ class GoogleMapViewTests {

@Test
fun testCameraReportsNotMoving() {
initMap()
zoom(shouldAnimate = true, zoomIn = true) {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
Expand All @@ -97,6 +104,7 @@ class GoogleMapViewTests {

@Test
fun testCameraZoomInAnimation() {
initMap()
zoom(shouldAnimate = true, zoomIn = true) {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
Expand All @@ -114,6 +122,7 @@ class GoogleMapViewTests {

@Test
fun testCameraZoomIn() {
initMap()
zoom(shouldAnimate = false, zoomIn = true) {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
Expand All @@ -131,6 +140,7 @@ class GoogleMapViewTests {

@Test
fun testCameraZoomOut() {
initMap()
zoom(shouldAnimate = false, zoomIn = false) {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
Expand All @@ -148,6 +158,7 @@ class GoogleMapViewTests {

@Test
fun testCameraZoomOutAnimation() {
initMap()
zoom(shouldAnimate = true, zoomIn = false) {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
Expand All @@ -164,48 +175,49 @@ class GoogleMapViewTests {
}

@Test
@UiThreadTest
fun testLatLngInVisibleRegion() {
val projection = cameraPositionState.projection
assertNotNull(projection)
assertTrue(
projection!!.visibleRegion.latLngBounds.contains(startingPosition)
)
initMap()
composeTestRule.runOnUiThread {
val projection = cameraPositionState.projection
assertNotNull(projection)
assertTrue(
projection!!.visibleRegion.latLngBounds.contains(startingPosition)
)
}
}

@Test
@UiThreadTest
fun testLatLngNotInVisibleRegion() {
val projection = cameraPositionState.projection
assertNotNull(projection)
val latLng = LatLng(23.4, 25.6)
assertFalse(
projection!!.visibleRegion.latLngBounds.contains(latLng)
)
initMap()
composeTestRule.runOnUiThread {
val projection = cameraPositionState.projection
assertNotNull(projection)
val latLng = LatLng(23.4, 25.6)
assertFalse(
projection!!.visibleRegion.latLngBounds.contains(latLng)
)
}
}

@Test(expected = IllegalStateException::class)
fun testMarkerStateCannotBeReused() {
val countDownLatch = CountDownLatch(1)
composeTestRule.setContent {
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
onMapLoaded = {
countDownLatch.countDown()
}
) {
val markerState = rememberMarkerState()
Marker(
state = markerState
)
Marker(
state = markerState
)
}
initMap {
val markerState = rememberMarkerState()
Marker(
state = markerState
)
Marker(
state = markerState
)
}
val mapLoaded = countDownLatch.await(30, TimeUnit.SECONDS)
assertTrue(mapLoaded)
}

@Test
fun testCameraPositionStateMapClears() {
initMap()
composeTestRule.onNodeWithTag("toggleMapVisibility")
.performClick()
.performClick()
}

private fun zoom(
Expand Down
140 changes: 68 additions & 72 deletions app/src/main/java/com/google/maps/android/compose/MapSampleActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ fun GoogleMapView(
modifier: Modifier,
cameraPositionState: CameraPositionState,
onMapLoaded: () -> Unit,
content: @Composable () -> Unit = {}
) {
val singaporeState = rememberMarkerState(position = singapore)
val singapore2State = rememberMarkerState(position = singapore2)
Expand All @@ -123,73 +124,78 @@ fun GoogleMapView(
var mapProperties by remember {
mutableStateOf(MapProperties(mapType = MapType.NORMAL))
}
var mapVisible by remember { mutableStateOf(true) }

GoogleMap(
modifier = modifier,
cameraPositionState = cameraPositionState,
properties = mapProperties,
uiSettings = uiSettings,
onMapLoaded = onMapLoaded,
onPOIClick = {
Log.d(TAG, "POI clicked: ${it.name}")
}
) {
// Drawing on the map is accomplished with a child-based API
val markerClick: (Marker) -> Boolean = {
Log.d(TAG, "${it.title} was clicked")
cameraPositionState.projection?.let { projection ->
Log.d(TAG, "The current projection is: $projection")
if (mapVisible) {
GoogleMap(
modifier = modifier,
cameraPositionState = cameraPositionState,
properties = mapProperties,
uiSettings = uiSettings,
onMapLoaded = onMapLoaded,
onPOIClick = {
Log.d(TAG, "POI clicked: ${it.name}")
}
false
}
MarkerInfoWindowContent(
state = singaporeState,
title = "Zoom in has been tapped $ticker times.",
onClick = markerClick,
draggable = true,
) {
Text(it.title ?: "Title", color = Color.Red)
}
MarkerInfoWindowContent(
state = singapore2State,
title = "Marker with custom info window.\nZoom in has been tapped $ticker times.",
icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE),
onClick = markerClick,
) {
Text(it.title ?: "Title", color = Color.Blue)
// Drawing on the map is accomplished with a child-based API
val markerClick: (Marker) -> Boolean = {
Log.d(TAG, "${it.title} was clicked")
cameraPositionState.projection?.let { projection ->
Log.d(TAG, "The current projection is: $projection")
}
false
}
MarkerInfoWindowContent(
state = singaporeState,
title = "Zoom in has been tapped $ticker times.",
onClick = markerClick,
draggable = true,
) {
Text(it.title ?: "Title", color = Color.Red)
}
MarkerInfoWindowContent(
state = singapore2State,
title = "Marker with custom info window.\nZoom in has been tapped $ticker times.",
icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE),
onClick = markerClick,
) {
Text(it.title ?: "Title", color = Color.Blue)
}
Marker(
state = singapore3State,
title = "Marker in Singapore",
onClick = markerClick
)
Circle(
center = circleCenter,
fillColor = MaterialTheme.colors.secondary,
strokeColor = MaterialTheme.colors.secondaryVariant,
radius = 1000.0,
)
content()
}
Marker(
state = singapore3State,
title = "Marker in Singapore",
onClick = markerClick
)
Circle(
center = circleCenter,
fillColor = MaterialTheme.colors.secondary,
strokeColor = MaterialTheme.colors.secondaryVariant,
radius = 1000.0,
)
}

}
Column {
MapTypeControls(onMapTypeClick = {
Log.d("GoogleMap", "Selected map type $it")
mapProperties = mapProperties.copy(mapType = it)
})
Button(
modifier = Modifier.padding(4.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.onPrimary,
contentColor = MaterialTheme.colors.primary
),
onClick = {
mapProperties = mapProperties.copy(mapType = MapType.NORMAL)
cameraPositionState.position = defaultCameraPosition
singaporeState.position = singapore
singaporeState.hideInfoWindow()
}
) {
Text(text = "RESET MAP", style = MaterialTheme.typography.body1)
Row {
MapButton(
text = "Reset Map",
onClick = {
mapProperties = mapProperties.copy(mapType = MapType.NORMAL)
cameraPositionState.position = defaultCameraPosition
singaporeState.position = singapore
singaporeState.hideInfoWindow()
}
)
MapButton(
text = "Toggle Map",
onClick = { mapVisible = !mapVisible },
modifier = Modifier.testTag("toggleMapVisibility"),
)
}
val coroutineScope = rememberCoroutineScope()
ZoomControls(
Expand Down Expand Up @@ -242,18 +248,8 @@ private fun MapTypeControls(
}

@Composable
private fun MapTypeButton(type: MapType, onClick: () -> Unit) {
Button(
modifier = Modifier.padding(4.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.onPrimary,
contentColor = MaterialTheme.colors.primary
),
onClick = onClick
) {
Text(text = type.toString(), style = MaterialTheme.typography.body1)
}
}
private fun MapTypeButton(type: MapType, onClick: () -> Unit) =
MapButton(text = type.toString(), onClick = onClick)

@Composable
private fun ZoomControls(
Expand Down Expand Up @@ -284,16 +280,16 @@ private fun ZoomControls(
}

@Composable
private fun MapButton(text: String, onClick: () -> Unit) {
private fun MapButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
Button(
modifier = Modifier.padding(8.dp),
modifier = modifier.padding(4.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.onPrimary,
contentColor = MaterialTheme.colors.primary
),
onClick = onClick
) {
Text(text = text, style = MaterialTheme.typography.h5)
Text(text = text, style = MaterialTheme.typography.body1)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.google.android.gms.maps.model.Polyline
internal interface MapNode {
fun onAttached() {}
fun onRemoved() {}
fun onCleared() {}
}

private object MapNodeRoot : MapNode
Expand All @@ -43,6 +44,8 @@ internal class MapApplier(

override fun onClear() {
map.clear()
decorations.forEach { it.onCleared() }
decorations.clear()
}

override fun insertBottomUp(index: Int, instance: MapNode) {
Expand Down
Loading

0 comments on commit 2f09c6d

Please sign in to comment.