Skip to content

feat!: rewrite android maps utils - #1658

Merged
dkhawk merged 88 commits into
mainfrom
feat/rewrite-android-maps-utils
Jul 15, 2026
Merged

dkhawk merged 88 commits into
mainfrom
feat/rewrite-android-maps-utils

Conversation

@kikoso

@kikoso kikoso commented Feb 24, 2026 •

Copy link
Copy Markdown
Collaborator

This Pull Request introduces a comprehensive rewrite and modernization of the Android Maps Utils library. The primary goals are to migrate the codebase to Kotlin, improve modularity, and introduce modern Android development patterns (such as Jetpack Compose and enhanced testing capabilities).

Key Changes

1. Modularization

The library has been decomposed into several focused modules to improve maintainability and allow developers to include only the components they need:

  • clustering: Dedicated module for marker clustering logic and renderers.
  • data: Core data layer handling.
  • heatmaps: Heatmap tile providers and utilities.
  • ui: Shared UI components and icon generators.
  • onion: A new modern rendering engine with support for KML, GeoJSON, and GPX.
  • visual-testing: A new module for automated visual regression testing.

2. Kotlin Migration & Modernization

  • Major portions of the library (including clustering algorithms and UI utilities) have been migrated from Java to Kotlin.
    • Updated the project to Java 17 toolchain and Kotlin 2.2.21.
    • Standardized build logic using convention plugins and KTS files.
    • Updated target and compile SDK to API 36.

3. New Features & Improvements

  • Onion Renderer: A new, more robust rendering engine for KML and GeoJSON data.
  • Jetpack Compose Demo: The demo application's main entry point has been rewritten in Jetpack Compose, featuring a modern accordion-style navigation.
  • Transit Layer Demo: Added a new demonstration for transit layers.
  • Advanced Markers: Improved support for Google Maps Advanced Markers within the clustering module.
  • Visual Testing: Introduced a GeminiVisualTestHelper to facilitate visual validation of map rendering.

4. Build & CI/CD

  • Migrated from secrets.gradle.plugin to a more robust secrets.properties handling in the demo app to prevent build failures during CI/IDE sync.
  • Updated GitHub Actions to use gradle/actions/setup-gradle@v4.
  • Consolidated dependency management using Gradle Version Catalogs (libs.versions.toml).

Testing Performed

  • Unit tests migrated and validated for all core modules (clustering, heatmaps, ui, onion).
  • Manual validation using the updated Demo App (Compose UI).
  • Verified KML/GeoJSON rendering with new test datasets (mountain ranges, peaks, earthquakes).

Breaking Changes

  • Package structures have changed due to modularization.
  • Some classes migrated to Kotlin may have slight API differences in nullability or property access when called from Java.

kikoso and others added 30 commits October 30, 2025 15:46
Previously, the  task did not correctly trigger the  check, allowing the demo app to attempt building without the necessary API key. This commit refines the logic to:
- Enforce  presence for all tasks that build the  module (e.g., , , , , ).
- Allow non-build tasks (e.g., , ) to proceed without the  file, printing a warning instead of throwing an error.

This ensures that tests can run independently while preventing incomplete demo app builds.
The secrets check in `demo/build.gradle.kts` has been refined to address issues with Android Studio Gradle syncs and to more accurately enforce the presence of `secrets.properties` for build-related tasks.

Previously, the script would fail during an Android Studio sync (when no specific tasks are requested) due to an overly broad interpretation of "build task." This commit introduces the following changes:

- **Graceful handling of IDE syncs:** If no tasks are explicitly requested (common during IDE sync), a warning is now issued instead of a hard failure. This allows the IDE to sync successfully while still informing the developer about the missing secrets.
- **Stricter enforcement for build tasks:** For command-line builds or tasks that are not explicitly exempt (e.g., `clean`, `test`, `lint`), the script will now correctly throw an error if `secrets.properties` is missing.

This ensures a better developer experience by preventing unnecessary build failures during IDE operations, while maintaining the necessary security check for actual application builds.
The previous logic prevented any  task from running without a  file. This change allows the  task to run if it's accompanied by a test-related task, such as . This enables running test reports without needing to provide API keys.
…tasks (e.g., `:demo:assembleDebug`) when validating secrets.
Improved and expanded assertions in `GeoJsonParserTest` for:
- `earthquakes_with_usa.json`
- `south_london_line_geojson.json`
- `south_london_square_geojson.json`
- `usa.json`
- `earthquakes.json`

Tests now include detailed verification of feature counts, properties, geometry types, and coordinate values to ensure robust GeoJSON parsing.
adds Truth Subjects for Points and LatLngAlt.kt
Establish the pattern to migrate off GeoData object.

Additionally, a minor typo in `testAmuBallonGxPrefix` has been corrected.
- Add `LatLonBoxSubject` to support cleaner, tolerance-based assertions for KML bounds using Google Truth.
- Update `amu_empty_hotspot.kml` to wrap contents in a `<Document>` tag, ensuring valid KML structure for parsing.
- Refactor `KmlParserTest` to use new assertions, remove dead code/comments, and fix a resource filename typo ("ballon" -> "balloon").
- Inline variable in `KmlParser.parseAsKml` for brevity.
Adds support for rotation in LatLonBox
Update the geojson tests to use the geojson specific model.
Refactor GeoJSON parsing and mapping for type-safe coordinates.

This commit refactors GeoJSON parsing to use a new Coordinates data class, simplifying GeoJsonObjects and GeoJsonParser. It also introduces a platform-agnostic renderer model and an initial MapViewRenderer implementation. Test updates are included to reflect these changes and ensure correctness, improving overall code robustness and maintainability.
This commit introduces the `KmlMapper` to transform KML data models into the platform-agnostic renderer model (`Scene` and `Feature` objects).

Key changes include:
- **`KmlMapper.kt`**: A new mapper that converts existing KML parser model objects into the generic renderer model, handling geometry transformations and nullability.
- **`KmlMapperTest.kt`**: Comprehensive unit tests to verify the correct functioning of the `KmlMapper`, ensuring accurate conversion of KML Points, LineStrings, Polygons, and MultiGeometries, as well as handling of nested structures and null-safe coordinate access.

This adds support for KML data within the new `onion` rendering architecture, leveraging existing KML parsing capabilities.
kikoso added 4 commits May 28, 2026 21:25
…apply false

- Added 'apply false' declaration for vanniktech maven-publish plugin at root level
- This fixes the classloader conflict when multiple modules share the build service
- Publishing now works: publishToMavenLocal succeeds, publishToMavenCentral reaches upload stage
- Version remains at 4.4.1
@kikoso
kikoso force-pushed the feat/rewrite-android-maps-utils branch from db17494 to e455f27 Compare June 15, 2026 19:57
kikoso added 3 commits June 15, 2026 22:10
This resolves the Gradle task dependency issue where dokkaGeneratePublicationHtml
was using the output of generateArtifactIdFile without an explicit dependency.
This was causing the gh-pages workflow to fail.
Apply the security fix from PR #1703 to amu_info_window.xml and amu_text_bubble.xml.
This prevents touch event spoofing when views are obscured by other windows.
@kikoso
kikoso force-pushed the feat/rewrite-android-maps-utils branch 2 times, most recently from 757a3be to 545cdf4 Compare June 23, 2026 19:13
dkhawk and others added 5 commits June 24, 2026 16:40
…maps-utils

# Conflicts:
#	library/src/main/java/com/google/maps/android/data/geojson/GeoJsonParser.java
#	library/src/main/java/com/google/maps/android/data/kml/KmlContainerParser.java
#	library/src/main/java/com/google/maps/android/data/kml/KmlFeatureParser.java
#	library/src/test/java/com/google/maps/android/data/geojson/GeoJsonParserTest.java
#	library/src/test/java/com/google/maps/android/data/kml/KmlFeatureParserTest.java
#	library/src/test/java/com/google/maps/android/data/kml/KmlTestUtil.java
Port the depth-limit protection from #1710 to the Kotlin parser. The
generated serializers recurse once per nested element, so a depth guard
at the XmlReader level aborts parsing of maliciously deep documents
(e.g. thousands of nested Folder or MultiGeometry elements) before the
stack can overflow.
@kikoso
kikoso force-pushed the feat/rewrite-android-maps-utils branch from 2550407 to 62c3586 Compare July 15, 2026 15:46
@dkhawk
dkhawk merged commit eeb0156 into main Jul 15, 2026
20 checks passed
@dkhawk
dkhawk deleted the feat/rewrite-android-maps-utils branch July 15, 2026 16:38
kikoso added a commit that referenced this pull request Aug 5, 2026
Coverage reporting on `library` was already producing an all-zero
report on main before this branch touched anything (reproduced on a
clean main worktree): the report only contained the untested Java
`collections` classes, and every tested Kotlin class (PolyUtil,
MathUtil, SphericalUtil, StreetViewUtils, AttributionIdInitializer)
was silently absent.

Root cause: com.mxalbert.gradle.jacoco-android (last released for
AGP 8.x) auto-detects classDirectories from paths that predate AGP's
built-in Kotlin compiler. AGP 9 now compiles Kotlin into
intermediates/built_in_kotlinc/, which the plugin doesn't know about,
so it silently falls back to javac output only. Overriding
classDirectories on the debug JacocoReport tasks to include both
javac and built_in_kotlinc output fixes this for every module using
the shared PublishingConventionPlugin (verified library, clustering,
data, heatmaps, ui all now report real, non-zero coverage).

Also fixes report.yml, which only ever read
library/build/jacoco/jacoco.xml. Before the module rewrite (#1658)
`library` contained the entire codebase, so that was the whole
project's coverage; after the split into clustering/data/heatmaps/ui,
report.yml was never updated, so "Overall Project" reflected ~2% of
the codebase. Added the other four modules' jacoco.xml to the report
paths. Combined coverage is ~37.5%, comfortably above the existing
26% floor, so no threshold changes needed.
dkhawk pushed a commit that referenced this pull request Aug 5, 2026
…1740)

* build(deps): update dependency versions and Gradle wrapper

Bump Kotlin, Ktor, Material, kotlinx-serialization, Navigation SDK,
UiAutomator, Lint, AGP, and the Maven Publish plugin to their latest
stable releases. AGP 9.3.1 requires Gradle >= 9.5.0, so the wrapper is
regenerated (via `gradlew wrapper`) to 9.6.1.

xmlutil serialization is left pinned at 0.91.3: 1.0.0 restructured the
XML {} config DSL and breaks GpxParser/KmlParser, which needs a
separate migration.

* build(deps): bump GitHub Actions to latest major versions

Bump actions/checkout to v7 (and align release-rc.yml, which was
still on v5), actions/setup-go to v7, actions/setup-python to v7,
peter-evans/create-pull-request to v8, and madrapps/jacoco-report to
v1.8.0.

* fix(ci): resolve zizmor security findings and lint JDK crash

zizmor (required check) was failing on two mandatory findings:
- unpinned-uses: pin every third-party action in the touched
  workflows to a full commit SHA (with the resolved version as a
  trailing comment) instead of a mutable tag.
- template-injection: release-rc.yml interpolated the workflow_dispatch
  `branch` input directly into a `run:` shell script
  (`git push origin "${{ inputs.branch }}"`), which is a shell
  injection vector. Routed it through a BRANCH env var instead,
  matching how VERSION is already handled in the same job.

Separately, `lintDebug`/`lintStandardDebug` were failing with
`NoSuchMethodError: java.util.List.removeLast()` inside
BidirectionalTextDetector. Reproduced locally: `lint` 32.3.1 (pulled
in by the AGP 9.3.1 bump) now needs JDK 21 to run, but test.yml,
report.yml, docs.yml, and lint-report.yml were still provisioning
JDK 17. Bumped those to JDK 21 (publish.yml already used 21). This is
purely the JDK Gradle itself runs on; the project's own Java
toolchain target is unaffected.

* fix(ci): scope down default GITHUB_TOKEN permissions

zizmor scores its excessive-permissions finding as Medium severity,
which independently fails the required check regardless of the
mandatory-checks list. docs.yml, lint-report.yml, and publish.yml had
no permissions: block, so they inherited the org/repo default (broad)
token scope even though none of their steps need it:
- docs.yml and publish.yml only read the repo via checkout and use a
  separate PAT (SYNCED_GITHUB_TOKEN_REPO) for anything that writes,
  so contents: read is sufficient.
- lint-report.yml additionally uploads SARIF via
  github/codeql-action/upload-sarif, which needs security-events:
  write.

* fix(build): restore real JaCoCo coverage for Kotlin sources

Coverage reporting on `library` was already producing an all-zero
report on main before this branch touched anything (reproduced on a
clean main worktree): the report only contained the untested Java
`collections` classes, and every tested Kotlin class (PolyUtil,
MathUtil, SphericalUtil, StreetViewUtils, AttributionIdInitializer)
was silently absent.

Root cause: com.mxalbert.gradle.jacoco-android (last released for
AGP 8.x) auto-detects classDirectories from paths that predate AGP's
built-in Kotlin compiler. AGP 9 now compiles Kotlin into
intermediates/built_in_kotlinc/, which the plugin doesn't know about,
so it silently falls back to javac output only. Overriding
classDirectories on the debug JacocoReport tasks to include both
javac and built_in_kotlinc output fixes this for every module using
the shared PublishingConventionPlugin (verified library, clustering,
data, heatmaps, ui all now report real, non-zero coverage).

Also fixes report.yml, which only ever read
library/build/jacoco/jacoco.xml. Before the module rewrite (#1658)
`library` contained the entire codebase, so that was the whole
project's coverage; after the split into clustering/data/heatmaps/ui,
report.yml was never updated, so "Overall Project" reflected ~2% of
the codebase. Added the other four modules' jacoco.xml to the report
paths. Combined coverage is ~37.5%, comfortably above the existing
26% floor, so no threshold changes needed.
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