From 8ec5639cc2c5d1e4fb7fdf21749d815d6ed1ec55 Mon Sep 17 00:00:00 2001 From: ziyad-aljohani <135967503+ziyad-aljohani@users.noreply.github.com> Date: Sun, 19 Jul 2026 06:22:38 +0300 Subject: [PATCH] Avoid redundant ground overlay image updates --- .../google_maps_flutter_android/CHANGELOG.md | 4 +++ .../flutter/plugins/googlemaps/Convert.java | 8 +++-- .../googlemaps/GroundOverlayController.java | 15 +++++++++- .../googlemaps/GroundOverlaysController.java | 16 ++++++++-- .../GroundOverlaysControllerTest.java | 29 ++++++++++++++++++- .../google_maps_flutter_android/pubspec.yaml | 2 +- 6 files changed, 66 insertions(+), 8 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md index c62c53095ef3..bfe52050cabe 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.19.13 + +* Avoids recreating ground overlay images when only other overlay properties change. + ## 2.19.12 * Bumps the androidx group across 10 directories with 1 update. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java index a127cbcc9a1e..f9f21659e446 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java @@ -821,6 +821,7 @@ static Tile tileFromPigeon(PlatformTile tile) { * asset files stored in the application's assets directory. * @param density the density of the display, used to calculate pixel dimensions. * @param wrapper the BitmapDescriptorFactoryWrapper to create BitmapDescriptor. + * @param updateImage whether to convert and set the ground overlay image. * @return the identifier of the ground overlay. The identifier is valid as long as the ground * overlay exists. * @throws IllegalArgumentException if required fields are missing or invalid. @@ -830,7 +831,8 @@ static Tile tileFromPigeon(PlatformTile tile) { @NonNull GroundOverlaySink sink, @NonNull AssetManager assetManager, float density, - @NonNull BitmapDescriptorFactoryWrapper wrapper) { + @NonNull BitmapDescriptorFactoryWrapper wrapper, + boolean updateImage) { sink.setTransparency((float) groundOverlay.getTransparency()); sink.setZIndex((float) groundOverlay.getZIndex()); sink.setVisible(groundOverlay.getVisible()); @@ -840,7 +842,9 @@ static Tile tileFromPigeon(PlatformTile tile) { } sink.setBearing((float) groundOverlay.getBearing()); sink.setClickable(groundOverlay.getClickable()); - sink.setImage(toBitmapDescriptor(groundOverlay.getImage(), assetManager, density, wrapper)); + if (updateImage) { + sink.setImage(toBitmapDescriptor(groundOverlay.getImage(), assetManager, density, wrapper)); + } if (groundOverlay.getPosition() != null) { if (groundOverlay.getWidth() == null) { throw new FlutterError( diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GroundOverlayController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GroundOverlayController.java index de711c7cdc4b..4f0e87e1ec2c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GroundOverlayController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GroundOverlayController.java @@ -15,11 +15,16 @@ class GroundOverlayController implements GroundOverlaySink { private final GroundOverlay groundOverlay; private final String googleMapsGroundOverlayId; private final boolean isCreatedWithBounds; + private @NonNull PlatformBitmap platformBitmap; - GroundOverlayController(@NonNull GroundOverlay groundOverlay, boolean isCreatedWithBounds) { + GroundOverlayController( + @NonNull GroundOverlay groundOverlay, + boolean isCreatedWithBounds, + @NonNull PlatformBitmap platformBitmap) { this.groundOverlay = groundOverlay; this.googleMapsGroundOverlayId = groundOverlay.getId(); this.isCreatedWithBounds = isCreatedWithBounds; + this.platformBitmap = platformBitmap; } void remove() { @@ -85,4 +90,12 @@ String getGoogleMapsGroundOverlayId() { public boolean isCreatedWithBounds() { return isCreatedWithBounds; } + + boolean hasImage(@NonNull PlatformBitmap platformBitmap) { + return this.platformBitmap.equals(platformBitmap); + } + + void setPlatformBitmap(@NonNull PlatformBitmap platformBitmap) { + this.platformBitmap = platformBitmap; + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GroundOverlaysController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GroundOverlaysController.java index cbabe2f526f1..575d14dc14bd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GroundOverlaysController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GroundOverlaysController.java @@ -85,12 +85,16 @@ private void addGroundOverlay(@NonNull PlatformGroundOverlay platformGroundOverl groundOverlayOptionsBuilder, assetManager, density, - bitmapDescriptorFactoryWrapper); + bitmapDescriptorFactoryWrapper, + true); GroundOverlayOptions options = groundOverlayOptionsBuilder.build(); final GroundOverlay groundOverlay = googleMap.addGroundOverlay(options); if (groundOverlay != null) { GroundOverlayController groundOverlayController = - new GroundOverlayController(groundOverlay, platformGroundOverlay.getBounds() != null); + new GroundOverlayController( + groundOverlay, + platformGroundOverlay.getBounds() != null, + platformGroundOverlay.getImage()); groundOverlayIdToController.put(groundOverlayId, groundOverlayController); googleMapsGroundOverlayIdToDartGroundOverlayId.put(groundOverlay.getId(), groundOverlayId); } @@ -101,12 +105,18 @@ private void changeGroundOverlay(@NonNull PlatformGroundOverlay platformGroundOv GroundOverlayController groundOverlayController = groundOverlayIdToController.get(groundOverlayId); if (groundOverlayController != null) { + final PlatformBitmap image = platformGroundOverlay.getImage(); + final boolean imageChanged = !groundOverlayController.hasImage(image); Convert.interpretGroundOverlayOptions( platformGroundOverlay, groundOverlayController, assetManager, density, - bitmapDescriptorFactoryWrapper); + bitmapDescriptorFactoryWrapper, + imageChanged); + if (imageChanged) { + groundOverlayController.setPlatformBitmap(image); + } } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GroundOverlaysControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GroundOverlaysControllerTest.java index 76d7b36a1859..c1c068073c8d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GroundOverlaysControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GroundOverlaysControllerTest.java @@ -8,6 +8,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.content.Context; @@ -46,6 +48,12 @@ public class GroundOverlaysControllerTest { @NonNull private PlatformGroundOverlay createGroundOverlay(String overlayId, double transparency) { + return createGroundOverlay(overlayId, transparency, 100.0); + } + + @NonNull + private PlatformGroundOverlay createGroundOverlay( + String overlayId, double transparency, double imageWidth) { byte[] bmpData = Base64.decode(base64Image, Base64.DEFAULT); return new PlatformGroundOverlay( @@ -55,7 +63,7 @@ private PlatformGroundOverlay createGroundOverlay(String overlayId, double trans bmpData, PlatformMapBitmapScaling.AUTO, /* imagePixelRatio */ 2.0, - /* width */ 100.0, /* height */ + /* width */ imageWidth, /* height */ null)), /* position */ null, /* bounds */ null, @@ -107,9 +115,28 @@ public void controller_AddChangeAndRemoveGroundOverlay() { controller.changeGroundOverlays( Collections.singletonList(createGroundOverlay(googleGroundOverlayId, newTransparency))); Mockito.verify(groundOverlay, times(1)).setTransparency(newTransparency); + verify(bitmapDescriptorFactoryWrapper, times(1)).fromBitmap(any()); + verifyNoMoreInteractions(bitmapDescriptorFactoryWrapper); controller.removeGroundOverlays(Collections.singletonList(googleGroundOverlayId)); Mockito.verify(groundOverlay, times(1)).remove(); } + + @Test + public void controller_ChangeGroundOverlayUpdatesChangedImage() { + final GroundOverlay groundOverlay = mock(GroundOverlay.class); + final String groundOverlayId = "ground-overlay"; + + when(groundOverlay.getId()).thenReturn("google-ground-overlay"); + when(googleMap.addGroundOverlay(any(GroundOverlayOptions.class))).thenReturn(groundOverlay); + + controller.addGroundOverlays( + Collections.singletonList(createGroundOverlay(groundOverlayId, 0.1, 100.0))); + controller.changeGroundOverlays( + Collections.singletonList(createGroundOverlay(groundOverlayId, 0.1, 101.0))); + + verify(bitmapDescriptorFactoryWrapper, times(2)).fromBitmap(any()); + verify(groundOverlay).setImage(mockBitmapDescriptor); + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml index 8d34180d23a9..79fccd19b6d9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_android description: Android implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.19.12 +version: 2.19.13 environment: sdk: ^3.12.0