Extract Android host into federated packages and add JNI command transport - #612
Draft
liodali wants to merge 60 commits into
Draft
Extract Android host into federated packages and add JNI command transport#612liodali wants to merge 60 commits into
liodali wants to merge 60 commits into
Conversation
Introduce MarkerId, AndroidMapException and sealed AndroidMapEvent hierarchy plus AndroidMapPlatform/AndroidMapBackend contract. Barrier file re-exports the new domain so web/iOS imports stay free of JNI types.
Validate MarkerId equality, AndroidMapException context and typed event payload retention without importing any platform transport.
Move dispose onto IBaseOSMController, make BaseMapController timer nullable and keep legacy initialization on the legacy path. Typed Android controllers own their lifecycle via AndroidMapPlatform instead.
Add AndroidMapTransport contract and factory plus JNI/MethodChannel transports. Phase 2 keeps JNI in capability-probe mode so auto can still select MethodChannel before any stateful command is replayed.
Introduce opt-in controller covering attach, activeBackend/ready/events, single-attach guard, view-filtered event propagation, disposal during attach, and auto fallback restricted to JNI attach before initialization. Initialization errors are terminal and never replayed via fallback.
…arrier Re-export Android typed domain and transports from lib/android.dart and align MapController/OSM disposal to the IBaseOSMController contract so the typed path does not depend on a concrete controller cast.
Handle AndroidMapPlatform in MobileOsmFlutter, forward typed events to controller/observer callbacks, guard non-Android attach, and make disposal idempotent for the transport event subscription.
…harness Cover attach/ready/event filtering, disposal/fallback semantics and non-Android guard. Add integration harness asserting MethodChannel active backend and readiness delivery over OSMFlutter.
…rations Surface initialize, setZoom, moveTo, and setRotation as Boolean returns so the JNI bridge can detect command rejection. Add emitAcknowledgement, emitError, emitMarkerTap, and emitReady helpers for the typed event contract. Add getZoomSnapshot so JNI queries from off the main thread stay thread-safe. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…apter Add a TypedMapCommand enum so the typed Android controller can dispatch android#camera#rotation and android#marker#add/remove without colliding with the legacy method set. Forward unknown methods to notImplemented once both legacy and typed lookups miss. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…MapSession Implement initialize, setZoom, moveTo, setRotation, and addMarker through the Mapscore camera. Emit acknowledgements and errors through the existing per-view event sink using the android#event envelope. Track a volatile zoomSnapshot so JNI queries stay thread-safe and stable marker identity is preserved through receiveGeoPoint taps. Wire the typed dispatch path into handleLegacyChannelCommands. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Add a companion resolve(viewId) that returns a MapSession only when a single Flutter engine exposes it, failing closed when two engines happen to share an overlapping view ID. Expose and hide registries from the factory lifecycle so disposing a factory withdraws its sessions from JNI lookup. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Queue every JNI mutation on Android's main thread, then acknowledge it through the per-view event sink once Mapscore accepts or rejects. attach selects a session before any state-changing command; close withdraws it. Each command resolves the session lazily to defend against disposal between queueing and execution. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…contract Verify resolve returns the only exposed session, fails closed when two engines expose the same view ID, and clears when hide is called. Update the FakeMapSession to honour the expanded interface, including the new emit helpers and Boolean return values. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Add jnigen bindings for attach, initialize, moveTo, setZoom, getZoom, setRotation, addMarker, removeMarker, and close on OsmAndroidBridge. These mirror the Kotlin façade so the Dart transport can call straight through the JVM with one native hop. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…ker commands Add moveTo, setZoom, getZoom, setRotation, addMarker, and removeMarker to the typed transport. Document that stateful commands are never replayed through another transport after initialization begins, so attach-only fallback stays safe. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…pTransport Implement moveTo, setZoom, getZoom, setRotation, addMarker, and removeMarker against the existing per-view channel with the matching legacy method names. Centralize error handling through _invokeVoid and _invokeValue so PlatformException and MissingPluginException map to the typed AndroidMapException. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…owledgements Replace the Phase 2 capability gate with a real command transport that queues every mutation through jnigen and waits for the matching android#event acknowledgement before completing the Dart future. Pull shared event decoding into android_event_decoder.dart so the JNI and MethodChannel transports stay in sync on the envelope. Failed acknowledgements and timeouts surface as typed AndroidMapException and are never replayed through MethodChannel. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…ontroller Expose AndroidMapCamera (moveTo, setZoom, getZoom, setRotation) and AndroidMapMarkers (add, remove) on the controller. Each method awaits ready before dispatching and routes through whichever transport won attach, so the same Dart API works on both the JNI and MethodChannel backends. Issue stable MarkerIds per controller so removeMarker can target the right native pin. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Verify the typed command slice goes through the attached transport, records the issued marker IDs, and rejects duplicate IDs before any command reaches the backend. Add an event decoder test that decodes the ack and error envelopes and rejects malformed payloads. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Replace the Phase 2 attach harness with the JNI-ready slice that runs camera move/zoom/rotation and a stable-ID marker add/remove through the JNI backend. Add a second view isolation test that proves commands stay routed to the right platform view even when both share a marker ID. Keep a methodChannel fallback test so the typed slice stays available when JNI is unavailable. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Bump objectVersion to 54, switch the thin script to embed_and_thin, drop the stale App.framework and Flutter.framework references, and raise the iOS deployment target to 15.0. Update the shared scheme to match the new project upgrade version. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…ypes Introduce value-typed overlay identifiers alongside MarkerId so the typed Android controller can issue stable IDs for road geometry, polygon shapes, and static-position groups. Reject empty values through an assertion since the JNI side uses them as native keys. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…e commands Extend the session contract with bulk add/update/remove for markers, circle and rectangle shape overlays, static-position groups, road geometry with options, raster/vector tile sources, and overlay-layer visibility. Each new method returns Boolean so the bridge can detect rejection from Mapscore without ambiguity. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…dapter Add the Phase 4 method names to TypedMapCommand so the per-view MethodChannel adapter can dispatch bulk marker updates, shape overlays, static-position groups, road geometry, tile sources, and overlay visibility alongside the existing camera and marker commands. Forward unknown methods to notImplemented once both lookups miss. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…isibility Implement bulk addMarkers/updateMarkerIcon/removeMarkers, circle and rectangle shape overlays through the polygon layer, static-position groups through the static icon layer, road geometry through the line layer, raster/vector tile configuration, and overlay-layer visibility across every overlay layer. Return Boolean acceptance so the JNI bridge can detect rejection. Validate geometry, lat/lon, and zoom ranges before mutating Mapscore state. Clear all overlay state on dispose so a recycled session cannot leak markers, shapes, or roads. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Add bulk addMarkers/updateMarkerIcon/removeMarkers, circle and rectangle shape overlays, static-position groups, road geometry with options, raster/vector tile sources, overlay-layer visibility, and a resetTile entry point through the same main-thread dispatch path as Phase 3. Empty API key/value strings are normalised to null before reaching Mapscore so unset credentials do not corrupt the layer cache. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Verify bulk marker, shape, road, and overlay-visibility calls route through MapSessionRegistry to one session only. Confirm the Phase 4 typed method names are accepted by TypedMapCommand and stay separate from LegacyMapCommand lookups. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Regenerate jnigen bindings covering bulk addMarkers, updateMarkerIcon, removeMarkers, circle and rectangle shape overlays, static-position groups, road geometry, raster/vector tile sources, overlay visibility, and resetTile. The addMarker signature now accepts an optional icon ByteArray; native string arrays pass through JArray<JString>. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
… and tiles Add bulk marker commands (addAll/updateIcon/removeAll), circle and rectangle shape overlays, static-position groups, road geometry with options, raster/vector tile configuration, and overlay-layer visibility to the typed transport contract. Document that the icon argument is encoded image bytes such as PNG. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…MapTransport Implement bulk marker, shape, static-position, road, tile, and overlay visibility commands over the existing per-view channel. Encode icon bytes inline, encode road option colors as signed ARGB, and use android#tile#set for both raster and vector sources. Reuse the existing _invokeVoid helper so every new path benefits from the typed AndroidMapException mapping. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Implement bulk addMarkers, updateMarkerIcon, removeMarkers, circle and rectangle shapes, static-position groups, road geometry with options, raster/vector tile sources, overlay visibility, and resetTile through the JNI bridge. Encode icon bytes through JByteArray, string collections through JArray<JString>, and coordinates through JDoubleArray. Release every native reference after the call so JNI references do not leak when commands reject. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…oidMapController Expose AndroidMapShapes (circle/rectangle add/remove/clear), AndroidMapStaticPositions (set/remove), AndroidMapRoads (draw/remove/clear), and AndroidMapLayers (setTile/setOverlaysVisible) on the controller. Each method awaits ready, routes through whichever transport won attach, issues stable IDs per controller, and validates geometry so failures surface as AndroidMapException before any native call. Re-export the new overlay types through android.dart. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Verify that bulk markers, shapes, static positions, roads, tile configuration, and overlay visibility route through the attached transport on either backend. Reject duplicate shape IDs, invalid geometry, and empty position lists before any command reaches the backend. Add a MethodChannelAndroidMapTransport test that asserts the serialised envelopes for marker, shape, road, and tile commands. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Drive the JNI backend through bulk addAll/updateIcon, shape overlays, static-position groups with icon bytes, road geometry with options, overlay-layer visibility toggles, and raster tile configuration. Verify every Phase 4 operation reaches Mapscore by collecting the acknowledgement event names. Add bulk marker and overlay commands to the fallback test so the typed slice stays available on MethodChannel. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Drive bulk markers, shape overlays, static positions, road geometry, and overlay visibility through both JNI and MethodChannel backends in the same row so the timings compare equal payloads under one tester. Add the missing integration_test_driver so `flutter drive` can pick up the Phase 4 benchmark alongside the controller attach suite. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Generated by a tool misparsing a CLI flag as a path. Not part of the plugin source. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Coalesces region events to 100ms and location events to 1s, emitting the first event immediately and trailing events with the newest payload. Close cancels all pending tasks. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…updates Separates caller intent (isRequested) from Activity visibility (isResumed). Updates stop on pause and resume only when explicitly requested. A failed start clears requested state. Close rejects further starts. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds SHOW_CURRENT_LOCATION, GET_CURRENT_LOCATION, START/STOP_LOCATION_UPDATES, and START/STOP_LOCATION_TRACKING to TypedMapCommand so both JNI and MethodChannel backends share the same typed command set. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…tion Replaces the coroutine-based currentUserPosition with a main-thread pending-result model. Adds a 15s timeout, duplicate-request rejection, and cancellation on pause, stop, destroy, and activity detach. Location subscriptions now use ResumableLocationSubscription so updates pause and resume with caller intent. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Routes region/location events through MapEventCoalescer. Replaces the leaky resultFlutter/skipCheckLocation flags with typed pending-result fields. Extracts per-view activity request codes into LocationActivityRequestCodes so simultaneous maps cannot consume each others settings results. Cancels pending requests on activity detach, pause, and disposal. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Typed event for foreground user-location updates delivered through the MethodChannel event plane. Retains the legacy receiveUserLocation callback alongside the typed path. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Wraps the decoder in try/catch so malformed native events are dropped instead of failing the MethodChannel handler. Adds explicit type checks for envelope fields, marker taps, and user-location payloads. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds showCurrentLocation, getCurrentLocation, start/stopLocationUpdates, and start/stopLocationTracking to the transport interface. Both JNI and MethodChannel implementations route these through the MethodChannel command plane. Pending invocations are tracked and cancelled with transport_closed on close. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Exposes showCurrentLocation, getCurrentLocation, start/stopUpdates, and start/stopTracking on AndroidMapController. Foreground permission is requested via an injectable AndroidLocationPermissionRequester before acquisition commands; stop commands skip the permission request. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds a JNI integration test exercising the full location command set and user-location event delivery. Adds a lifecycle harness that verifies location subscriptions pause and resume with the host Activity. Expands the MethodChannel fallback path to cover all Phase 5 location commands. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Decouple the JNI runtime from the core plugin so it ships as an optional federated add-on rather than a hard dependency. The AndroidMapTransport contract and event codec move into flutter_osm_interface, the main plugin now bundles only the MethodChannel transport, and the JNI bindings, probe, transport, and generator live in the new flutter_osm_android_jni package. Drop jni/jnigen from the root pubspec and update docs, tests, and the example accordingly. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Extract the Android host implementation (native Kotlin + MethodChannel transport) into a new endorsed flutter_osm_android package that implements flutter_osm_interface. This is the first step of the federated plugin extraction: the new package owns the platform view, map sessions, JNI bridge, and MethodChannel transport, while the root plugin will endorse it as the default Android implementation. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Centralize the five-package registry and dependency graph in release_packages.py and refactor update_versions.py, check_pubspec_release.py, and pre_release.py onto the shared helpers. Add dependency-aware modes (interface/web/android/jni/osm/all), robust pubspec parsing, cycle detection, pub.dev checks with timeouts, and sequential pre-release tag pushes. Add fixture tests for all four scripts. Update flutter_osm_android_jni to depend on the new flutter_osm_android host package via path dependency. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Wire the root plugin to endorse flutter_osm_android as the default Android implementation, update android_map_controller to import the transport from the new package, and refresh AGENTS.md to document the five-package federated structure. Ignore the old_android rollback backup directory. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The Android Gradle setup, Kotlin sources, assets, resources, tests, and the bundled MethodChannel transport are removed so the main plugin is transport-agnostic. The default Android transport factory now defers to the optional flutter_osm_android_jni package for every Android backend.
Add architecture diagram, when-to-use guidance, exported API table, fallback semantics, binding regeneration notes, platform support matrix, and manual publish instructions. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The publish job already removes the web and interface plugin directories before formatting and publishing. The new flutter_osm_android and flutter_osm_android_jni packages (added for JNI integration) must be cleaned up as well so they aren't included in the published artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Android implementation was bundled directly inside the main plugin, which made it hard to evolve independently and left no clean boundary for alternative command transports like JNI.
Solution
Split Android into a proper federated plugin setup and add an optional JNI command plane.
flutter_osm_plugininto a new endorsedflutter_osm_androidpackage (platform view, map sessions, MethodChannel transport).flutter_osm_android_jnipackage that routes typed map commands through generated JNI bindings into the Kotlin bridge, while MethodChannel keeps ownership of events, acknowledgements, location, permissions, and lifecycle.flutter_osm_interface.AndroidMapControllerwith an attach-only backend selection (methodChannel,jni, orautofallback) — commands are never replayed across transports after initialization.Testing