-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0) #1716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
029b92b
dd5dcbe
85ab029
68f7429
d33f2dc
fc7c984
7b0b32e
a0a837d
e68576b
9667221
36d824a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| { | ||
| ".": "5.2.0" | ||
| ".": "6.0.0-rc04" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,43 @@ | ||
| # Changelog | ||
|
|
||
| ## [6.0.0-rc04](https://github.com/googlemaps/android-maps-utils/compare/v6.0.0-rc03...v6.0.0-rc04) (2026-09-15) | ||
|
|
||
| ### Bug Fixes & Refactoring | ||
|
|
||
| * Address review feedback on coroutines, inlining, and file organization: | ||
| * Migrate all suspension functions from `suspendCoroutine` to `suspendCancellableCoroutine` to avoid continuation leaks | ||
| * Remove `inline` and `@file:Suppress("NOTHING_TO_INLINE")` on non-lambda functions across core utilities and KTX shims | ||
| * Separate collection Flow extensions and DSL helpers into dedicated `*Flows.kt` companion files | ||
| * Standardize copyright headers and normalize shim alias naming to camelCase | ||
| * Improve `GoogleMapTest` assertions and remove unused mocks | ||
| * Update dependencies and libraries for v6.0.0 release candidate | ||
|
|
||
|
|
||
| ## [6.0.0-rc03](https://github.com/googlemaps/android-maps-utils/compare/v6.0.0-rc02...v6.0.0-rc03) (2026-08-31) | ||
|
|
||
| ### Bug Fixes | ||
|
|
||
| * Resolve lint detector test Android SDK requirement with `allowMissingSdk()` | ||
| * Configure isolated test home directory for unit test execution in restricted sandboxes | ||
| * Suppress internal deprecation warnings in legacy KTX compatibility shims | ||
|
|
||
| ## [6.0.0-rc02](https://github.com/googlemaps/android-maps-utils/compare/v6.0.0-rc01...v6.0.0-rc02) (2026-08-28) | ||
|
|
||
| ### Chores | ||
|
|
||
| * Rebase on main branch | ||
| * Make GPG publication signing conditional for local maven publishing | ||
| * Update dependencies and Gradle wrapper | ||
|
|
||
| ## [6.0.0-rc01](https://github.com/googlemaps/android-maps-utils/compare/v5.2.0...v6.0.0-rc01) (2026-07-13) | ||
|
|
||
| ### Features & Breaking Changes (KTX Consolidation) | ||
|
|
||
| * **Consolidated Kotlin Extensions (KTX into Utils)**: Moved all functionality from `android-maps-ktx` (`maps-ktx` and `maps-utils-ktx`) directly into `android-maps-utils`. | ||
| * **Canonical Non-KTX Packages**: All reactive Coroutine/Flow extensions (`awaitMap()`, `mapClickEvents()`, `cameraMoveEvents()`) and option builder DSLs (`addMarker`, `addPolyline`, `addPolygon`) now reside in canonical `android-maps-utils` packages (`com.google.maps.android.*`, `com.google.maps.android.model.*`, `com.google.maps.android.collections.*`, `com.google.maps.android.clustering.*`, `com.google.maps.android.data.geojson.*`, `com.google.maps.android.data.kml.*`, `com.google.maps.android.heatmaps.*`). | ||
| * **Deprecated Compatibility Layer**: To ensure full backward compatibility for existing codebases, the legacy `com.google.maps.android.ktx.*` package structure has been preserved with `@Deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...))` forwarding wrappers and typealiases. Existing imports will continue to compile seamlessly with deprecation warnings pointing to the canonical replacements. | ||
| * **Demo & Test Consolidation**: Integrated full reactive extension demo (`KtxExtensionsDemoActivity`) into the `:demo` app and migrated all 18 unit test suites from `android-maps-ktx` with both canonical and shim test coverage. | ||
|
|
||
| ## [5.2.0](https://github.com/googlemaps/android-maps-utils/compare/v5.1.1...v5.2.0) (2026-08-28) | ||
|
|
||
|
|
||
|
|
@@ -23,12 +61,10 @@ | |
|
|
||
| ## [5.1.0](https://github.com/googlemaps/android-maps-utils/compare/v5.0.0...v5.1.0) (2026-08-05) | ||
|
|
||
|
|
||
| ### Features | ||
|
|
||
| * **data:** add support for parsing GroundOverlay with LatLonQuad bounds ([#1739](https://github.com/googlemaps/android-maps-utils/issues/1739)) ([c2a165d](https://github.com/googlemaps/android-maps-utils/commit/c2a165d03b63cdd12ac8abbd06e9717c854cd4d7)) | ||
|
|
||
|
|
||
| ### Bug Fixes | ||
|
|
||
| * apply polygon styling to GeoJSON MultiPolygon features ([#1727](https://github.com/googlemaps/android-maps-utils/issues/1727)) ([67d7252](https://github.com/googlemaps/android-maps-utils/commit/67d7252e5c1cdf1556b7fc707e80c8372540036c)) | ||
|
|
@@ -60,6 +96,7 @@ | |
| ### Bug Fixes | ||
|
|
||
| * prevent StackOverflowError when parsing deeply nested KML containers and multi-geometries ([#1710](https://github.com/googlemaps/android-maps-utils/issues/1710)) ([1463cc5](https://github.com/googlemaps/android-maps-utils/commit/1463cc572da85b8a2317690fa94b0e2432995a96)) | ||
| >>>>>>> origin/main | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there any purpose for adding this ?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Merge error, probably! |
||
|
|
||
| ## [4.5.1](https://github.com/googlemaps/android-maps-utils/compare/v4.5.0...v4.5.1) (2026-06-17) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,8 +30,10 @@ upgrading from 4.x, see the [migration guide](MIGRATION.md). | |
| - **Spherical geometry** — for example: computeDistance, computeHeading, | ||
| computeArea | ||
| - **Street View metadata** — checks if a Street View panorama exists at a given location | ||
| - **Reactive Kotlin Extensions & Builders** — coroutine suspensions (`awaitMap()`), reactive `Flow` observers (`mapClickEvents()`), and option builder DSLs (`addMarker { ... }`) consolidated directly into `com.google.maps.android.*`. | ||
|
|
||
| You can also find Kotlin extensions for this library in [Maps Android KTX][android-maps-ktx]. | ||
| > [!IMPORTANT] | ||
| > **KTX Consolidation Notice (`v6.0.0+`)**: All Kotlin extensions (`maps-ktx` and `maps-utils-ktx` from `android-maps-ktx`) are now built directly into `android-maps-utils` under the canonical `com.google.maps.android.*` packages. Separate dependencies on `android-maps-ktx` or `maps-utils-ktx` are no longer needed and should be removed. Legacy calls to `com.google.maps.android.ktx.*` packages remain supported via `@Deprecated(level = DeprecationLevel.WARNING)` bridges that forward directly to canonical implementations. | ||
|
|
||
| <p align="center"><img width="90%" vspace="20" src="https://cloud.githubusercontent.com/assets/1950036/6629704/f57bc6d8-c908-11e4-815a-0d909fe02f99.gif"></p> | ||
|
|
||
|
|
@@ -46,9 +48,7 @@ You can also find Kotlin extensions for this library in [Maps Android KTX][andro | |
|
|
||
| ```kotlin | ||
| dependencies { | ||
| // Utilities for Maps SDK for Android (requires Google Play Services) | ||
| // You do not need to add a separate dependency for the Maps SDK for Android | ||
| // since this library builds in the compatible version of the Maps SDK. | ||
| // Utilities and consolidated Kotlin Extensions for Maps SDK for Android | ||
| // The aggregator artifact transitively pulls in all submodules below. | ||
| implementation("com.google.maps.android:android-maps-utils:5.2.0") // x-release-please-version | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one still says 5.2.0 while every other marker got bumped to rc04. Another reason to let release-please handle all of them rather than bumping by hand. |
||
| } | ||
|
|
@@ -167,6 +167,65 @@ Full guides for using the utilities are published in | |
|
|
||
| </details> | ||
|
|
||
| <details> | ||
| <summary>Reactive Kotlin Extensions & Builders (Consolidated in v6.0.0)</summary> | ||
|
|
||
| ### Reactive Kotlin Extensions & Builders | ||
|
|
||
| All Kotlin extensions formerly provided by `android-maps-ktx` (`maps-ktx` and `maps-utils-ktx`) are now integrated into `android-maps-utils` (`v6.0.0+`) under canonical packages (`com.google.maps.android.*`, `com.google.maps.android.clustering.*`, etc.). | ||
|
|
||
| #### 1. Coroutine Suspensions (`awaitMapsSdkInitialized()`, `awaitMap()`, `awaitAnimateCamera()`) | ||
| ```kotlin | ||
| import com.google.android.gms.maps.MapsInitializer | ||
| import com.google.maps.android.awaitMapsSdkInitialized | ||
| import com.google.maps.android.awaitMap | ||
| import com.google.maps.android.awaitAnimateCamera | ||
|
|
||
| // Suspend until Maps SDK is initialized | ||
| val renderer: MapsInitializer.Renderer = context.awaitMapsSdkInitialized(MapsInitializer.Renderer.LATEST) | ||
|
|
||
| // Suspend until GoogleMap is ready on MapView / MapFragment | ||
| val googleMap: GoogleMap = mapView.awaitMap() | ||
|
|
||
| // Suspend until camera animation completes | ||
| googleMap.awaitAnimateCamera(CameraUpdateFactory.newLatLngZoom(sydney, 12f)) | ||
| ``` | ||
|
|
||
| #### 2. Option Builders DSL (`addMarker`, `addPolyline`, `addPolygon`) | ||
| ```kotlin | ||
| import com.google.maps.android.addMarker | ||
| import com.google.maps.android.addCircle | ||
|
|
||
| googleMap.addMarker { | ||
| position(LatLng(-33.852, 151.211)) | ||
| title("Sydney Opera House") | ||
| } | ||
|
|
||
| googleMap.addCircle { | ||
| center(LatLng(-33.870, 151.200)) | ||
| radius(500.0) | ||
| strokeWidth(2f) | ||
| } | ||
| ``` | ||
|
|
||
| #### 3. Reactive `Flow` Observers (`mapClickEvents`, `cameraMoveEvents`) | ||
| ```kotlin | ||
| import com.google.maps.android.mapClickEvents | ||
|
|
||
| lifecycleScope.launch { | ||
| repeatOnLifecycle(Lifecycle.State.STARTED) { | ||
| googleMap.mapClickEvents().collect { latLng -> | ||
| Log.d("MapClick", "Clicked: $latLng") | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| #### Backward Compatibility & Deprecation | ||
| Existing references to `com.google.maps.android.ktx.*` continue to work through `@Deprecated(level = DeprecationLevel.WARNING)` forwarding wrappers. You can safely migrate your code incrementally to `com.google.maps.android.*`. | ||
|
|
||
| </details> | ||
|
|
||
| <details> | ||
| <summary>Street View metadata utility</summary> | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,14 +49,31 @@ tasks.register<Exec>("installAndLaunch") { | |
| allprojects { | ||
| group = "com.google.maps.android" | ||
| // {x-release-please-start-version} | ||
| version = "5.2.0" | ||
| version = "6.0.0-rc04" | ||
| // {x-release-please-end} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: stray blank line. Same kind of thing in |
||
|
|
||
|
|
||
| plugins.withId("java") { | ||
| configure<JavaPluginExtension> { | ||
| toolchain { | ||
| languageVersion.set(JavaLanguageVersion.of(17)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| tasks.withType<Test>().configureEach { | ||
| val testHome = rootProject.layout.buildDirectory.dir("test-home").get().asFile | ||
| testHome.mkdirs() | ||
| val m2Link = File(testHome, ".m2") | ||
| if (!m2Link.exists()) { | ||
| val realM2 = File(System.getProperty("user.home"), ".m2") | ||
| if (realM2.exists()) { | ||
| try { | ||
| java.nio.file.Files.createSymbolicLink(m2Link.toPath(), realM2.toPath()) | ||
| } catch (_: Exception) {} | ||
| } | ||
| } | ||
| systemProperty("user.home", testHome.absolutePath) | ||
| environment("ANDROID_HOME", System.getenv("ANDROID_HOME") ?: "/usr/local/google/home/dkhawk/Android/Sdk") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need this "/usr/local/google/home/dkhawk/Android/Sdk" path ? |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| * | ||
| */ | ||
|
|
||
| package com.google.maps.android.clustering | ||
|
|
||
| import com.google.maps.android.clustering.Cluster | ||
| import com.google.maps.android.clustering.ClusterItem | ||
| import com.google.maps.android.clustering.ClusterManager | ||
| import kotlinx.coroutines.channels.awaitClose | ||
| import kotlinx.coroutines.flow.Flow | ||
| import kotlinx.coroutines.flow.callbackFlow | ||
|
|
||
| /** | ||
| * Returns a flow that emits when a cluster is clicked. Using this to observe cluster clicks | ||
| * will override an existing listener (if any) to [ClusterManager.setOnClusterClickListener]. | ||
| * | ||
| * **Warning**: This is a cold flow wrapping a single-listener SDK callback. Concurrently subscribing | ||
| * multiple collectors will result in listener hijacking, and cancelling any observer will unregister | ||
| * the active listener completely. Always share this flow (e.g. using [kotlinx.coroutines.flow.shareIn]) | ||
| * for multi-observer configurations. | ||
| * | ||
| * **Note on event consumption**: The underlying SDK listener returns the result of `trySend().isSuccess`. | ||
| * When an emission is accepted by the flow buffer, the click event is considered consumed (`true`), | ||
| * suppressing default SDK behavior (such as zooming). Under backpressure if the buffer is full, | ||
| * `trySend` returns `false`, allowing default SDK click handling to proceed. | ||
| */ | ||
| public fun <T : ClusterItem> ClusterManager<T>.clusterClickEvents(): Flow<Cluster<T>> = | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These six functions are public and return So the published POM for Can you move it to |
||
| callbackFlow { | ||
| setOnClusterClickListener { | ||
| trySend(it).isSuccess | ||
|
dkhawk marked this conversation as resolved.
|
||
| } | ||
| awaitClose { | ||
| setOnClusterClickListener(null) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns a flow that emits when a cluster item is clicked. Using this to observe cluster item clicks | ||
| * will override an existing listener (if any) to [ClusterManager.setOnClusterItemClickListener]. | ||
| * | ||
| * **Warning**: This is a cold flow wrapping a single-listener SDK callback. Concurrently subscribing | ||
| * multiple collectors will result in listener hijacking, and cancelling any observer will unregister | ||
| * the active listener completely. Always share this flow (e.g. using [kotlinx.coroutines.flow.shareIn]) | ||
| * for multi-observer configurations. | ||
| * | ||
| * **Note on event consumption**: The underlying SDK listener returns the result of `trySend().isSuccess`. | ||
| * When an emission is accepted by the flow buffer, the click event is considered consumed (`true`), | ||
| * suppressing default SDK behavior. Under backpressure if the buffer is full, `trySend` returns `false`, | ||
| * allowing default SDK click handling to proceed. | ||
| */ | ||
| public fun <T : ClusterItem> ClusterManager<T>.clusterItemClickEvents(): Flow<T> = | ||
| callbackFlow { | ||
| setOnClusterItemClickListener { | ||
| trySend(it).isSuccess | ||
| } | ||
| awaitClose { | ||
| setOnClusterItemClickListener(null) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns a flow that emits when a cluster's info window is clicked. Using this to observe cluster info window clicks | ||
| * will override an existing listener (if any) to [ClusterManager.setOnClusterInfoWindowClickListener]. | ||
| * | ||
| * **Warning**: This is a cold flow wrapping a single-listener SDK callback. Concurrently subscribing | ||
| * multiple collectors will result in listener hijacking, and cancelling any observer will unregister | ||
| * the active listener completely. Always share this flow (e.g. using [kotlinx.coroutines.flow.shareIn]) | ||
| * for multi-observer configurations. | ||
| */ | ||
| public fun <T : ClusterItem> ClusterManager<T>.clusterInfoWindowClickEvents(): Flow<Cluster<T>> = | ||
| callbackFlow { | ||
| setOnClusterInfoWindowClickListener { | ||
| trySend(it).isSuccess | ||
| } | ||
| awaitClose { | ||
| setOnClusterInfoWindowClickListener(null) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns a flow that emits when a cluster's info window is long clicked. Using this to observe cluster info window long clicks | ||
| * will override an existing listener (if any) to [ClusterManager.setOnClusterInfoWindowLongClickListener]. | ||
| * | ||
| * **Warning**: This is a cold flow wrapping a single-listener SDK callback. Concurrently subscribing | ||
| * multiple collectors will result in listener hijacking, and cancelling any observer will unregister | ||
| * the active listener completely. Always share this flow (e.g. using [kotlinx.coroutines.flow.shareIn]) | ||
| * for multi-observer configurations. | ||
| */ | ||
| public fun <T : ClusterItem> ClusterManager<T>.clusterInfoWindowLongClickEvents(): Flow<Cluster<T>> = | ||
| callbackFlow { | ||
| setOnClusterInfoWindowLongClickListener { | ||
| trySend(it).isSuccess | ||
| } | ||
| awaitClose { | ||
| setOnClusterInfoWindowLongClickListener(null) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns a flow that emits when a cluster item's info window is clicked. Using this to observe cluster item info window clicks | ||
| * will override an existing listener (if any) to [ClusterManager.setOnClusterItemInfoWindowClickListener]. | ||
| * | ||
| * **Warning**: This is a cold flow wrapping a single-listener SDK callback. Concurrently subscribing | ||
| * multiple collectors will result in listener hijacking, and cancelling any observer will unregister | ||
| * the active listener completely. Always share this flow (e.g. using [kotlinx.coroutines.flow.shareIn]) | ||
| * for multi-observer configurations. | ||
| */ | ||
| public fun <T : ClusterItem> ClusterManager<T>.clusterItemInfoWindowClickEvents(): Flow<T> = | ||
| callbackFlow { | ||
| setOnClusterItemInfoWindowClickListener { | ||
| trySend(it).isSuccess | ||
| } | ||
| awaitClose { | ||
| setOnClusterItemInfoWindowClickListener(null) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns a flow that emits when a cluster item's info window is long clicked. Using this to observe cluster item info window long clicks | ||
| * will override an existing listener (if any) to [ClusterManager.setOnClusterItemInfoWindowLongClickListener]. | ||
| * | ||
| * **Warning**: This is a cold flow wrapping a single-listener SDK callback. Concurrently subscribing | ||
| * multiple collectors will result in listener hijacking, and cancelling any observer will unregister | ||
| * the active listener completely. Always share this flow (e.g. using [kotlinx.coroutines.flow.shareIn]) | ||
| * for multi-observer configurations. | ||
| */ | ||
| public fun <T : ClusterItem> ClusterManager<T>.clusterItemInfoWindowLongClickEvents(): Flow<T> = | ||
| callbackFlow { | ||
| setOnClusterItemInfoWindowLongClickListener { | ||
| trySend(it).isSuccess | ||
| } | ||
| awaitClose { | ||
| setOnClusterItemInfoWindowLongClickListener(null) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AGENTS.md says not to hand edit this or CHANGELOG.md. The title is already
feat!:so release-please lands on 6.0.0 by itself. Can we revert both and let it do its job?Also worth checking: the description says final 6.0.0, but
release-please-config.jsonis untouched and still has"prerelease": truewith"prerelease-type": "rc". As it stands the next run gives us 6.0.0-rc05, not 6.0.0.