Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class GoogleMapViewTests {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
}
composeTestRule.waitUntil(1000) {
composeTestRule.waitUntil(3000) {
!cameraPositionState.isMoving
}
assertEquals(
Expand All @@ -118,7 +118,7 @@ class GoogleMapViewTests {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
}
composeTestRule.waitUntil(1000) {
composeTestRule.waitUntil(3000) {
!cameraPositionState.isMoving
}
assertEquals(
Expand All @@ -135,7 +135,7 @@ class GoogleMapViewTests {
composeTestRule.waitUntil(1000) {
cameraPositionState.isMoving
}
composeTestRule.waitUntil(1000) {
composeTestRule.waitUntil(3000) {
!cameraPositionState.isMoving
}
assertEquals(
Expand Down Expand Up @@ -184,6 +184,30 @@ class GoogleMapViewTests {
)
}

@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
)
}
}
val mapLoaded = countDownLatch.await(30, TimeUnit.SECONDS)
assertTrue(mapLoaded)
}

private fun zoom(
shouldAnimate: Boolean,
zoomIn: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,12 @@ fun GoogleMapView(
cameraPositionState: CameraPositionState,
onMapLoaded: () -> Unit,
) {
val singaporePositionState = rememberMarkerPositionState(position = singapore)
val singapore2PositionState = rememberMarkerPositionState(position = singapore2)
var circlePositionState by remember { mutableStateOf(singapore) }
if (singaporePositionState.dragState == DragState.END) {
circlePositionState = singaporePositionState.position
val singaporeState = rememberMarkerState(position = singapore)
val singapore2State = rememberMarkerState(position = singapore2)
val singapore3State = rememberMarkerState(position = singapore3)
var circleCenter by remember { mutableStateOf(singapore) }
if (singaporeState.dragState == DragState.END) {
circleCenter = singaporeState.position
}

var uiSettings by remember { mutableStateOf(MapUiSettings(compassEnabled = false)) }
Expand Down Expand Up @@ -142,28 +143,28 @@ fun GoogleMapView(
false
}
MarkerInfoWindowContent(
positionState = singaporePositionState,
state = singaporeState,
title = "Zoom in has been tapped $ticker times.",
onClick = markerClick,
draggable = true,
) {
Text(it.title ?: "Title", color = Color.Red)
}
MarkerInfoWindowContent(
positionState = singapore2PositionState,
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(
positionState = MarkerPositionState(position = singapore3),
state = singapore3State,
title = "Marker in Singapore",
onClick = markerClick
)
Circle(
center = circlePositionState,
center = circleCenter,
fillColor = MaterialTheme.colors.secondary,
strokeColor = MaterialTheme.colors.secondaryVariant,
radius = 1000.0,
Expand All @@ -184,7 +185,8 @@ fun GoogleMapView(
onClick = {
mapProperties = mapProperties.copy(mapType = MapType.NORMAL)
cameraPositionState.position = defaultCameraPosition
singaporePositionState.position = singapore
singaporeState.position = singapore
singaporeState.hideInfoWindow()
}
) {
Text(text = "RESET MAP", style = MaterialTheme.typography.body1)
Expand Down Expand Up @@ -219,7 +221,7 @@ fun GoogleMapView(
uiSettings = uiSettings.copy(zoomControlsEnabled = it)
}
)
DebugView(cameraPositionState, singaporePositionState)
DebugView(cameraPositionState, singaporeState)
}
}

Expand Down Expand Up @@ -298,7 +300,7 @@ private fun MapButton(text: String, onClick: () -> Unit) {
@Composable
private fun DebugView(
cameraPositionState: CameraPositionState,
markerPositionState: MarkerPositionState
markerState: MarkerState
) {
Column(
Modifier
Expand All @@ -311,8 +313,8 @@ private fun DebugView(
Text(text = "Camera position is ${cameraPositionState.position}")
Spacer(modifier = Modifier.height(4.dp))
val dragging =
if (markerPositionState.dragState == DragState.DRAG) "dragging" else "not dragging"
if (markerState.dragState == DragState.DRAG) "dragging" else "not dragging"
Text(text = "Marker is $dragging")
Text(text = "Marker position is ${markerPositionState.position}")
Text(text = "Marker position is ${markerState.position}")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,22 @@ internal class MapApplier(
map.setOnMarkerDragListener(object : GoogleMap.OnMarkerDragListener {
override fun onMarkerDrag(marker: Marker) {
with(decorations.nodeForMarker(marker)) {
this?.markerPositionState?.position = marker.position
this?.markerPositionState?.dragState = DragState.DRAG
this?.markerState?.position = marker.position
this?.markerState?.dragState = DragState.DRAG
}
}

override fun onMarkerDragEnd(marker: Marker) {
with(decorations.nodeForMarker(marker)) {
this?.markerPositionState?.position = marker.position
this?.markerPositionState?.dragState = DragState.END
this?.markerState?.position = marker.position
this?.markerState?.dragState = DragState.END
}
}

override fun onMarkerDragStart(marker: Marker) {
with(decorations.nodeForMarker(marker)) {
this?.markerPositionState?.position = marker.position
this?.markerPositionState?.dragState = DragState.START
this?.markerState?.position = marker.position
this?.markerState?.dragState = DragState.START
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,19 @@ import com.google.maps.android.ktx.addMarker
internal class MarkerNode(
val compositionContext: CompositionContext,
val marker: Marker,
val markerPositionState: MarkerPositionState,
val markerState: MarkerState,
var onMarkerClick: (Marker) -> Boolean,
var onInfoWindowClick: (Marker) -> Unit,
var onInfoWindowClose: (Marker) -> Unit,
var onInfoWindowLongClick: (Marker) -> Unit,
var infoWindow: (@Composable (Marker) -> Unit)?,
var infoContent: (@Composable (Marker) -> Unit)?,
) : MapNode {
override fun onAttached() {
markerState.marker = marker
}
override fun onRemoved() {
markerState.marker = null
marker.remove()
}
}
Expand All @@ -57,7 +61,7 @@ enum class DragState {
*
* @param position the initial marker position
*/
class MarkerPositionState(
class MarkerState(
position: LatLng = LatLng(0.0, 0.0)
) {
/**
Expand All @@ -71,30 +75,54 @@ class MarkerPositionState(
var dragState: DragState by mutableStateOf(DragState.END)
internal set

// The marker associated with this MarkerState.
internal var marker: Marker? = null
set(value) {
if (field == null && value == null) return
if (field != null && value != null) {
error("MarkerState may only be associated with one Marker at a time.")
}
field = value
}

/**
* Shows the info window for the underlying marker
*/
fun showInfoWindow() {
marker?.showInfoWindow()
}

/**
* Hides the info window for the underlying marker
*/
fun hideInfoWindow() {
marker?.hideInfoWindow()
}

companion object {
/**
* The default saver implementation for [MarkerPositionState]
* The default saver implementation for [MarkerState]
*/
val Saver = Saver<MarkerPositionState, LatLng>(
val Saver = Saver<MarkerState, LatLng>(
save = { it.position },
restore = { MarkerPositionState(it) }
restore = { MarkerState(it) }
)
}
}

@Composable
fun rememberMarkerPositionState(
fun rememberMarkerState(
key: String? = null,
position: LatLng = LatLng(0.0, 0.0)
): MarkerPositionState = rememberSaveable(key = key, saver = MarkerPositionState.Saver) {
MarkerPositionState(position)
): MarkerState = rememberSaveable(key = key, saver = MarkerState.Saver) {
MarkerState(position)
}

/**
* A composable for a marker on the map.
*
* @param positionState the [MarkerPositionState] to be used to control or observe the marker
* position state
* @param state the [MarkerState] to be used to control or observe the marker
* state such as its position and info window
* @param alpha the alpha (opacity) of the marker
* @param anchor the anchor for the marker image
* @param draggable sets the draggability for the marker
Expand All @@ -114,7 +142,7 @@ fun rememberMarkerPositionState(
*/
@Composable
fun Marker(
positionState: MarkerPositionState = rememberMarkerPositionState(),
state: MarkerState = rememberMarkerState(),
alpha: Float = 1.0f,
anchor: Offset = Offset(0.5f, 1.0f),
draggable: Boolean = false,
Expand All @@ -133,7 +161,7 @@ fun Marker(
onInfoWindowLongClick: (Marker) -> Unit = {},
) {
MarkerImpl(
positionState = positionState,
state = state,
alpha = alpha,
anchor = anchor,
draggable = draggable,
Expand All @@ -158,8 +186,8 @@ fun Marker(
* customized. If this customization is not required, use
* [com.google.maps.android.compose.Marker].
*
* @param positionState the [MarkerPositionState] to be used to control or observe the marker
* position state
* @param state the [MarkerState] to be used to control or observe the marker
* state such as its position and info window
* @param alpha the alpha (opacity) of the marker
* @param anchor the anchor for the marker image
* @param draggable sets the draggability for the marker
Expand All @@ -181,7 +209,7 @@ fun Marker(
*/
@Composable
fun MarkerInfoWindow(
positionState: MarkerPositionState = rememberMarkerPositionState(),
state: MarkerState = rememberMarkerState(),
alpha: Float = 1.0f,
anchor: Offset = Offset(0.5f, 1.0f),
draggable: Boolean = false,
Expand All @@ -201,7 +229,7 @@ fun MarkerInfoWindow(
content: (@Composable (Marker) -> Unit)? = null
) {
MarkerImpl(
positionState = positionState,
state = state,
alpha = alpha,
anchor = anchor,
draggable = draggable,
Expand All @@ -227,8 +255,8 @@ fun MarkerInfoWindow(
* customized. If this customization is not required, use
* [com.google.maps.android.compose.Marker].
*
* @param positionState the [MarkerPositionState] to be used to control or observe the marker
* position state
* @param state the [MarkerState] to be used to control or observe the marker
* state such as its position and info window
* @param alpha the alpha (opacity) of the marker
* @param anchor the anchor for the marker image
* @param draggable sets the draggability for the marker
Expand All @@ -250,7 +278,7 @@ fun MarkerInfoWindow(
*/
@Composable
fun MarkerInfoWindowContent(
positionState: MarkerPositionState = rememberMarkerPositionState(),
state: MarkerState = rememberMarkerState(),
alpha: Float = 1.0f,
anchor: Offset = Offset(0.5f, 1.0f),
draggable: Boolean = false,
Expand All @@ -270,7 +298,7 @@ fun MarkerInfoWindowContent(
content: (@Composable (Marker) -> Unit)? = null
) {
MarkerImpl(
positionState = positionState,
state = state,
alpha = alpha,
anchor = anchor,
draggable = draggable,
Expand All @@ -294,8 +322,8 @@ fun MarkerInfoWindowContent(
/**
* Internal implementation for a marker on a Google map.
*
* @param positionState the [MarkerPositionState] to be used to control or observe the marker
* position state
* @param state the [MarkerState] to be used to control or observe the marker
* state such as its position and info window
* @param alpha the alpha (opacity) of the marker
* @param anchor the anchor for the marker image
* @param draggable sets the draggability for the marker
Expand All @@ -320,7 +348,7 @@ fun MarkerInfoWindowContent(
*/
@Composable
private fun MarkerImpl(
positionState: MarkerPositionState = rememberMarkerPositionState(),
state: MarkerState = rememberMarkerState(),
alpha: Float = 1.0f,
anchor: Offset = Offset(0.5f, 1.0f),
draggable: Boolean = false,
Expand Down Expand Up @@ -351,7 +379,7 @@ private fun MarkerImpl(
flat(flat)
icon(icon)
infoWindowAnchor(infoWindowAnchor.x, infoWindowAnchor.y)
position(positionState.position)
position(state.position)
rotation(rotation)
snippet(snippet)
title(title)
Expand All @@ -362,7 +390,7 @@ private fun MarkerImpl(
MarkerNode(
compositionContext = compositionContext,
marker = marker,
markerPositionState = positionState,
markerState = state,
onMarkerClick = onClick,
onInfoWindowClick = onInfoWindowClick,
onInfoWindowClose = onInfoWindowClose,
Expand All @@ -385,7 +413,7 @@ private fun MarkerImpl(
set(flat) { this.marker.isFlat = it }
set(icon) { this.marker.setIcon(it) }
set(infoWindowAnchor) { this.marker.setInfoWindowAnchor(it.x, it.y) }
set(positionState.position) { this.marker.position = it }
set(state.position) { this.marker.position = it }
set(rotation) { this.marker.rotation = it }
set(snippet) {
this.marker.snippet = it
Expand Down