Skip to content

feat(utils): update android-maps-utils to 6.1.0 and expose clustering executors/contexts - #1047

Open
dkhawk wants to merge 2 commits into
mainfrom
feat/maps-utils-6.1.0
Open

dkhawk wants to merge 2 commits into
mainfrom
feat/maps-utils-6.1.0

Conversation

@dkhawk

@dkhawk dkhawk commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Stack

Summary

  • Bump com.google.maps.android:android-maps-utils and com.google.maps.android:android-maps-utils-core from 6.0.0 to 6.1.0.
  • Expose mainContext: CoroutineContext = Dispatchers.Main and backgroundContext: CoroutineContext = Dispatchers.Default in rememberClusterManager to match the coroutine context injection introduced in android-maps-utils 6.1.0 (feat(clustering): allow injecting coroutine contexts into ClusterManager android-maps-utils#1822).
  • Expose executor: Executor? = null in rememberClusterRenderer and forward it through ReportingDefaultClusterRenderer and ComposeUiClusterRenderer to DefaultClusterRenderer (feat: add constructor parameter for custom Executor in cluster renderers android-maps-utils#1682).
  • Key DisposableEffect and LaunchedEffects in Clustering.kt on clusterManager so cluster items are not cleared and re-clustered on every items update prior to LaunchedEffect, and recreating clusterManager updates active effects.
  • Override onClusterUpdated and onClusterItemUpdated in ComposeUiClusterRenderer so cached markers retain custom Compose anchor, zIndex, and rotation when updated (rendering the item bitmap only when creating the view).
  • Add unit tests in ClusterRendererTest.kt.

BEGIN_COMMIT_OVERRIDE
feat(utils): update android-maps-utils to 6.1.0 and expose clustering executors/contexts
fix(clustering): key DisposableEffect and LaunchedEffects on clusterManager
fix(clustering): preserve custom marker properties across onClusterUpdated and onClusterItemUpdated
END_COMMIT_OVERRIDE

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Coverage (unit tests)

Compared against db20d0d on main (#1056).

Module Line % Change Branch % Change
maps-compose 15.28% no change 3.76% no change
maps-compose-utils 14.45% 🔺 +8.36 7.99% 🔺 +3.52
maps-compose-widgets 0.00% no change 0.00% no change
TOTAL 14.30% 🔺 +1.89 4.52% 🔺 +0.73

Line and branch coverage from unit test reports. History is recorded on the coverage-history branch after each merge to main.

@googlemaps-bot

googlemaps-bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Coverage (instrumentation tests)

Overall Project 43.62% -1.37% 🍏
Files changed 55.7% ❌

Module Coverage
maps-app 49.5% -9.24% ❌
Files
Module File Coverage
maps-app ClusterRenderer.kt 67.49% -18.73% ❌
Clustering.kt 44.12% -6.23% ❌

@dkhawk
dkhawk marked this pull request as ready for review October 21, 2026 19:12
@dkhawk
dkhawk requested a review from kikoso October 21, 2026 19:14
@dkhawk
dkhawk added this pull request to stack #1049 October 21, 2026 22:54

@kikoso kikoso left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you take a look at the comments, @dkhawk ? Since this bundles a feat with two fixes, let's add a BEGIN_COMMIT_OVERRIDE block so both fixes end up in the changelog. Thanks!

val viewInfo = keysToViews[ViewKey.Item(item)] ?: createAndAddView(ViewKey.Item(item))
applyClusterItemMarkerProperties(
marker = marker,
icon = renderViewToBitmapDescriptor(viewInfo.view),

@kikoso kikoso Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultClusterRenderer calls onClusterItemUpdated for every cached marker on each re-cluster, so this redraws every visible item's Compose view to a bitmap on the main thread after each zoom. The base implementation never touches the icon (only title, snippet, position and zIndex), so re-applying anchor, zIndex and rotation should be enough here, rendering only when createAndAddView had to create the view.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one is still pending: onClusterItemUpdated still calls renderViewToBitmapDescriptor on every update, even when the view already exists in keysToViews. The PR description says the bitmap is only rendered when creating the view, so maybe a change got lost in the squash?

}
}
DisposableEffect(itemsState) {
DisposableEffect(clusterManager) {

@kikoso kikoso Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keying this on clusterManager makes sense, but LaunchedEffect(itemsState) and LaunchedEffect(cameraPositionState) above still capture the first clusterManager, so after a change the items keep going to the old one. Now that rememberClusterManager recreates the manager when the contexts change this is easier to hit, could we add clusterManager to both keys?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The items effect is keyed on clusterManager now, thanks! LaunchedEffect(cameraPositionState) still captures the first manager though, so onCameraIdle() would keep going to the old one. Could we add clusterManager to that key too?

clusterManager ?: return null
MapEffect(context) { map ->
val renderer = ReportingDefaultClusterRenderer(context, map, clusterManager)
MapEffect(context, executor) { map ->

@kikoso kikoso Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being executor now an effect key, an unremembered Executor { it.run() } would recreate the renderer on every recomposition. Should we mention in the KDoc that it has to be remembered?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still worth a line in the KDoc, I think. The same applies now to mainContext and backgroundContext in rememberClusterManager, since something like Dispatchers.Default + job created inline would recreate the manager on every recomposition.

context = context,
map = map,
clusterManager = clusterManager,
executor = executor ?: Executors.newSingleThreadExecutor(),

@kikoso kikoso Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this repeats the default from DefaultClusterRenderer, we could pass executor only when it is not null and keep the default in one place.

clusterManager = clusterManager,
executor = customExecutor,
)
assertNotNull(renderer)

@kikoso kikoso Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This passes as long as the constructors don't throw, so it doesn't really verify the injection. A test for the actual fix would be more useful, i.e. that a custom zIndex survives onClusterItemUpdated.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new tests for applyClusterMarkerProperties are nice, but they cover the helper on its own. What I had in mind is a test that goes through onClusterItemUpdated and checks that a custom zIndex survives, which is the actual bug this fixes. Is that hard to set up with the mocks here?

}

testOptions {
unitTests.isReturnDefaultValues = true

@kikoso kikoso Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we need this for the whole module, since it makes any unmocked Android call return a default value and can hide errors in other tests. I would rather mock what ClusterRendererTest needs.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed? With the mockkStatic/mockkConstructor setup in ClusterRendererTest I would expect we can drop it and keep the rest of the module tests strict.

@dkhawk
dkhawk force-pushed the feat/maps-utils-6.1.0 branch 2 times, most recently from b1271aa to 46396fa Compare October 26, 2026 18:30
@dkhawk
dkhawk requested review from LoyalAbbas and kikoso October 26, 2026 19:29

@kikoso kikoso left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the BEGIN_COMMIT_OVERRIDE block, @dkhawk ! I think most threads from the previous round are still open though (e.g. the description mentions rendering only on view creation, but the code still renders on every update). Can you take a look? Thanks!

@dkhawk
dkhawk force-pushed the feat/maps-utils-6.1.0 branch from 46396fa to db03085 Compare October 29, 2026 17:21
* invalidated. Works by reporting invalidations from its inner AndroidComposeView.
*/
private class InvalidatingComposeView(
internal class InvalidatingComposeView(
… executors/contexts

- Bump com.google.maps.android:android-maps-utils and android-maps-utils-core to 6.1.0.
- Expose mainContext and backgroundContext in rememberClusterManager to match ClusterManager coroutine context injection introduced in android-maps-utils 6.1.0 (PR #1822).
- Expose executor: Executor? in rememberClusterRenderer and forward it through ReportingDefaultClusterRenderer and ComposeUiClusterRenderer to DefaultClusterRenderer (PR #1682).
- Fix DisposableEffect key in Clustering.kt to key on clusterManager instead of itemsState so cluster items are not cleared and re-clustered on every items update prior to LaunchedEffect.
- Override onClusterUpdated and onClusterItemUpdated in ComposeUiClusterRenderer so cached markers retain custom Compose icons, anchors, zIndex, and rotation when updated.
@dkhawk
dkhawk force-pushed the feat/maps-utils-6.1.0 branch from 45774c8 to b70b969 Compare October 29, 2026 21:25

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants