Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ jobs:
run: echo "MAPS_API_KEY=dummy" > secrets.properties

- name: Build modules
run: ./gradlew build koverXmlReportDebug --stacktrace
run: ./gradlew build apiCheck koverXmlReportDebug --stacktrace
10 changes: 10 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,13 @@ geoJsonLayer.setOnFeatureClickListener { feature ->
}
```
Unlike the previous version, this SAM conversion is fully supported for native Kotlin callers without requiring anonymous object syntax (`object : Layer.OnFeatureClickListener { ... }`).

---

## 4. Data Renderer Model Style Updates (v6.0.0)

In `v6.0.0`, the `com.google.maps.android.data.renderer.model.Style` hierarchy was expanded to support complete feature styling and heterogeneous multi-geometries:

- **`PointStyle`**, **`LineStyle`**, and **`PolygonStyle`**: Added default-valued constructor properties (`title`, `snippet`, `draggable`, `flat`, `visible`, `infoWindowAnchorU`, and `infoWindowAnchorV` on `PointStyle`; `clickable` and `visible` on `LineStyle` and `PolygonStyle`). Source calls with named or positional arguments continue to compile unchanged, though recompilation is required against the updated data class `copy()` and constructor signatures.
- **`CompositeStyle`**: Added `CompositeStyle(pointStyle, lineStyle, polygonStyle)` as a new subtype of the `sealed interface Style` to support heterogeneous `MultiGeometry` features. Exhaustive `when (style)` expressions over `Style` in consumer code should add a `is CompositeStyle ->` branch (or an `else ->` branch).

67 changes: 63 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand All @@ -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

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 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 85cb2fbf — reverted README.md to match origin/main so release-please updates all version markers together.

}
Expand Down Expand Up @@ -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>

Expand Down
1 change: 1 addition & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation(libs.dokka.gradle.plugin)
implementation(libs.kover.gradle.plugin)
implementation(libs.gradle.maven.publish.plugin)
implementation(libs.binary.compatibility.validator)
}

gradlePlugin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
import com.vanniktech.maven.publish.AndroidSingleVariantLibrary
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension
import kotlinx.validation.KotlinApiBuildTask
import kotlinx.validation.KotlinApiCompareTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.Copy
import org.gradle.kotlin.dsl.*

class PublishingConventionPlugin : Plugin<Project> {
Expand All @@ -28,6 +31,7 @@ class PublishingConventionPlugin : Plugin<Project> {
applyPlugins()
configureKover()
configureVanniktechPublishing()
configureBinaryCompatibilityValidator()
}
}

Expand Down Expand Up @@ -106,4 +110,65 @@ class PublishingConventionPlugin : Plugin<Project> {
}
}
}

private fun Project.configureBinaryCompatibilityValidator() {
val ignoredProjects = setOf("demo", "visual-testing", "lint-checks", "maps-utils")
if (name in ignoredProjects) return

val projectName = name
val apiFile = layout.projectDirectory.file("api/$projectName.api")
val buildApiFile = layout.buildDirectory.file("api/$projectName.api")

afterEvaluate {
val bundleTask = tasks.findByName("bundleLibCompileToJarRelease") ?: return@afterEvaluate
val classesJar = layout.buildDirectory.file(
"intermediates/compile_library_classes_jar/release/bundleLibCompileToJarRelease/classes.jar"
)

val apiBuild = tasks.register<KotlinApiBuildTask>("apiBuild") {
group = "verification"
description = "Builds public API declaration for $projectName."
inputJar.set(classesJar)
outputApiFile.set(buildApiFile)
ignoredClasses.addAll(
"com.google.maps.android.R",
"com.google.maps.android.clustering.R",
"com.google.maps.android.data.R",
"com.google.maps.android.heatmaps.R",
"com.google.maps.android.ui.R",
"com.google.maps.android.BuildConfig",
"com.google.maps.android.clustering.BuildConfig",
"com.google.maps.android.data.BuildConfig",
"com.google.maps.android.heatmaps.BuildConfig",
"com.google.maps.android.ui.BuildConfig",
)
dependsOn(bundleTask)
}

val apiDump = tasks.register<Copy>("apiDump") {
group = "verification"
description = "Syncs public API declarations of $projectName to the project api/ directory."
from(apiBuild.flatMap { it.outputApiFile })
into(apiFile.asFile.parentFile)
dependsOn(apiBuild)
}

val apiCheck = tasks.register<KotlinApiCompareTask>("apiCheck") {
group = "verification"
description = "Checks public API declarations of $projectName against the committed api/$projectName.api."
projectApiFile.set(apiFile)
generatedApiFile.set(apiBuild.flatMap { it.outputApiFile })
dependsOn(apiBuild)
}

tasks.findByName("check")?.dependsOn(apiCheck)

rootProject.tasks.matching { it.name == "apiDump" }.configureEach {
dependsOn(apiDump)
}
rootProject.tasks.matching { it.name == "apiCheck" }.configureEach {
dependsOn(apiCheck)
}
}
}
}
41 changes: 41 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,26 @@
* limitations under the License.
*/

import java.nio.file.Files
import java.util.Properties

plugins {
id("com.vanniktech.maven.publish") version libs.versions.gradleMavenPublishPlugin.get() apply false
alias(libs.plugins.binary.compatibility.validator)
}

apiValidation {
ignoredProjects += listOf("demo", "visual-testing", "lint-checks", "maps-utils")
}

val apiDump = tasks.register("apiDump") {
group = "verification"
description = "Dumps public API declarations for all library modules."
}

val apiCheck = tasks.register("apiCheck") {
group = "verification"
description = "Checks that public API declarations match the committed .api files."
}

buildscript {
Expand Down Expand Up @@ -59,4 +77,27 @@ allprojects {
}
}
}

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 {
Files.createSymbolicLink(m2Link.toPath(), realM2.toPath())
} catch (_: Exception) {}
}
}
systemProperty("user.home", testHome.absolutePath)
val androidSdkDir = System.getenv("ANDROID_HOME")
?: System.getenv("ANDROID_SDK_ROOT")
?: rootProject.file("local.properties").takeIf { it.isFile }?.let { localPropsFile ->
Properties().apply { localPropsFile.inputStream().use(::load) }.getProperty("sdk.dir")
}
if (androidSdkDir != null) {
environment("ANDROID_HOME", androidSdkDir)
}
}
}
Loading
Loading