Skip to content

fix(map): initialize Maps SDK before building marker bitmap descriptors#5709

Merged
jamesarich merged 1 commit into
mainfrom
claude/inline-map-bitmapdescriptor-init
Jun 1, 2026
Merged

fix(map): initialize Maps SDK before building marker bitmap descriptors#5709
jamesarich merged 1 commit into
mainfrom
claude/inline-map-bitmapdescriptor-init

Conversation

@jamesarich

Copy link
Copy Markdown
Collaborator

Problem

New FATAL on the node-detail screen (released alongside #5702/#5704/#5708):

java.lang.NullPointerException: IBitmapDescriptorFactory is not initialized
  at com.google.android.gms.maps.model.BitmapDescriptorFactory.fromBitmap(...)
  at org.meshtastic.app.map.component.MarkerBitmapRendererKt.renderNodeChipBitmap(MarkerBitmapRenderer.kt:95)
  at org.meshtastic.app.map.component.MarkerBitmapRendererKt.rememberNodeChipDescriptor(MarkerBitmapRenderer.kt:50)
  at org.meshtastic.app.node.component.InlineMapKt.InlineMap(InlineMap.kt:52)

BitmapDescriptorFactory only works after the Maps SDK has been initialized, which normally happens when a GoogleMap/MapView is created. The Canvas marker descriptors introduced in #5702 are built during composition — and on the node-detail inline map the marker icon is computed (InlineMap.kt:52, outside/before the GoogleMap {}) before that screen's GoogleMap has loaded the SDK, so BitmapDescriptorFactory.fromBitmap() throws.

This is why it only bites the inline map: on the full map screen a GoogleMap is already present, so the factory is initialized by the time markers render.

Fix

Call MapsInitializer.initialize(context) before creating any descriptor, centralized in rememberNodeChipDescriptor / rememberEmojiMarkerDescriptor. It is synchronous and idempotent, so it's a no-op once the SDK is already up. Centralizing in the renderer covers every call site (inline map, waypoint markers, full map) and any future ones.

Notes

  • MapsInitializer.initialize(Context) is deprecated in favor of the async renderer-callback overload, but we need the synchronous guarantee here; suppressed locally with a comment.
  • Compile-verified (spotlessCheck, detekt, :androidApp:compileGoogleDebugKotlin all pass). Not runtime-verified here (needs a Maps key + real google-services.json) — suggest a smoke test: open a node's detail screen (Position section) directly after a cold start, before visiting the full map.

🤖 Generated with Claude Code

BitmapDescriptorFactory only works after the Maps SDK has been
initialized, which normally happens when a GoogleMap/MapView is created.
The Canvas marker descriptors added in #5702 are built during
composition; on the node-detail inline map the marker icon is computed
(InlineMap.kt) before that screen's GoogleMap has loaded the SDK, so
BitmapDescriptorFactory.fromBitmap crashes with a fatal
NullPointerException "IBitmapDescriptorFactory is not initialized".

Call MapsInitializer.initialize(context) before creating any descriptor
in rememberNodeChipDescriptor/rememberEmojiMarkerDescriptor. It is
synchronous and idempotent, so it is a no-op once the SDK is already up
(e.g. on the full map screen, where a GoogleMap is already present).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the bugfix PR tag label Jun 1, 2026
@jamesarich jamesarich added this pull request to the merge queue Jun 1, 2026
Merged via the queue into main with commit 5ec4fa7 Jun 1, 2026
18 checks passed
@jamesarich jamesarich deleted the claude/inline-map-bitmapdescriptor-init branch June 1, 2026 23:46
jamesarich added a commit that referenced this pull request Jun 2, 2026
…p, drop Google Maps leftovers

Rebased onto main and dusted off the MapLibre Compose Multiplatform branch.

- Bump maplibre-compose 0.12.1 -> 0.13.0 and migrate the redesigned location API
  (BearingUpdate.TRACK_LOCATION -> TRACK_AUTOMATIC; LocationPuck locationState -> location).
- desktopApp: add a host-detecting maplibre-native-bindings-jni runtime backend so the
  desktop base map renders (Metal on macOS arm64, OpenGL on Linux/Windows amd64). Without it
  Gradle links no native renderer and the map canvas is black.
- Gate Compose map overlays off on the JVM target via a mapOverlaysSupported expect/actual flag
  (true Android/iOS, false desktop). maplibre-compose 0.13.0 stubs the desktop layers/sources
  API with TODO(), so composing markers/waypoints/tracks/traceroute threw NotImplementedError
  and tore down the window. Desktop now renders base-map-only; overlays auto-enable when upstream
  implements desktop layers.
- Remove orphaned Google Maps leftovers the rebase carried over from main's #5702/#5709 marker
  work (MarkerBitmapRenderer.kt + play-services-maps); this branch replaces Google Maps entirely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich added a commit that referenced this pull request Jun 3, 2026
…ine-map crash structurally

The late-May/early-June crash-fix flurry introduced app-side MapsInitializer
calls from Compose to work around a "BitmapDescriptorFactory is not initialized"
crash on the node-detail inline map (#5709), then extended it to the main map
(#5715). maps-compose initializes the SDK itself when a GoogleMap creates its
MapView, and the idiom is to build BitmapDescriptors inside a live map.
Initializing from Compose (ahead of / racing the MapView's own init) correlates
with the LATEST-renderer vector base map rendering black on affected devices
("Normal" black while satellite/terrain/hybrid render fine). Open-tag bisection
of a field report (fine at open.16/17, black at open.20) places the inline-map
regression at open.19 and the main-map regression at open.20.

Revert to the pre-flurry, library-idiomatic initialization:
- delete MapsSdkInitializer; remove all app-side MapsInitializer calls (the
  MapView init LaunchedEffect and MarkerBitmapRenderer's ensureMapsInitialized;
  buildNodeChipDescriptor drops its now-unused Context param)
- fix the inline-map crash structurally: build the marker descriptor inside
  InlineMap's GoogleMap content, where the SDK is already initialized, exactly
  like the main map (which never crashed)
- drop the redundant play-services-maps version pin; maps-compose pulls
  20.0.0 transitively via maps-ktx

Every descriptor build site now sits inside a live map, so no app-side init is
needed and the crash cannot recur.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix PR tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant