From d0adc749cce8edaa75069fe31fc1688ed58c37be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Lo=CC=81pez=20Man=CC=83as?= Date: Fri, 14 Aug 2026 20:03:02 +0700 Subject: [PATCH 1/2] feat: expose info window customization for advanced markers AdvancedMarkerImpl already supported customizing a marker's info window via infoWindow/infoContent, wiring them into MarkerNode correctly, but the public AdvancedMarker composable never exposed those parameters. Add AdvancedMarkerInfoWindow and AdvancedMarkerInfoWindowContent, mirroring the existing MarkerInfoWindow/MarkerInfoWindowContent public API for regular markers. Fixes #822 --- .../android/compose/GoogleMapViewTests.kt | 43 +++++ .../com/google/maps/android/compose/Marker.kt | 164 ++++++++++++++++++ 2 files changed, 207 insertions(+) diff --git a/maps-app/src/androidTest/java/com/google/maps/android/compose/GoogleMapViewTests.kt b/maps-app/src/androidTest/java/com/google/maps/android/compose/GoogleMapViewTests.kt index abdd2a467..e9689180b 100644 --- a/maps-app/src/androidTest/java/com/google/maps/android/compose/GoogleMapViewTests.kt +++ b/maps-app/src/androidTest/java/com/google/maps/android/compose/GoogleMapViewTests.kt @@ -243,6 +243,49 @@ class GoogleMapViewTests { } } + @Test + fun testAdvancedMarkerInfoWindowContentDoesNotCrash() { + // Regression test for https://github.com/googlemaps/android-maps-compose/issues/822: + // AdvancedMarker didn't expose infoContent/infoWindow customization, even though the + // underlying implementation already supported it. Custom info window content is + // rendered to a bitmap by ComposeInfoWindowAdapter (not present in the Compose + // semantics tree), so this asserts the composable wires up and shows without crashing + // rather than asserting on-screen content. + lateinit var markerState: MarkerState + + initMap { + markerState = rememberUpdatedMarkerState(position = startingPosition) + AdvancedMarkerInfoWindowContent(state = markerState) { + Text(text = "custom advanced marker info window") + } + } + + composeTestRule.runOnUiThread { + markerState.showInfoWindow() + } + composeTestRule.waitForIdle() + } + + @Test + fun testAdvancedMarkerInfoWindowDoesNotCrash() { + // Regression test for https://github.com/googlemaps/android-maps-compose/issues/822: + // see testAdvancedMarkerInfoWindowContentDoesNotCrash for why this doesn't assert + // on-screen content. + lateinit var markerState: MarkerState + + initMap { + markerState = rememberUpdatedMarkerState(position = startingPosition) + AdvancedMarkerInfoWindow(state = markerState) { + Text(text = "custom advanced marker whole info window") + } + } + + composeTestRule.runOnUiThread { + markerState.showInfoWindow() + } + composeTestRule.waitForIdle() + } + @Test(expected = IllegalStateException::class) fun testMarkerStateInsideMarkerInfoWindowComposableCannotBeReused() { initMap { diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt b/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt index f2333f8c0..40d329b82 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt @@ -778,6 +778,170 @@ public fun AdvancedMarker( ) } +/** + * A composable for an advanced marker on the map wherein its entire info window can be + * customized. If this customization is not required, use + * [com.google.maps.android.compose.AdvancedMarker]. + * + * @param state the [MarkerState] to be used to control or observe the marker + * state such as its position and info window + * @param contentDescription the content description for accessibility purposes + * @param alpha the alpha (opacity) of the marker + * @param anchor the anchor for the marker image + * @param draggable sets the draggability for the marker + * @param flat sets if the marker should be flat against the map + * @param infoWindowAnchor the anchor point of the info window on the marker image + * @param rotation the rotation of the marker in degrees clockwise about the marker's anchor point + * @param snippet the snippet for the marker + * @param tag optional tag to associate with the marker + * @param title the title for the marker + * @param visible the visibility of the marker + * @param zIndex the z-index of the marker + * @param onClick a lambda invoked when the marker is clicked + * @param onInfoWindowClick a lambda invoked when the marker's info window is clicked + * @param onInfoWindowClose a lambda invoked when the marker's info window is closed + * @param onInfoWindowLongClick a lambda invoked when the marker's info window is long clicked + * @param icon sets the icon for the marker + * @param pinConfig the PinConfig object that will be used for the advanced marker + * @param iconView the custom view to be used on the advanced marker + * @param collisionBehavior the expected collision behavior + * @param content optional composable lambda expression for customizing the + * info window's content + */ +@Composable +@GoogleMapComposable +public fun AdvancedMarkerInfoWindow( + state: MarkerState = rememberUpdatedMarkerState(), + contentDescription: String? = "", + alpha: Float = 1.0f, + anchor: Offset = Offset(0.5f, 1.0f), + draggable: Boolean = false, + flat: Boolean = false, + infoWindowAnchor: Offset = Offset(0.5f, 0.0f), + rotation: Float = 0.0f, + snippet: String? = null, + tag: Any? = null, + title: String? = null, + visible: Boolean = true, + zIndex: Float = 0.0f, + onClick: (Marker) -> Boolean = { false }, + onInfoWindowClick: (Marker) -> Unit = {}, + onInfoWindowClose: (Marker) -> Unit = {}, + onInfoWindowLongClick: (Marker) -> Unit = {}, + icon: BitmapDescriptor? = null, + pinConfig: PinConfig? = null, + iconView: View? = null, + collisionBehavior: Int = AdvancedMarkerOptions.CollisionBehavior.REQUIRED, + content: (@UiComposable @Composable (Marker) -> Unit)? = null, +) { + AdvancedMarkerImpl( + state = state, + contentDescription = contentDescription, + alpha = alpha, + anchor = anchor, + draggable = draggable, + flat = flat, + infoWindowAnchor = infoWindowAnchor, + rotation = rotation, + snippet = snippet, + tag = tag, + title = title, + visible = visible, + zIndex = zIndex, + onClick = onClick, + onInfoWindowClick = onInfoWindowClick, + onInfoWindowClose = onInfoWindowClose, + onInfoWindowLongClick = onInfoWindowLongClick, + icon = icon, + pinConfig = pinConfig, + iconView = iconView, + collisionBehavior = collisionBehavior, + infoWindow = content, + ) +} + +/** + * A composable for an advanced marker on the map wherein its info window contents can be + * customized. If this customization is not required, use + * [com.google.maps.android.compose.AdvancedMarker]. + * + * @param state the [MarkerState] to be used to control or observe the marker + * state such as its position and info window + * @param contentDescription the content description for accessibility purposes + * @param alpha the alpha (opacity) of the marker + * @param anchor the anchor for the marker image + * @param draggable sets the draggability for the marker + * @param flat sets if the marker should be flat against the map + * @param infoWindowAnchor the anchor point of the info window on the marker image + * @param rotation the rotation of the marker in degrees clockwise about the marker's anchor point + * @param snippet the snippet for the marker + * @param tag optional tag to associate with the marker + * @param title the title for the marker + * @param visible the visibility of the marker + * @param zIndex the z-index of the marker + * @param onClick a lambda invoked when the marker is clicked + * @param onInfoWindowClick a lambda invoked when the marker's info window is clicked + * @param onInfoWindowClose a lambda invoked when the marker's info window is closed + * @param onInfoWindowLongClick a lambda invoked when the marker's info window is long clicked + * @param icon sets the icon for the marker + * @param pinConfig the PinConfig object that will be used for the advanced marker + * @param iconView the custom view to be used on the advanced marker + * @param collisionBehavior the expected collision behavior + * @param content optional composable lambda expression for customizing the + * info window's content + */ +@Composable +@GoogleMapComposable +public fun AdvancedMarkerInfoWindowContent( + state: MarkerState = rememberUpdatedMarkerState(), + contentDescription: String? = "", + alpha: Float = 1.0f, + anchor: Offset = Offset(0.5f, 1.0f), + draggable: Boolean = false, + flat: Boolean = false, + infoWindowAnchor: Offset = Offset(0.5f, 0.0f), + rotation: Float = 0.0f, + snippet: String? = null, + tag: Any? = null, + title: String? = null, + visible: Boolean = true, + zIndex: Float = 0.0f, + onClick: (Marker) -> Boolean = { false }, + onInfoWindowClick: (Marker) -> Unit = {}, + onInfoWindowClose: (Marker) -> Unit = {}, + onInfoWindowLongClick: (Marker) -> Unit = {}, + icon: BitmapDescriptor? = null, + pinConfig: PinConfig? = null, + iconView: View? = null, + collisionBehavior: Int = AdvancedMarkerOptions.CollisionBehavior.REQUIRED, + content: (@UiComposable @Composable (Marker) -> Unit)? = null, +) { + AdvancedMarkerImpl( + state = state, + contentDescription = contentDescription, + alpha = alpha, + anchor = anchor, + draggable = draggable, + flat = flat, + infoWindowAnchor = infoWindowAnchor, + rotation = rotation, + snippet = snippet, + tag = tag, + title = title, + visible = visible, + zIndex = zIndex, + onClick = onClick, + onInfoWindowClick = onInfoWindowClick, + onInfoWindowClose = onInfoWindowClose, + onInfoWindowLongClick = onInfoWindowLongClick, + icon = icon, + pinConfig = pinConfig, + iconView = iconView, + collisionBehavior = collisionBehavior, + infoContent = content, + ) +} + /** * Internal implementation for an advanced marker on a Google map. * From 9da12b099dbc05d91fab469fb1789dbac0eeda9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Lo=CC=81pez=20Man=CC=83as?= Date: Fri, 14 Aug 2026 22:34:46 +0700 Subject: [PATCH 2/2] docs: showcase AdvancedMarkerInfoWindowContent in the marker demo Demonstrates the new public API from #822 by using it for Marker 1 in AdvancedMarkersActivity. --- .../markerexamples/AdvancedMarkersActivity.kt | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/maps-app/src/main/java/com/google/maps/android/compose/markerexamples/AdvancedMarkersActivity.kt b/maps-app/src/main/java/com/google/maps/android/compose/markerexamples/AdvancedMarkersActivity.kt index 54db3e221..9746e3b44 100644 --- a/maps-app/src/main/java/com/google/maps/android/compose/markerexamples/AdvancedMarkersActivity.kt +++ b/maps-app/src/main/java/com/google/maps/android/compose/markerexamples/AdvancedMarkersActivity.kt @@ -24,13 +24,18 @@ import android.widget.TextView import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.material3.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color as ComposeColor +import androidx.compose.ui.unit.dp import com.google.android.gms.maps.GoogleMapOptions import com.google.android.gms.maps.MapsInitializer import com.google.android.gms.maps.OnMapsSdkInitializedCallback @@ -40,6 +45,7 @@ import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.Marker import com.google.android.gms.maps.model.PinConfig import com.google.maps.android.compose.AdvancedMarker +import com.google.maps.android.compose.AdvancedMarkerInfoWindowContent import com.google.maps.android.compose.GoogleMap import com.google.maps.android.compose.MapProperties import com.google.maps.android.compose.MapType @@ -138,13 +144,24 @@ class AdvancedMarkersActivity : ComponentActivity(), OnMapsSdkInitializedCallbac .setBorderColor(Color.WHITE) .build() - AdvancedMarker( + // AdvancedMarkerInfoWindowContent lets you customize the info window's + // content while keeping the default info window frame. Tap this marker + // to see it. + AdvancedMarkerInfoWindowContent( state = marker1State, onClick = markerClick, collisionBehavior = 1, pinConfig = pinConfig, - title="Marker 1" - ) + title = "Marker 1" + ) { + Box( + modifier = Modifier + .background(ComposeColor.Yellow) + .padding(8.dp) + ) { + Text("Custom advanced marker info window!") + } + } val glyphOne = PinConfig.Glyph("A", Color.BLACK) val pinConfig2 = PinConfig.builder()